You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of the agentworkforce analyze feature. Issue 3 of 3. Depends on #75 (gather), #76 (persona-discoverer), and #71 (persona-kit publish).
This issue assumes the persona-kit migration (#64–#71) has shipped. The spawn flow below uses the post-migration buildPersonaSpawnPlan / executePersonaSpawnPlan API, not the pre-migration runAgentSelector.
Goal
Wire up the user-facing agentworkforce analyze subcommand. This is the orchestration layer: invoke the gather module, launch the persona-discoverer persona, walk the proposals interactively, and write accepted personas to disk.
After this lands, a user in any repo can run agentworkforce analyze and end up with 3–7 starter personas in ./.agentworkforce/workforce/personas/, each grounded in a real cluster of work the repo has been doing.
Files to touch
New:
packages/cli/src/analyze-walk.ts — proposal parser + interactive accept-loop + disk writer.
packages/cli/src/analyze-walk.test.ts — Node test runner.
Modify:
packages/cli/src/cli.ts:
Add 'analyze' to the subcommand dispatcher.
Add parseAnalyzeArgs(rest) + runAnalyze(flags).
Update the USAGE const with the new line.
packages/cli/README.md — short analyze section: usage, flags, example.
Type imports:PersonaSpec (used by analyze-walk.ts) imports from @agentworkforce/persona-kit, not @agentworkforce/workload-router.
Pre-existing helpers that likely stay in cli.ts post-migration (interactive UX, not spawn logic — verify before assuming): parseProposals + applyAcceptedPatches from the auto-improve flow; readSingleCharChoice; promptYesNoSync; resolveCreateTarget / ensureCreateTargetDir / buildCreateInputValues. If any of these moved to persona-kit during the migration, adjust the import path — the behavior contract is unchanged.
Persona inputs are wired via envOverrides on the plan — there is no inputValues argument anymore. The harness stdio is inherit so the user sees the analyzer working (matches create UX).
Phase 3 — Walk + write. Read proposalsOutputPath, parse + validate, walk interactively.
parseAnalyzeProposals validates required fields (id kebab-case, summary<=80 chars, rationale non-empty, persona is a valid PersonaSpec). Reuse the validators in parseProposals at cli.ts:3164–3225 — share helpers; do not duplicate. Throws on schema violations with a clear pointer to the offending proposal.
walkAndWrite: for each proposal, print summary + rationale + a compact preview of the persona (id, intent, tags, description, model per tier), then prompt accept? [y/N/a/q] via readSingleCharChoice (cli.ts:3309).
y — accept this one.
N (default on empty) — skip.
a — accept this and all remaining.
q — quit; everything not yet decided is rejected.
Write accepted proposals to ${targetDir}/<id>.json (2-space indent + trailing newline — match existing applyAcceptedPatches write format).
On id collision: if --overwrite, replace; else skip with a warning. Track in skipped.
Tasks
Implement parseAnalyzeProposals with full schema validation reusing the helpers from cli.ts:3164–3225.
Implement walkAndWrite with the four-choice prompt loop.
Implement parseAnalyzeArgs(rest) + runAnalyze(flags) in cli.ts. Match the parser style used by parseCreateArgs / parseAgentArgs.
Add the analyze branch to the dispatcher.
Update USAGE const with analyze [flags] line.
Update packages/cli/README.md — short section, mirror the create section's tone.
walkAndWrite: stub IO that returns y, n, a, q in various sequences → assert exactly the expected files are written under a temp TARGET_DIR. Assert a short-circuits the remaining prompts.
Id collision: pre-populate TARGET_DIR/foo.json → walk a proposal with id: 'foo' → without overwrite, file unchanged + skipped: ['foo']; with overwrite: true, file replaced + written: ['foo'].
parseAnalyzeArgs: every flag in the table above produces the expected AnalyzeFlags shape; unknown flags error with a usage hint.
Verification
corepack pnpm --filter @agentworkforce/cli test passes.
corepack pnpm -r build clean.
Dry-run end-to-end (no LLM, no writes):npm run dev:cli -- analyze --dry-run --lookback-days 30 in this repo. Expected: gather summary line, exit 0, no persona launched, no files written.
Live run against this repo:npm run dev:cli -- analyze --tier minimum --lookback-days 90. Expected: spinner through gather → analyzer launches and runs → proposals walk → accepted personas land in ./.agentworkforce/workforce/personas/.
For each accepted persona: agentworkforce list shows it and agentworkforce agent <id>@best-value --dry-run passes cleanly.
Negative paths: with gh uninstalled, with AGENTWORKFORCE_LAUNCH_METADATA=0 and no prior burn data, and re-running analyze after a previous accept (id collision without --overwrite) — all behave per spec.
Constraints
No new runtime dependencies. Reuse ora, readSingleCharChoice, promptYesNoSync, resolveCreateTarget, etc.
Clean up the temp dir on every exit path, including SIGINT during the walk.
Don't crash on partial signal. Empty prs, empty sessions, empty commit history (brand-new repo) all need to produce a useful error message or proceed gracefully — not a stack trace.
Walk UX matches auto-improve. Same four-choice grammar, same prompt format, same spinner style — users should feel one is a sibling of the other.
Part of the
agentworkforce analyzefeature. Issue 3 of 3. Depends on #75 (gather), #76 (persona-discoverer), and #71 (persona-kit publish).This issue assumes the persona-kit migration (#64–#71) has shipped. The spawn flow below uses the post-migration
buildPersonaSpawnPlan/executePersonaSpawnPlanAPI, not the pre-migrationrunAgentSelector.Goal
Wire up the user-facing
agentworkforce analyzesubcommand. This is the orchestration layer: invoke the gather module, launch thepersona-discovererpersona, walk the proposals interactively, and write accepted personas to disk.After this lands, a user in any repo can run
agentworkforce analyzeand end up with 3–7 starter personas in./.agentworkforce/workforce/personas/, each grounded in a real cluster of work the repo has been doing.Files to touch
New:
packages/cli/src/analyze-walk.ts— proposal parser + interactive accept-loop + disk writer.packages/cli/src/analyze-walk.test.ts— Node test runner.Modify:
packages/cli/src/cli.ts:'analyze'to the subcommand dispatcher.parseAnalyzeArgs(rest)+runAnalyze(flags).USAGEconst with the new line.packages/cli/README.md— shortanalyzesection: usage, flags, example.Type imports:
PersonaSpec(used byanalyze-walk.ts) imports from@agentworkforce/persona-kit, not@agentworkforce/workload-router.Pre-existing helpers that likely stay in cli.ts post-migration (interactive UX, not spawn logic — verify before assuming):
parseProposals+applyAcceptedPatchesfrom the auto-improve flow;readSingleCharChoice;promptYesNoSync;resolveCreateTarget/ensureCreateTargetDir/buildCreateInputValues. If any of these moved to persona-kit during the migration, adjust the import path — the behavior contract is unchanged.Flags
--lookback-days <n>--max-commits <n>--no-prsghcall entirely--no-sessions--save-in-directory=<t>cwd(./.agentworkforce/workforce/personas)create; supportscwd|user|library|dir:n|<path>--overwritepersonas/<id>.jsonon disk--tier <best|best-value|minimum>best-value--dry-run--no-launch-metadataagent/createFlow
runAnalyze(flags)does:resolveCreateTarget/ensureCreateTargetDir. Same--save-in-directorysemantics ascreate.os.tmpdir(); allocateanalysisInputPath+proposalsOutputPath.oraspinner. Callgather()from [analyze 1/3] analyze-gather: collect git/PR/codebase/session signal into JSON #75 with the resolved bounds, write the result toanalysisInputPath. Print a one-line summary:Gathered N commits, M PRs, K sessions, P packages.--dry-run: print summary, exit 0. Do not launch the persona, do not walk.persona-discoverer@<tier>via persona-kit's spawn API:envOverrideson the plan — there is noinputValuesargument anymore. The harness stdio isinheritso the user sees the analyzer working (matchescreateUX).proposalsOutputPath, parse + validate, walk interactively.analyze-walk.tsAPIBehavior:
parseAnalyzeProposalsvalidates required fields (idkebab-case,summary<=80chars,rationalenon-empty,personais a validPersonaSpec). Reuse the validators inparseProposalsatcli.ts:3164–3225— share helpers; do not duplicate. Throws on schema violations with a clear pointer to the offending proposal.walkAndWrite: for each proposal, print summary + rationale + a compact preview of the persona (id, intent, tags, description, model per tier), then promptaccept? [y/N/a/q]viareadSingleCharChoice(cli.ts:3309).y— accept this one.N(default on empty) — skip.a— accept this and all remaining.q— quit; everything not yet decided is rejected.${targetDir}/<id>.json(2-space indent + trailing newline — match existingapplyAcceptedPatcheswrite format).--overwrite, replace; else skip with a warning. Track inskipped.Tasks
parseAnalyzeProposalswith full schema validation reusing the helpers fromcli.ts:3164–3225.walkAndWritewith the four-choice prompt loop.parseAnalyzeArgs(rest)+runAnalyze(flags)incli.ts. Match the parser style used byparseCreateArgs/parseAgentArgs.analyzebranch to the dispatcher.USAGEconst withanalyze [flags]line.packages/cli/README.md— short section, mirror thecreatesection's tone.Tests
parseAnalyzeProposals: canned valid JSON → parsed object. Canned invalid JSON (bad id, missing tier, etc.) → throws with line/proposal pointer.walkAndWrite: stub IO that returnsy,n,a,qin various sequences → assert exactly the expected files are written under a tempTARGET_DIR. Assertashort-circuits the remaining prompts.TARGET_DIR/foo.json→ walk a proposal withid: 'foo'→ withoutoverwrite, file unchanged +skipped: ['foo']; withoverwrite: true, file replaced +written: ['foo'].parseAnalyzeArgs: every flag in the table above produces the expectedAnalyzeFlagsshape; unknown flags error with a usage hint.Verification
corepack pnpm --filter @agentworkforce/cli testpasses.corepack pnpm -r buildclean.npm run dev:cli -- analyze --dry-run --lookback-days 30in this repo. Expected: gather summary line, exit 0, no persona launched, no files written.npm run dev:cli -- analyze --tier minimum --lookback-days 90. Expected: spinner through gather → analyzer launches and runs → proposals walk → accepted personas land in./.agentworkforce/workforce/personas/.agentworkforce listshows it andagentworkforce agent <id>@best-value --dry-runpasses cleanly.ghuninstalled, withAGENTWORKFORCE_LAUNCH_METADATA=0and no prior burn data, and re-runninganalyzeafter a previous accept (id collision without--overwrite) — all behave per spec.Constraints
ora,readSingleCharChoice,promptYesNoSync,resolveCreateTarget, etc.prs, emptysessions, empty commit history (brand-new repo) all need to produce a useful error message or proceed gracefully — not a stack trace.