feat: vibesift propose — scan, kickoff, auto-open, live page#37
feat: vibesift propose — scan, kickoff, auto-open, live page#37polrai-bg wants to merge 5 commits into
Conversation
Pure repo-scan module classifies project type (node/python/rust/go/static) and produces project-appropriate constraint stubs and starter task lists for vibesift propose to seed sessions with. Cross-platform browser-open helper lands the user on the rendered HTML right after creation. Both modules are zero-dep and side-effect-free (open spawns once and unrefs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Low-friction kickoff verb. propose [<slug>] scans the repo, auto-bootstraps docs/sessions/ if missing, seeds project-type constraints and a starter ship task list, writes one commit, and opens the rendered page in the default browser. Prompts only when stdin is a TTY so CI and agent flows that pass --title and --problem never block. main() goes async so future verbs that need stdin can also use readline without restructuring. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
One-line callout above the pipeline graphic naming the single next action the user (or agent) should take, with a phase-tinted left border. Eight distinct states across scope/sift/ship/deployed, including a monospace inline preview of the next ship task. data-vibesift-next attribute lets future JS hook in without touching the embedded state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds vibesift propose to the canonical commands block and the trigger list so harnesses know to invoke it for "kickoff a session" phrasing. Auto-ticking discipline section tells agents to mark ship tasks done inline as they complete work, not batched at the end, since the HTML page is a live broadcast. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The HTML page polls itself every 3s, parses the embedded JSON state out of the response, and reloads when updatedAt changes. Toggleable via a LIVE / PAUSED pill in the toolbar that persists in localStorage. Stays inside the zero-runtime-deps promise: no SSE, no WebSocket. Silent on file:// where fetch is blocked. Closes the "watch the agent work" gap for vibe-coding flows: a Claude Code agent running ship task done shows up on the page within seconds, no manual refresh. Also wires tests/scan.test.js and tests/cli-propose.test.js into the npm test script (they were skipped on the first pass). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
neurot1cal
left a comment
There was a problem hiding this comment.
Thanks for this — propose, the hero, and the auto-ticking discipline are clear wins. A few inline notes below. Most are small; the README one is the only one I want resolved before this lands so the project's scope/docs stay in sync.
| // deps promise: no SSE, no WebSocket, no framework. The close tag for the | ||
| // embedded state block is built by string concatenation so the literal | ||
| // `</script>` never appears inside this script body. | ||
| const LIVE_POLL_SCRIPT = ` |
There was a problem hiding this comment.
This adds live auto-refresh, but the README still lists Real-time updates — Pages serves whatever's committed. Reload to see new state. as out-of-scope for v1 (line 251). Two options to resolve:
- Update
README.mdline 251 to admit the scope, e.g. Real-time updates land via polling (no SSE, no WebSocket); the page reloads itself whenupdatedAtchanges. - Pull the live-refresh out and land it in a follow-up PR with the README amendment.
I'm fine with (1), but the doc needs to match the code before merge.
| var nextState; | ||
| try { nextState = JSON.parse(raw); } catch(err) { return; } | ||
| if (nextState.updatedAt && nextState.updatedAt !== lastSeen) { | ||
| window.location.reload(); |
There was a problem hiding this comment.
Full window.location.reload() loses scroll position and any expanded <details> state. On a long session page being reviewed on mobile, the reader can lose their place every 3s while an agent is actively working.
Not a blocker for v1 of this feature, but worth a follow-up to do something more surgical — swap the embedded JSON state and re-render just the affected section. Could also be mitigated short-term by preserving document.scrollingElement.scrollTop across the reload via sessionStorage.
| }) | ||
| .catch(function(){}); | ||
| } | ||
| setInterval(tick, INTERVAL); |
There was a problem hiding this comment.
Polling continues on hidden / backgrounded tabs. On a phone with a vibesift tab in the background, this keeps hitting the network every 3s. Cheap fix:
function tick(){
if (!enabled) return;
if (typeof document !== 'undefined' && document.visibilityState === 'hidden') return;
// ...rest as-is
}Or gate the setInterval behind a visibilitychange listener that pauses/resumes. Either way, optional but worth doing.
| Same shape applies to sift and ship. | ||
|
|
||
| For a faster start that pre-seeds the session with project-type-derived | ||
| constraints and a starter task list, use `vibesift propose <slug>` instead |
There was a problem hiding this comment.
The canonical commands block above mentions [--no-open] but this paragraph (the one agents actually read for flow guidance) doesn't tell them to use it. In a CI or headless-agent flow, default-opening a browser is at best a no-op and at worst a stray process. Suggest a line like:
In CI or any non-interactive context, pass
--no-opento suppress the browser. The CLI also auto-suppresses when stdin isn't a TTY for the prompt path, but the open-in-browser call is unconditional unless the flag is set.
| // Expected commits: initial empty + propose. Index regen may add 1 more | ||
| // if the landing index actually changed; with a single new session it | ||
| // does, so total is 3. | ||
| assert.ok(lines.length >= 2, `expected >=2 commits, got ${lines.length}: ${log.stdout}`); |
There was a problem hiding this comment.
Coverage notes on this PR overall, anchored here since this is the loosest of the new tests:
- This test is too loose.
lines.length >= 2plus thevibesift: proposeline check would still pass if some future regression caused every constraint and task to fire its own commit. Tightening toassert.equal(lines.length, 2)or<= 3(allowing the optional index-regen follow-up) would catch that. - No test for
openInBrowser. Fair — cross-platform browser spawn is hard to test cleanly. But a unit test that mocksspawnand asserts the rightcmd+argsper platform would be cheap. Not blocking. - No test enumerates all 8
nextStepstates explicitly. Three template tests cover live-pill / live-poll / no-literal-script-tag, but the hero's 8 phase states are only spot-checked through the integration paths. A pure unit test that callsnextStep(state)with each shape (no constraints, constraints + no decision, etc.) would lock the labels in. - No
ensureBootstrappedidempotence test. The function is intentionally idempotent viawx+ EEXIST catch, but no test asserts that calling propose twice in a row doesn't clobberdocs/index.html.
| for (const c of constraintStubsFor(scan)) addConstraint(state, c); | ||
| for (const t of taskScaffoldFor(scan)) addTask(state, t); | ||
|
|
||
| writeFileSync(path, renderHTML(state)); |
| args = [filepath]; | ||
| } | ||
| try { | ||
| const child = spawn(cmd, args, { stdio: 'ignore', detached: true }); |
Summary
vibesift propose [<slug>]verb: low-friction kickoff. Scans the repo, classifies the project (node/python/rust/go/static), seeds project-appropriate constraint stubs + a starter ship task list, auto-commits a singlevibesift: propose <slug>commit, and opens the rendered HTML in the default browser. Prompts only when stdin is a TTY; CI/agent flows pass everything via--titleand--problem.data-vibesift-nextattribute for future JS hooks.updatedAtchanges. Toggleable via a LIVE / PAUSED pill in the toolbar that persists inlocalStorage. Stays inside the zero-runtime-deps promise: no SSE, no WebSocket. Closes the "watch the agent work" gap for vibe-coding flows: a Claude Code agent runningvibesift ship task doneshows up on the page within seconds, no manual refresh.Plan-file parsing in
proposewas deliberately deferred. Thefeat/import-planbranch (separate work) carries the PRD/build-plan parser; this PR detects plan files and surfaces their paths in the propose summary, but does not parse or seed from them. Two PRs can stack cleanly later.Files
src/scan.js,src/open.js,tests/scan.test.js,tests/cli-propose.test.jssrc/cli.js,src/template.js,tests/template.test.js,skills/vibesift/SKILL.md,package.jsonTest plan
npm test(state, template, cli-no-commit, cli-index, cli-propose, scan, svg-pipeline, svg-tree)./tmp/vibesift-fulltest.vibesift: propose <slug>commit + optionalvibesift: index regeneratedfollow-up (test).</script>tag in its body (test asserts the OPEN_TAG / CLOSE_TAG split).ship task donefrom another terminal, watching the page reload within 3s.Smoke test
🤖 Generated with Claude Code