mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-21 10:36:27 +03:00
Port /impeccable generate to the engine crates
The Node-era server, CLI, hook, and pin halves of the generate command move into the Rust workspace, with the protocol unchanged: - crates/live: POST /agent-target is held open on a channel plus a timer thread (the manual-apply deferred pattern), releasing its turnstile ticket before it parks like /poll; /agent-target-result resolves it; /agent-target-claim is the roll call with its renewable lease. SSE connections carry the overlay's clientId: a late overlay is replayed every pending target, and a disconnect retires that overlay's report, releases its lease, and re-judges each roll call. Shutdown drains held requests with server_stopping. - crates/live/src/live_generate.rs: the live-generate verb (the router already forwards every live* verb), same flags, verdicts, and _instructions, spelled with the engine's self command. - crates/hook: every entry stands down on live preview markers (skipped: live-preview), checking the proposed content and the file on disk for hook-before-edit. - crates/context: pin accepts generate; the crate's command-metadata.json copy carries its entry. Tests: crates/cli/tests/agent_target.rs (six HTTP cases with an SSE reader), tests/live-agent-target.test.mjs rewritten to drive the binary (28 cases, registered in the live suite), hook stand-down cases, oracle goldens for live-generate plus the re-recorded pin list goldens, the e2e prompt assertion waiting for the journaled event, and the contract documented in docs/CLI-CONTRACT.md. AI-assisted: implemented and tested with Claude Code under maintainer direction. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Abdul Wahab
co-authored by
Claude Fable 5
parent
fc89b0ed62
commit
d397140a77
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* `live-generate` (the `generate` command's agent-initiated targeting): the
|
||||
* verdicts the verb decides locally, without a browser, plus the one the
|
||||
* helper answers when no overlay is attached. Everything that needs an
|
||||
* overlay (the roll call, leases, replay) is covered by
|
||||
* tests/live-agent-target.test.mjs and crates/cli/tests/agent_target.rs.
|
||||
*/
|
||||
import { LIVE_FILES } from '../live-helpers.mjs';
|
||||
|
||||
const NORM = [
|
||||
['localhost:\\d{4,5}', 'g', 'localhost:<PORT>'],
|
||||
['"(port|serverPort)":(\\s*)\\d{4,5}', 'g', '"$1":$2<PORT>'],
|
||||
['Stopped live server on port \\d+\\.', 'g', 'Stopped live server on port <PORT>.'],
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
id: 'live-generate-local-verdicts', workspace: 'live-html', files: [...LIVE_FILES],
|
||||
// No helper is recorded in the staged workspace, so every step short
|
||||
// of a valid request ends in the verb's own verdict, and the valid one
|
||||
// ends in server_not_running.
|
||||
steps: [
|
||||
{ verb: 'live-generate', args: ['--help'] },
|
||||
{ verb: 'live-generate', args: [] },
|
||||
{ verb: 'live-generate', args: ['--selector'] },
|
||||
{ verb: 'live-generate', args: ['--selector', 'h1', '--action', 'bold'] },
|
||||
{ verb: 'live-generate', args: ['--selector', 'h1', '--count', '9'] },
|
||||
{ verb: 'live-generate', args: ['--selector', 'h1', '--count', 'three'] },
|
||||
{ verb: 'live-generate', args: ['--selector', 'h1', '--index', '0'] },
|
||||
{ verb: 'live-generate', args: ['--selector', 'h1', '--wait-for-browser', 'soon'] },
|
||||
{ verb: 'live-generate', args: ['--selector', 'h1', '--action', 'bolder'] },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'live-generate-no-browser-connected', workspace: 'live-html', files: [...LIVE_FILES], normalize: NORM,
|
||||
// A running helper with no overlay attached answers at once instead of
|
||||
// holding the request.
|
||||
steps: [
|
||||
{ verb: 'live-server', daemon: true, readyFile: '.impeccable/live/server.json', readyTimeoutMs: 15000 },
|
||||
{ verb: 'live-generate', args: ['--selector', 'h1', '--action', 'bolder', '--count', '2', '--prompt', 'warmer'] },
|
||||
{ verb: 'live-server', args: ['stop'] },
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"stdout": "Usage: impeccable live-generate --selector <css> [--text <snippet>] [--index <n>] [--action <name>] [--count <n>] [--prompt <text>] [--dry-run] [--wait-for-browser <ms>]\n\nFlags:\n --selector <css> required; resolved with document.querySelectorAll\n --text <snippet> optional; keeps only matches whose textContent contains it\n --index <n> optional; 1-based pick among the remaining matches\n --action <name> optional; one of the live action vocabulary (default: impeccable)\n --count <n> optional; variants to request, 1-8 (default: 3)\n --prompt <text> optional; freeform direction, same as typing before Go\n --dry-run optional; resolve and report without starting anything\n --wait-for-browser <ms> optional; poll the helper until a page with the\n overlay connects (or the budget runs out) before sending\n the target.\n\n",
|
||||
"stderr": "",
|
||||
"exit": 0,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"stdout": "{\n \"ok\": false,\n \"error\": \"selector_required\",\n \"_instructions\": \"Pass --selector with a CSS selector for the element to target. Derive it from the page source: prefer an id, a unique class, or a landmark section, and add --text \\\"<visible text>\\\" when the class repeats.\"\n}\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"stdout": "{\n \"ok\": false,\n \"error\": \"missing_flag_value\",\n \"flag\": \"--selector\"\n}\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"stdout": "{\n \"ok\": false,\n \"error\": \"invalid_action\",\n \"action\": \"bold\",\n \"validActions\": [\n \"impeccable\",\n \"bolder\",\n \"quieter\",\n \"distill\",\n \"polish\",\n \"typeset\",\n \"colorize\",\n \"layout\",\n \"adapt\",\n \"animate\",\n \"delight\",\n \"overdrive\"\n ],\n \"_instructions\": \"Map the request wording onto the closest listed action (bold -> bolder, quiet/calmer -> quieter, simplify -> distill). When no action fits, use --action impeccable and carry the wording via --prompt.\"\n}\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"stdout": "{\n \"ok\": false,\n \"error\": \"invalid_count\",\n \"count\": \"9\",\n \"_instructions\": \"Pass --count as an integer from 1 to 8.\"\n}\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"stdout": "{\n \"ok\": false,\n \"error\": \"invalid_count\",\n \"count\": \"three\",\n \"_instructions\": \"Pass --count as an integer from 1 to 8.\"\n}\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"stdout": "{\n \"ok\": false,\n \"error\": \"invalid_index\",\n \"index\": \"0\",\n \"_instructions\": \"Pass --index as a 1-based integer position among the matches.\"\n}\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"stdout": "{\n \"ok\": false,\n \"error\": \"invalid_wait\",\n \"wait\": \"soon\",\n \"_instructions\": \"Pass --wait-for-browser as a positive integer of milliseconds, e.g. --wait-for-browser 120000.\"\n}\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"stdout": "{\n \"ok\": false,\n \"error\": \"server_not_running\",\n \"_instructions\": \"No live helper server is recorded for this project. Run the live boot first (<IMPECCABLE> live), open the app URL that serves a pageFiles entry, then rerun this command.\"\n}\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null
|
||||
}
|
||||
],
|
||||
"files": {
|
||||
".impeccable/live/config.json": "{\n \"files\": [\"index.html\", \"public/**/*.html\"],\n \"insertBefore\": \"</body>\",\n \"commentSyntax\": \"html\"\n}\n"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"stdout": "",
|
||||
"stderr": "",
|
||||
"exit": null,
|
||||
"signal": null,
|
||||
"daemon": true
|
||||
},
|
||||
{
|
||||
"stdout": "{\n \"ok\": false,\n \"error\": \"no_browser_connected\",\n \"_instructions\": \"No page with the live overlay is connected. Open the app URL that serves a pageFiles entry yourself with your harness browser tool, then rerun this command. Only when no browser tool exists: give the user the URL and rerun with --wait-for-browser 120000 so the command fires as soon as they open the page.\"\n}\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null
|
||||
},
|
||||
{
|
||||
"stdout": "Stopped live server on port <PORT>.\n",
|
||||
"stderr": "",
|
||||
"exit": 0,
|
||||
"signal": null
|
||||
}
|
||||
],
|
||||
"files": {
|
||||
".impeccable/live/config.json": "{\n \"files\": [\"index.html\", \"public/**/*.html\"],\n \"insertBefore\": \"</body>\",\n \"commentSyntax\": \"html\"\n}\n"
|
||||
},
|
||||
"daemon": [
|
||||
{
|
||||
"stdout": "\nImpeccable live server running on http://localhost:<PORT>\nToken: <UUID>\n\nScript: http://localhost:<PORT>/live.js\nInject: managed by impeccable live-inject; Astro source tags use is:inline automatically.\nStop: impeccable live-server stop\n",
|
||||
"stderr": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"stdout": "",
|
||||
"stderr": "Unknown command: teach\nAvailable commands: craft, init, extract, document, shape, critique, audit, polish, bolder, quieter, distill, harden, onboard, live, animate, colorize, typeset, layout, delight, overdrive, clarify, adapt, optimize\n",
|
||||
"stderr": "Unknown command: teach\nAvailable commands: craft, init, extract, document, shape, critique, audit, polish, bolder, quieter, distill, harden, onboard, live, animate, colorize, typeset, layout, delight, overdrive, clarify, adapt, optimize, generate\n",
|
||||
"exit": 1,
|
||||
"signal": null,
|
||||
"files": {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"stdout": "",
|
||||
"stderr": "Unknown command: doctor\nAvailable commands: craft, init, extract, document, shape, critique, audit, polish, bolder, quieter, distill, harden, onboard, live, animate, colorize, typeset, layout, delight, overdrive, clarify, adapt, optimize\n",
|
||||
"stderr": "Unknown command: doctor\nAvailable commands: craft, init, extract, document, shape, critique, audit, polish, bolder, quieter, distill, harden, onboard, live, animate, colorize, typeset, layout, delight, overdrive, clarify, adapt, optimize, generate\n",
|
||||
"exit": 1,
|
||||
"signal": null,
|
||||
"files": {}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"stdout": "Usage: impeccable pin <pin|unpin> <command>\n\nAvailable commands: craft, init, extract, document, shape, critique, audit, polish, bolder, quieter, distill, harden, onboard, live, animate, colorize, typeset, layout, delight, overdrive, clarify, adapt, optimize\n",
|
||||
"stdout": "Usage: impeccable pin <pin|unpin> <command>\n\nAvailable commands: craft, init, extract, document, shape, critique, audit, polish, bolder, quieter, distill, harden, onboard, live, animate, colorize, typeset, layout, delight, overdrive, clarify, adapt, optimize, generate\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"stdout": "Usage: impeccable pin <pin|unpin> <command>\n\nAvailable commands: craft, init, extract, document, shape, critique, audit, polish, bolder, quieter, distill, harden, onboard, live, animate, colorize, typeset, layout, delight, overdrive, clarify, adapt, optimize\n",
|
||||
"stdout": "Usage: impeccable pin <pin|unpin> <command>\n\nAvailable commands: craft, init, extract, document, shape, critique, audit, polish, bolder, quieter, distill, harden, onboard, live, animate, colorize, typeset, layout, delight, overdrive, clarify, adapt, optimize, generate\n",
|
||||
"stderr": "",
|
||||
"exit": 1,
|
||||
"signal": null,
|
||||
|
||||
Reference in New Issue
Block a user