vibepro 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +9 -0
  3. package/README.ja.md +448 -0
  4. package/README.md +520 -0
  5. package/agent-instructions/codex/AGENTS.vibepro.md +45 -0
  6. package/bin/vibepro.js +9 -0
  7. package/docs/assets/vibepro-header.png +0 -0
  8. package/package.json +51 -0
  9. package/skills/vibepro-diagnosis-packages/SKILL.md +133 -0
  10. package/skills/vibepro-human-review/SKILL.md +73 -0
  11. package/skills/vibepro-story-refactor/SKILL.md +89 -0
  12. package/skills/vibepro-workflow/SKILL.md +139 -0
  13. package/src/agent-harness-map.js +230 -0
  14. package/src/agent-harness-scanner.js +337 -0
  15. package/src/agent-review.js +2180 -0
  16. package/src/api-boundary-scanner.js +452 -0
  17. package/src/architecture-profiler.js +423 -0
  18. package/src/authorization-scoring.js +149 -0
  19. package/src/brainbase-importer.js +534 -0
  20. package/src/change-risk-classifier.js +195 -0
  21. package/src/check-packs.js +605 -0
  22. package/src/checkpoint-manager.js +233 -0
  23. package/src/cli.js +2213 -0
  24. package/src/code-quality-scanner.js +310 -0
  25. package/src/codex-manager.js +143 -0
  26. package/src/component-style-scanner.js +336 -0
  27. package/src/coverage-report.js +99 -0
  28. package/src/database-access-scanner.js +163 -0
  29. package/src/decision-records.js +315 -0
  30. package/src/design-modernize.js +1435 -0
  31. package/src/design-system.js +1732 -0
  32. package/src/diagnostic-engine.js +1945 -0
  33. package/src/diagram-requirement-resolver.js +194 -0
  34. package/src/doctor.js +677 -0
  35. package/src/environment-graph.js +424 -0
  36. package/src/execution-state.js +849 -0
  37. package/src/explore-evidence.js +425 -0
  38. package/src/flow-design-scanner.js +896 -0
  39. package/src/flow-verifier.js +887 -0
  40. package/src/gesture-interaction-scanner.js +330 -0
  41. package/src/graph-context.js +263 -0
  42. package/src/graphify-adapter.js +189 -0
  43. package/src/html-report.js +1035 -0
  44. package/src/journey-map.js +1299 -0
  45. package/src/language.js +48 -0
  46. package/src/lazy-pattern-detector.js +182 -0
  47. package/src/local-dev-scanner.js +135 -0
  48. package/src/managed-worktree-gate.js +187 -0
  49. package/src/managed-worktree.js +766 -0
  50. package/src/merge-manager.js +501 -0
  51. package/src/network-contract-scanner.js +442 -0
  52. package/src/nocodb-story-sync.js +386 -0
  53. package/src/oss-readiness-scanner.js +417 -0
  54. package/src/performance-evidence.js +756 -0
  55. package/src/performance-measurer.js +591 -0
  56. package/src/pr-manager.js +8220 -0
  57. package/src/presets.js +682 -0
  58. package/src/public-discovery-scanner.js +519 -0
  59. package/src/refactoring-delta-reporter.js +367 -0
  60. package/src/refactoring-opportunity-generator.js +797 -0
  61. package/src/regression-risk-scanner.js +146 -0
  62. package/src/repo-status.js +266 -0
  63. package/src/report-fingerprint.js +188 -0
  64. package/src/report-pr-body-prompt-template.md +108 -0
  65. package/src/report-pr-body-schema.json +95 -0
  66. package/src/report-store.js +135 -0
  67. package/src/report-validator.js +192 -0
  68. package/src/requirement-consistency.js +1066 -0
  69. package/src/runtime-info.js +134 -0
  70. package/src/self-dogfood-scanner.js +476 -0
  71. package/src/session-learning.js +164 -0
  72. package/src/skills-manager.js +157 -0
  73. package/src/spec-drift.js +378 -0
  74. package/src/spec-fingerprint.js +445 -0
  75. package/src/spec-prompt-template.md +155 -0
  76. package/src/spec-schema.json +219 -0
  77. package/src/spec-store.js +258 -0
  78. package/src/spec-validator.js +459 -0
  79. package/src/static-site-scanner.js +316 -0
  80. package/src/story-candidate-generator.js +85 -0
  81. package/src/story-catalog-generator.js +2813 -0
  82. package/src/story-html.js +156 -0
  83. package/src/story-manager.js +2144 -0
  84. package/src/story-task-generator.js +522 -0
  85. package/src/task-manager.js +1029 -0
  86. package/src/terminal-link-scanner.js +238 -0
  87. package/src/usage-report.js +417 -0
  88. package/src/verification-evidence.js +284 -0
  89. package/src/workspace.js +126 -0
package/src/cli.js ADDED
@@ -0,0 +1,2213 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { promisify } from 'node:util';
6
+
7
+ import { getWorkspaceDir, initWorkspace } from './workspace.js';
8
+ import { installCodexInstructions, renderCodexInstall, renderCodexVerify, verifyCodexInstructions } from './codex-manager.js';
9
+ import { generateAgentHarnessMap, renderAgentHarnessMapSummary } from './agent-harness-map.js';
10
+ import { renderAgentHarnessStatus, scanAgentHarness } from './agent-harness-scanner.js';
11
+ import {
12
+ getExploreEvidenceStatus,
13
+ prepareExploreEvidence,
14
+ recordExploreEvidence,
15
+ renderExplorePrepareSummary,
16
+ renderExploreRecordSummary,
17
+ renderExploreStatusSummary
18
+ } from './explore-evidence.js';
19
+ import { importGraphifyArtifacts } from './graphify-adapter.js';
20
+ import { deriveEnvironmentGraph, renderEnvironmentGraphSummary } from './environment-graph.js';
21
+ import { runDiagnosis } from './diagnostic-engine.js';
22
+ import {
23
+ captureDesignModernizeScreens,
24
+ createDesignModernizePlan,
25
+ deriveProductDesignSystem,
26
+ renderCaptureSummary,
27
+ renderDerivedDesignSystemSummary,
28
+ renderDesignModernizePlan
29
+ } from './design-modernize.js';
30
+ import {
31
+ deriveNativeDesignSystem,
32
+ exportDesignSystem,
33
+ initDesignSystem,
34
+ ingestExternalDesignSystemBundle,
35
+ ingestVisualDesignBrief,
36
+ renderDesignSystemValidationSummary,
37
+ renderNativeDesignSystemSummary,
38
+ validateDesignSystem
39
+ } from './design-system.js';
40
+ import { assertOutputLanguage, localizedText, normalizeOutputLanguage, resolveHumanOutputLanguage, setOutputLanguage } from './language.js';
41
+ import { listCheckPacks, renderCheckPackSummary, runCheckPack } from './check-packs.js';
42
+ import { renderDoctor, runDoctor } from './doctor.js';
43
+ import {
44
+ recordSessionLearning,
45
+ renderSessionLearningRecordSummary,
46
+ renderSessionLearningsReviewSummary,
47
+ reviewSessionLearnings
48
+ } from './session-learning.js';
49
+ import { createBrainbaseImport } from './brainbase-importer.js';
50
+ import { publishStatusToNocoDB, syncStoriesFromNocoDB } from './nocodb-story-sync.js';
51
+ import { getRepoStatus, renderRepoStatus } from './repo-status.js';
52
+ import {
53
+ comparePerformanceMeasurements,
54
+ renderPerformanceSummary,
55
+ runPerformanceMeasurement
56
+ } from './performance-measurer.js';
57
+ import {
58
+ compareStoryPerformance,
59
+ definePerformanceMetric,
60
+ recordPerformanceRun,
61
+ renderPerformanceDefineSummary,
62
+ renderPerformanceEvidenceSummary,
63
+ renderPerformanceRecordSummary
64
+ } from './performance-evidence.js';
65
+ import {
66
+ closeAgentReviewLifecycle,
67
+ getAgentReviewStatus,
68
+ prepareAgentReview,
69
+ recordAgentReview,
70
+ renderAgentReviewLifecycleCloseSummary,
71
+ renderAgentReviewLifecycleStartSummary,
72
+ renderAgentReviewPrepareSummary,
73
+ renderAgentReviewRecordSummary,
74
+ renderAgentReviewStatusSummary,
75
+ startAgentReviewLifecycle
76
+ } from './agent-review.js';
77
+ import { listCheckpointStages, renderCheckpointSummary, runCheckpoint } from './checkpoint-manager.js';
78
+ import {
79
+ getExecutionNext,
80
+ getExecutionStatus,
81
+ reconcileExecutionState,
82
+ renderExecutionNextSummary,
83
+ renderExecutionStateSummary,
84
+ startExecution,
85
+ updateExecutionStateFromPrCreate,
86
+ updateExecutionStateFromPrMerge,
87
+ updateExecutionStateFromPrPrepare
88
+ } from './execution-state.js';
89
+ import { executeMerge, renderPrMergeSummary } from './merge-manager.js';
90
+ import {
91
+ assertManagedWorktreeCommandAllowed,
92
+ buildManagedWorktreeCommandBinding,
93
+ buildManagedWorktreeCommandWarning,
94
+ evaluateManagedWorktreeCommandContext
95
+ } from './managed-worktree.js';
96
+ import {
97
+ createPullRequest,
98
+ preparePullRequest,
99
+ renderPrCreateSummary,
100
+ renderPrPrepareSummary,
101
+ renderPrShipSummary,
102
+ shipPullRequest
103
+ } from './pr-manager.js';
104
+ import { renderFlowVerificationSummary, runFlowVerification } from './flow-verifier.js';
105
+ import { recordVerificationEvidence, renderVerificationEvidenceSummary } from './verification-evidence.js';
106
+ import {
107
+ getDecisionStatus,
108
+ recordDecision,
109
+ renderDecisionRecordSummary,
110
+ renderDecisionStatusSummary
111
+ } from './decision-records.js';
112
+ import { buildSpecFingerprint } from './spec-fingerprint.js';
113
+ import { validateSpec } from './spec-validator.js';
114
+ import { buildSpecDrift, renderDriftMarkdown } from './spec-drift.js';
115
+ import {
116
+ readInferredSpec,
117
+ stabilizeClauseIds,
118
+ writeDrift,
119
+ writeDriftMarkdown,
120
+ writeInferredSpec
121
+ } from './spec-store.js';
122
+ import { buildReportFingerprint } from './report-fingerprint.js';
123
+ import { validateReportNarrative } from './report-validator.js';
124
+ import {
125
+ readNarrative,
126
+ REPORT_KINDS,
127
+ stabilizeTalkingPointIds,
128
+ writeNarrative
129
+ } from './report-store.js';
130
+ import { createUsageReport, renderUsageReport } from './usage-report.js';
131
+ import {
132
+ addStory,
133
+ archiveStory,
134
+ createStoryPlan,
135
+ createStoryReport,
136
+ deriveStories,
137
+ getStoryRuns,
138
+ getStoryStatus,
139
+ listStories,
140
+ parseStoryOptions,
141
+ readStoryMap,
142
+ renderStoryDeriveSummary,
143
+ renderStoryList,
144
+ renderStoryMap,
145
+ renderStoryPlanSummary,
146
+ renderStoryRuns,
147
+ renderStoryStatus,
148
+ resolveStoryContext,
149
+ selectStory
150
+ } from './story-manager.js';
151
+ import {
152
+ createTasksFromPlan,
153
+ createTaskBrief,
154
+ createTaskExecution,
155
+ createTaskHandoff,
156
+ createTaskPlan,
157
+ listTasks,
158
+ renderTaskCreateSummary,
159
+ renderTaskList,
160
+ renderTaskShow,
161
+ showTask
162
+ } from './task-manager.js';
163
+ import {
164
+ deriveJourneyMap,
165
+ getJourneyStatus,
166
+ renderJourneyMap,
167
+ renderJourneyStatus
168
+ } from './journey-map.js';
169
+ import {
170
+ installBundledSkills,
171
+ listBundledSkills,
172
+ renderSkillsInstall,
173
+ renderSkillsList,
174
+ renderSkillsVerify,
175
+ verifyBundledSkills
176
+ } from './skills-manager.js';
177
+
178
+ const execFileAsync = promisify(execFile);
179
+
180
+ const HELP_EN = `VibePro CLI
181
+
182
+ VibePro is a CLI control plane for safer AI-driven PRs. It turns Story,
183
+ Architecture, Spec, verification, Agent Review, and PR evidence into a
184
+ risk-adaptive Gate DAG, then blocks PR creation until required gates pass.
185
+
186
+ It does not directly rewrite the target repository. It stores diagnosis,
187
+ verification, review, split-plan, and PR-gate evidence under .vibepro/ so
188
+ humans and AI agents can continue with reviewable context.
189
+
190
+ Core model:
191
+ Story defines user value and acceptance criteria.
192
+ Architecture defines boundaries, responsibilities, and dependency direction.
193
+ Spec defines concrete behavior and invariants.
194
+ Graphify expands investigation scope beyond changed files when available.
195
+ Risk-adaptive Gate DAG decides what evidence is required before PR creation.
196
+
197
+ Typical PR-safety flow:
198
+ vibepro init <repo> --story-id <id> --title <title> --language en
199
+ vibepro pr prepare <repo> --base <base-branch> --story-id <id>
200
+ vibepro verify record <repo> --id <id> --kind unit --status pass --command "npm test"
201
+ vibepro review prepare <repo> --id <id> --stage gate
202
+ vibepro review status <repo> --id <id>
203
+ vibepro pr prepare <repo> --base <base-branch> --story-id <id>
204
+ vibepro pr ship <repo> --base <base-branch> --head <branch> --story-id <id> --dry-run
205
+ vibepro pr create <repo> --base <base-branch> --head <branch> --story-id <id>
206
+ vibepro execute merge <repo> --story-id <id> [--strategy merge|squash|rebase]
207
+
208
+ PR prepare creates pr-body, gate-dag, split-plan, review-cockpit, and
209
+ machine-readable evidence under .vibepro/pr/<story-id>/ when initialized.
210
+ If required gates are unresolved, next_commands points back to review or
211
+ verification. Only use vibepro pr create for normal PR creation; do not use raw
212
+ gh pr create as the standard path. After PR creation, use vibepro execute merge
213
+ to record merge-time checks and GitHub merge results.
214
+
215
+ Existing UI modernization:
216
+ vibepro design-system init <repo> --id <ds-id> --product <name>
217
+ Scaffold an empty-but-valid product-local Design System with explicit
218
+ needs_evidence gates before route/code evidence exists.
219
+ vibepro design-system derive <repo> --id <ds-id> --product <name> --routes <csv> --brief <text> --from-code
220
+ Derive a product-local Design System from current route code, style/token
221
+ files, optional Graphify evidence, and product semantics.
222
+ vibepro design-system ingest <repo> --id <ds-id> --bundle <file>
223
+ Normalize external token/component/guideline bundles into VibePro-native
224
+ DS sections as reference evidence only.
225
+ vibepro design-system export <repo> --id <ds-id> --format json|markdown|css
226
+ Export the aggregate DS JSON, human-readable summary, or CSS custom
227
+ property aliases. CSS export reports needs_tokens when no tokens exist.
228
+ vibepro design-system validate <repo> --id <ds-id> --story-id <story-id>
229
+ Validate DS drift, CTA priority, state semantics, component roles,
230
+ navigation/density policy, and secret leakage before UI implementation.
231
+ Review .vibepro/design-system/<ds-id>/evidence-coverage.json and ds-gate.json,
232
+ then use design-modernize derive-system or plan for screen-level work. Generated
233
+ visual ideas are hypotheses; current code, Story/Spec, DS gates, and Gate DAG
234
+ remain authoritative.
235
+
236
+ Usage:
237
+ vibepro help [command]
238
+ vibepro version
239
+ vibepro --version | -v
240
+ vibepro init [repo] [--story-id <id> --title <title>] [--horizon <value>] [--view <value>] [--period <value>] [--started-at <date>] [--due-at <date>] [--language ja|en]
241
+ vibepro config language [repo] --language ja|en
242
+ vibepro doctor [repo] [--fix] [--json]
243
+ vibepro status [repo] [--json]
244
+ vibepro usage report [repo] [--since <date>] [--log <path>] [--codex-log <path>] [--claude-log <path>] [--language ja|en] [--json]
245
+ vibepro skills list [--json]
246
+ vibepro skills install [repo] [--dry-run] [--force] [--json]
247
+ vibepro skills verify [repo] [--json]
248
+ vibepro codex install [repo] [--dry-run] [--force] [--json]
249
+ vibepro codex verify [repo] [--json]
250
+ vibepro harness status [repo] [--json]
251
+ vibepro harness map [repo] [--json]
252
+ vibepro harness learn [repo] --summary <text> [--kind <kind>] [--source <source>] [--evidence <ref>] [--pattern <text>] [--skill-candidate <text>] [--target <surface>] [--json]
253
+ vibepro harness review-learnings [repo] [--json]
254
+ vibepro graph [repo] [--from <graphify-out>] [--run-graphify]
255
+ vibepro env graph [repo] [--json] [--no-write]
256
+ vibepro diagnose [repo] [--run-id <id>]
257
+ vibepro check <ui|security|performance|architecture|pr-readiness|launch-readiness|agent-harness|public-discovery|self-dogfood|oss-readiness|regression-risk|all> [repo] [--run-id <id>] [--story-id <id>] [--base <ref>] [--head <ref>] [--measure] [--include-harness] [--include-public-discovery] [--top <n>] [--coverage-file <path>] [--fail-on-findings] [--json]
258
+ vibepro design-system init [repo] --id <ds-id> --product <name> [--json]
259
+ vibepro design-system derive [repo] --id <ds-id> [--product <name>] [--route <path>] [--routes <csv>] [--brief <text>] [--brief-file <path>] [--from-code] [--run-graphify] [--base-url <url>] [--json]
260
+ vibepro design-system ingest [repo] --id <ds-id> --bundle <file> [--product <name>] [--json]
261
+ vibepro design-system ingest-brief [repo] --id <ds-id> --brief-file <path> [--json]
262
+ vibepro design-system export [repo] --id <ds-id> --format json|markdown|css [--json]
263
+ vibepro design-system validate [repo] --id <ds-id> --story-id <story-id> [--json]
264
+ vibepro design-modernize derive-system [repo] --id <story-id> [--product <name>] [--route <path>] [--routes <csv>] [--brief <text>] [--design-system-bundle <file>] [--json]
265
+ vibepro design-modernize plan [repo] --id <story-id> [--product <name>] [--route <path>] [--routes <csv>] [--base-url <url>] [--brief <text>] [--design-system-id <id>] [--design-system-title <name>] [--design-system-bundle <file>] [--scene-id <id>] [--json]
266
+ vibepro design-modernize capture [repo] --id <story-id> --base-url <url> [--route <path>] [--routes <csv>] [--sample-hotel-id <id>] [--json]
267
+ vibepro verify flow [repo] --base-url <url> [--id <story-id>] [--run-id <id>] [--journey <id>] [--allow-mutation] [--headed] [--basic-auth-env <env>] [--basic-auth <user:pass>] [--json]
268
+ vibepro verify record [repo] --id <story-id> --kind <unit|integration|e2e|typecheck|build> --status <pass|fail|needs_setup> --command <cmd> [--summary <text>] [--artifact <path>] [--json]
269
+ vibepro decision record [repo] --id <story-id> --type <needs_review|noise|waiver|secret_exposure> --summary <text> [--source <gate-or-finding-id>] [--source-status <status>] [--reason <text>] [--artifact <path>] [--reviewer <name>] [--status <open|accepted|rejected|superseded>] [--secret-location <ref> --secret-action <redacted|rotated|revoked|false_positive>] [--from-stdin] [--json]
270
+ vibepro decision status [repo] --id <story-id> [--json]
271
+ vibepro review prepare [repo] --id <story-id> --stage <stage> [--role <role>] [--roles <csv>] [--json]
272
+ vibepro review start [repo] --id <story-id> --stage <stage> --role <role> --agent-system codex|claude_code --agent-id <id> [--timeout-ms <ms>] [--replacement-for <lifecycle-id>] [--json]
273
+ vibepro review close [repo] --id <story-id> --stage <stage> --role <role> --agent-id <id> [--close-reason completed|timeout|replaced|manual_shutdown] [--close-evidence <ref>] [--json]
274
+ vibepro review record [repo] --id <story-id> --stage <stage> --role <role> --status <pass|needs_changes|block> --summary <text> [--finding <severity:id:detail>] [--artifact <path>] [--from-stdin] [--agent-system codex|claude_code|human --execution-mode parallel_subagent|manual_review --agent-id <id>] [--agent-thread-id <id>] [--agent-session-id <id>] [--agent-call-id <id>] [--agent-model <name>] [--agent-transcript <path>] [--agent-closed] [--agent-close-evidence <ref>] [--inspection-summary <text>] [--inspection-evidence <ref>] [--json]
275
+ vibepro review status [repo] --id <story-id> [--stage <stage>] [--all] [--history] [--json]
276
+ vibepro checkpoint <story|implementation-start|test-plan|implementation-complete|verification|pr> [repo] [--story-id <id>] [--base <ref>] [--head <ref>] [--task <task-id>] [--group <group-id>] [--json]
277
+ vibepro execute <start|status|next|reconcile|merge> [repo] --story-id <id> [--target pr_create] [--base <ref>] [--branch <name>] [--worktree-path <path>] [--strategy merge|squash|rebase] [--delete-branch] [--pr <url|number>] [--dry-run] [--json]
278
+ vibepro explore prepare [repo] --id <story-id> [--topic <text>] [--role <role>] [--json]
279
+ vibepro explore record [repo] --id <story-id> --role <role> --status <pass|needs_review|block> --summary <text> [--finding <severity:id:detail>] [--artifact <path>] [--from-stdin] [--agent-system codex|claude_code --execution-mode parallel_subagent --agent-id <id>] [--agent-model <name>] [--agent-transcript <path>] [--json]
280
+ vibepro explore status [repo] --id <story-id> [--json]
281
+ vibepro measure [repo] [--base-url <url>] [--pages <csv>] [--apis <csv>] [--samples <n>] [--build] [--no-typecheck] [--startup-script <name>] [--ready-pattern <regex>] [--startup-timeout <ms>] [--prisma-log <file>] [--command <id=cmd>] [--run-id <id>] [--json]
282
+ vibepro measure compare [repo] --before <performance.json> --after <performance.json> [--json]
283
+ vibepro performance define [repo] --id <story-id> --metric-id <id> --user-story <text> --start-condition <text> --completion-condition <text> [--intermediate-marker <id>] [--timeout-ms <ms>] [--failure-classification <class>] [--evidence-source <server_log|browser_e2e|api_log|client_marker|manual_observation>] [--readiness-kind <server_side|user_perceived|external_dependency|system_internal>] [--comparison-policy <json|name>] [--json]
284
+ vibepro performance record [repo] --id <story-id> --metric-id <id> --label <before|after> --status <completed|blocked|needs_review|timeout|auth_required|resource_unavailable|unknown> [--duration-ms <ms>] [--marker <id=ms>] [--evidence-source <type:ref:summary>] [--completion-condition <text>] [--run-id <id>] [--json]
285
+ vibepro performance compare [repo] --id <story-id> [--metric-id <id>] [--before-label <label>] [--after-label <label>] [--json]
286
+ vibepro story list [repo] [--all]
287
+ vibepro story add [repo] --id <id> --title <title> [--horizon <value>] [--view <value>] [--period <value>] [--started-at <date>] [--due-at <date>]
288
+ vibepro story select [repo] --id <id>
289
+ vibepro story archive [repo] --id <id>
290
+ vibepro story runs [repo] [--id <id>]
291
+ vibepro story status [repo] [--id <id>]
292
+ vibepro story report [repo] [--id <id>]
293
+ vibepro story diagnose [repo] --id <id> [--run-graphify] [--run-id <id>]
294
+ vibepro story derive [repo] [--from-run <run-id>] [--run-graphify] [--from <graphify-out>] [--preset <id>] [--json]
295
+ vibepro story map [repo] [--json]
296
+ vibepro story plan [repo] [--limit <n>] [--json]
297
+ vibepro journey derive [repo] [--id <journey-id>] [--json]
298
+ vibepro journey map [repo] [--json]
299
+ vibepro journey status [repo] [--json]
300
+ vibepro task list [repo] [--id <story-id>]
301
+ vibepro task create [repo] --from-plan [--id <story-id>] [--task <task-id>] [--limit <n>] [--json]
302
+ vibepro task show [repo] --task <task-id> [--id <story-id>]
303
+ vibepro task brief [repo] --task <task-id> [--group <group-id>] [--id <story-id>]
304
+ vibepro task plan [repo] --task <task-id> [--group <group-id>] [--id <story-id>]
305
+ vibepro task handoff [repo] --task <task-id> [--group <group-id>] [--id <story-id>]
306
+ vibepro task execute [repo] --task <task-id> [--group <group-id>] [--id <story-id>] [--base <ref>] [--dry-run-pr] [--json]
307
+ vibepro pr prepare [repo] [--story-id <id>] [--task <task-id>] [--group <group-id>] [--base <ref>] [--head <ref>] [--branch <name>] [--max-files <n>] [--stage-timeout-ms <ms>] [--progress] [--strict] [--allow-extra-files] [--language ja|en] [--json]
308
+ vibepro pr ship [repo] [--story-id <id>] [--task <task-id>] [--group <group-id>] [--base <ref>] [--head <branch>] [--title <title>] [--dry-run] [--allow-needs-verification --verification-waiver <reason>] [--stage-timeout-ms <ms>] [--progress] [--strict] [--allow-extra-files] [--language ja|en] [--json]
309
+ vibepro pr create [repo] [--story-id <id>] [--task <task-id>] [--group <group-id>] [--base <ref>] [--head <branch>] [--title <title>] [--dry-run] [--allow-needs-verification --verification-waiver <reason>] [--stage-timeout-ms <ms>] [--progress] [--strict] [--allow-extra-files] [--language ja|en] [--json]
310
+ vibepro brainbase [repo] [--sync-stories] [--publish-status] [--dry-run] [--story-id <id>]
311
+ vibepro spec fingerprint [repo] --id <story-id> [--include-instructions] [--json]
312
+ vibepro spec write [repo] --id <story-id> [--from-stdin] [--input <file>] [--caller <name>] [--json]
313
+ vibepro spec show [repo] --id <story-id> [--clause <clause-id>] [--json]
314
+ vibepro spec drift [repo] --id <story-id> [--against <git-ref>] [--json]
315
+ vibepro report fingerprint [repo] --kind <kind> --id <story-id> [--base <ref>] [--task <id>] [--group <id>] [--include-instructions]
316
+ vibepro report write [repo] --kind <kind> --id <story-id> [--from-stdin] [--input <file>] [--caller <name>]
317
+ vibepro report show [repo] --kind <kind> --id <story-id>
318
+ `;
319
+
320
+ const HELP_JA = `VibePro CLI
321
+
322
+ VibeProは、AI駆動開発のPRを安全に進めるためのCLI制御基盤です。
323
+ Story / Architecture / Spec / Verification / Agent Review / PR Evidenceを
324
+ risk-adaptive Gate DAGにまとめ、必須Gateが通るまでPR作成を止めます。
325
+
326
+ 対象リポジトリのコードを直接書き換えるのではなく、診断結果・検証証跡・レビュー証跡・
327
+ 分割方針・PR Gateの文脈を .vibepro/ に保存します。
328
+
329
+ まず人間が使う基本コマンド:
330
+ vibepro init <repo> --language ja --story-id <id> --title <title>
331
+ .vibepro/ を作り、出力言語とStoryを設定します。
332
+ vibepro check pr-readiness <repo> --story-id <id> --base <base-branch>
333
+ PR前に見るべき診断をまとめます。
334
+ vibepro pr prepare <repo> --base <base-branch> --story-id <id>
335
+ pr-body / gate-dag / split-plan / review-cockpit を作り、変更リスクを分類します。
336
+ vibepro verify record <repo> --id <id> --kind unit --status pass --command "npm test"
337
+ 現在のgit状態で実行した検証証跡を記録します。
338
+ vibepro review prepare <repo> --id <id> --stage gate
339
+ Codex / Claude Code の並列サブエージェントへ渡すレビュー依頼を作ります。
340
+ vibepro review record <repo> --id <id> --stage gate --role <role> --status pass --summary <text> --agent-system codex|claude_code --execution-mode parallel_subagent --agent-id <id> --agent-closed
341
+ required Agent Review Gate を通すレビュー結果を、現在のgit状態・サブエージェント証跡・close済みlifecycleに紐づけて記録します。
342
+ サブエージェントの結果を受け取った後、review record を実行する前にそのサブエージェントを close/shutdown してください。
343
+ 人間レビューは監査文脈として記録できますが、required gate のpass代替にはなりません。
344
+ vibepro review status <repo> --id <id>
345
+ 必須レビューの不足・stale・blockを確認します。
346
+ vibepro pr ship <repo> --base <base-branch> --head <branch> --story-id <id> --dry-run
347
+ pr prepareを再実行し、PR作成に進めるか、必要なreview prepare / review start / review recordを表示します。
348
+ vibepro pr create <repo> --base <base-branch> --head <branch> --story-id <id>
349
+ Gate DAGがreadyになった後、VibePro経由でPRを作成します。
350
+ vibepro execute merge <repo> --story-id <id> [--strategy merge|squash|rebase]
351
+ PR作成後のmerge可否を監査し、GitHub merge結果をVibePro artifactへ記録します。
352
+
353
+ risk-adaptive Gate DAG:
354
+ workflow_heavy 変更では、workflow replay / production path / release confidence /
355
+ preview・network・runtime review などの重いGateが自動で追加されます。
356
+ 必須Gateが未解決の間、next_commands は PR作成ではなく review / verification / prepare を案内します。
357
+
358
+ .vibepro/ の意味:
359
+ 診断・Story・Gate・レビュー証跡を保存する作業台です。アプリ本体の実装とは分けて扱います。
360
+ AIエージェントには .vibepro/pr/<story-id>/pr-body.md と review-cockpit.html を渡すのが基本です。
361
+
362
+ PR作成経路:
363
+ 通常のPR作成では vibepro pr create を使ってください。GitHub CLIの直接実行はVibePro Gateとwaiver auditを通らないため、標準経路にしません。
364
+
365
+ base branch:
366
+ READMEや例の origin/develop は固定ではありません。リポジトリに合わせて origin/main や main を指定してください。
367
+ init後の案内と pr prepare の出力に候補を表示します。
368
+
369
+ 既存UI modernize:
370
+ vibepro design-system init <repo> --id <ds-id> --product <name>
371
+ route/code証跡がまだない段階で、needs_evidence Gate付きの空だがvalidな
372
+ プロダクトローカルDesign System正本を作ります。
373
+ vibepro design-system derive <repo> --id <ds-id> --product <name> --routes <csv> --brief <text> --brief-file <file> --from-code
374
+ 現行route code、style/token files、任意のGraphify証跡、product semanticsから
375
+ プロダクトローカルなDesign System正本を作ります。
376
+ vibepro design-system ingest <repo> --id <ds-id> --bundle <file>
377
+ 外部DS bundleのtokens/components/guidelinesをreference-onlyとして正規化し、
378
+ VibePro-native DS sectionsへ取り込みます。
379
+ vibepro design-system ingest-brief <repo> --id <ds-id> --brief-file <file>
380
+ 外部visual DS briefをreference-onlyなvisual foundationsとしてnative DSへ取り込みます。
381
+ vibepro design-system export <repo> --id <ds-id> --format json|markdown|css
382
+ aggregate DS JSON、人間向けMarkdown、CSS custom propertiesを出力します。
383
+ token未定義のCSS exportはneeds_tokensとして返します。
384
+ vibepro design-system validate <repo> --id <ds-id> --story-id <story-id>
385
+ DS drift、CTA優先度、状態意味、component role、navigation/density、secret混入を
386
+ Story/Spec/Architecture文脈に対して検証します。
387
+ .vibepro/design-system/<ds-id>/evidence-coverage.json と ds-gate.json を確認し、
388
+ その後に design-modernize derive-system または plan で画面別作業へ進みます。
389
+ 生成された見た目案は仮説であり、現行コード、Story/Spec、DS gate、Gate DAGが正です。
390
+
391
+ 英語で表示したい場合:
392
+ vibepro init <repo> --language en
393
+ vibepro config language <repo> --language en
394
+ vibepro help --language en
395
+
396
+ Usage:
397
+ vibepro help [command] [--language ja|en]
398
+ vibepro version
399
+ vibepro --version | -v
400
+ vibepro init [repo] [--story-id <id> --title <title>] [--horizon <value>] [--view <value>] [--period <value>] [--started-at <date>] [--due-at <date>] [--language ja|en]
401
+ vibepro config language [repo] --language ja|en
402
+ vibepro doctor [repo] [--fix] [--json]
403
+ vibepro status [repo] [--json]
404
+ vibepro usage report [repo] [--since <date>] [--log <path>] [--codex-log <path>] [--claude-log <path>] [--language ja|en] [--json]
405
+ vibepro skills list [--json]
406
+ vibepro skills install [repo] [--dry-run] [--force] [--json]
407
+ vibepro skills verify [repo] [--json]
408
+ vibepro codex install [repo] [--dry-run] [--force] [--json]
409
+ vibepro codex verify [repo] [--json]
410
+ vibepro harness status [repo] [--json]
411
+ vibepro harness map [repo] [--json]
412
+ vibepro harness learn [repo] --summary <text> [--kind <kind>] [--source <source>] [--evidence <ref>] [--pattern <text>] [--skill-candidate <text>] [--target <surface>] [--json]
413
+ vibepro harness review-learnings [repo] [--json]
414
+ vibepro graph [repo] [--from <graphify-out>] [--run-graphify]
415
+ vibepro env graph [repo] [--json] [--no-write]
416
+ vibepro diagnose [repo] [--run-id <id>]
417
+ vibepro check <ui|security|performance|architecture|pr-readiness|launch-readiness|agent-harness|public-discovery|self-dogfood|oss-readiness|regression-risk|all> [repo] [--run-id <id>] [--story-id <id>] [--base <ref>] [--head <ref>] [--measure] [--include-harness] [--include-public-discovery] [--top <n>] [--coverage-file <path>] [--fail-on-findings] [--json]
418
+ vibepro design-system init [repo] --id <ds-id> --product <name> [--json]
419
+ vibepro design-system derive [repo] --id <ds-id> [--product <name>] [--route <path>] [--routes <csv>] [--brief <text>] [--brief-file <path>] [--from-code] [--run-graphify] [--base-url <url>] [--json]
420
+ vibepro design-system ingest [repo] --id <ds-id> --bundle <file> [--product <name>] [--json]
421
+ vibepro design-system ingest-brief [repo] --id <ds-id> --brief-file <path> [--json]
422
+ vibepro design-system export [repo] --id <ds-id> --format json|markdown|css [--json]
423
+ vibepro design-system validate [repo] --id <ds-id> --story-id <story-id> [--json]
424
+ vibepro design-modernize derive-system [repo] --id <story-id> [--product <name>] [--route <path>] [--routes <csv>] [--brief <text>] [--design-system-bundle <file>] [--json]
425
+ vibepro design-modernize plan [repo] --id <story-id> [--product <name>] [--route <path>] [--routes <csv>] [--base-url <url>] [--brief <text>] [--design-system-id <id>] [--design-system-title <name>] [--design-system-bundle <file>] [--scene-id <id>] [--json]
426
+ vibepro design-modernize capture [repo] --id <story-id> --base-url <url> [--route <path>] [--routes <csv>] [--sample-hotel-id <id>] [--json]
427
+ vibepro verify flow [repo] --base-url <url> [--id <story-id>] [--run-id <id>] [--journey <id>] [--allow-mutation] [--headed] [--basic-auth-env <env>] [--basic-auth <user:pass>] [--json]
428
+ vibepro verify record [repo] --id <story-id> --kind <unit|integration|e2e|typecheck|build> --status <pass|fail|needs_setup> --command <cmd> [--summary <text>] [--artifact <path>] [--json]
429
+ vibepro decision record [repo] --id <story-id> --type <needs_review|noise|waiver|secret_exposure> --summary <text> [--source <gate-or-finding-id>] [--source-status <status>] [--reason <text>] [--artifact <path>] [--reviewer <name>] [--status <open|accepted|rejected|superseded>] [--secret-location <ref> --secret-action <redacted|rotated|revoked|false_positive>] [--from-stdin] [--json]
430
+ vibepro decision status [repo] --id <story-id> [--json]
431
+ vibepro review prepare [repo] --id <story-id> --stage <stage> [--role <role>] [--roles <csv>] [--json]
432
+ vibepro review start [repo] --id <story-id> --stage <stage> --role <role> --agent-system codex|claude_code --agent-id <id> [--timeout-ms <ms>] [--replacement-for <lifecycle-id>] [--json]
433
+ vibepro review close [repo] --id <story-id> --stage <stage> --role <role> --agent-id <id> [--close-reason completed|timeout|replaced|manual_shutdown] [--close-evidence <ref>] [--json]
434
+ vibepro review record [repo] --id <story-id> --stage <stage> --role <role> --status <pass|needs_changes|block> --summary <text> [--finding <severity:id:detail>] [--artifact <path>] [--from-stdin] [--agent-system codex|claude_code|human --execution-mode parallel_subagent|manual_review --agent-id <id>] [--agent-thread-id <id>] [--agent-session-id <id>] [--agent-call-id <id>] [--agent-model <name>] [--agent-transcript <path>] [--agent-closed] [--agent-close-evidence <ref>] [--inspection-summary <text>] [--inspection-evidence <ref>] [--json]
435
+ vibepro review status [repo] --id <story-id> [--stage <stage>] [--all] [--history] [--json]
436
+ vibepro execute <start|status|next|reconcile|merge> [repo] --story-id <id> [--target pr_create] [--base <ref>] [--branch <name>] [--worktree-path <path>] [--strategy merge|squash|rebase] [--delete-branch] [--pr <url|number>] [--dry-run] [--json]
437
+ vibepro checkpoint <story|implementation-start|test-plan|implementation-complete|verification|pr> [repo] [--story-id <id>] [--base <ref>] [--head <ref>] [--task <task-id>] [--group <group-id>] [--json]
438
+ vibepro explore prepare [repo] --id <story-id> [--topic <text>] [--role <role>] [--json]
439
+ vibepro explore record [repo] --id <story-id> --role <role> --status <pass|needs_review|block> --summary <text> [--finding <severity:id:detail>] [--artifact <path>] [--from-stdin] [--agent-system codex|claude_code --execution-mode parallel_subagent --agent-id <id>] [--agent-model <name>] [--agent-transcript <path>] [--json]
440
+ vibepro explore status [repo] --id <story-id> [--json]
441
+ vibepro measure [repo] [--base-url <url>] [--pages <csv>] [--apis <csv>] [--samples <n>] [--build] [--no-typecheck] [--startup-script <name>] [--ready-pattern <regex>] [--startup-timeout <ms>] [--prisma-log <file>] [--command <id=cmd>] [--run-id <id>] [--json]
442
+ vibepro measure compare [repo] --before <performance.json> --after <performance.json> [--json]
443
+ vibepro performance define [repo] --id <story-id> --metric-id <id> --user-story <text> --start-condition <text> --completion-condition <text> [--intermediate-marker <id>] [--timeout-ms <ms>] [--failure-classification <class>] [--evidence-source <server_log|browser_e2e|api_log|client_marker|manual_observation>] [--readiness-kind <server_side|user_perceived|external_dependency|system_internal>] [--comparison-policy <json|name>] [--json]
444
+ vibepro performance record [repo] --id <story-id> --metric-id <id> --label <before|after> --status <completed|blocked|needs_review|timeout|auth_required|resource_unavailable|unknown> [--duration-ms <ms>] [--marker <id=ms>] [--evidence-source <type:ref:summary>] [--completion-condition <text>] [--run-id <id>] [--json]
445
+ vibepro performance compare [repo] --id <story-id> [--metric-id <id>] [--before-label <label>] [--after-label <label>] [--json]
446
+ vibepro story diagnose [repo] --id <id> [--run-graphify] [--run-id <id>]
447
+ vibepro story derive [repo] [--from-run <run-id>] [--run-graphify] [--from <graphify-out>] [--preset <id>] [--json]
448
+ vibepro story plan [repo] [--limit <n>] [--json]
449
+ vibepro journey derive [repo] [--id <journey-id>] [--json]
450
+ vibepro journey map [repo] [--json]
451
+ vibepro journey status [repo] [--json]
452
+ vibepro task create [repo] --from-plan [--id <story-id>] [--task <task-id>] [--limit <n>] [--json]
453
+ vibepro pr prepare [repo] [--story-id <id>] [--task <task-id>] [--group <group-id>] [--base <ref>] [--head <ref>] [--branch <name>] [--max-files <n>] [--stage-timeout-ms <ms>] [--progress] [--strict] [--allow-extra-files] [--language ja|en] [--json]
454
+ vibepro pr ship [repo] [--story-id <id>] [--task <task-id>] [--group <group-id>] [--base <ref>] [--head <branch>] [--title <title>] [--dry-run] [--allow-needs-verification --verification-waiver <reason>] [--stage-timeout-ms <ms>] [--progress] [--strict] [--allow-extra-files] [--language ja|en] [--json]
455
+ vibepro pr create [repo] [--story-id <id>] [--task <task-id>] [--group <group-id>] [--base <ref>] [--head <branch>] [--title <title>] [--dry-run] [--allow-needs-verification --verification-waiver <reason>] [--stage-timeout-ms <ms>] [--progress] [--strict] [--allow-extra-files] [--language ja|en] [--json]
456
+ vibepro brainbase [repo] [--sync-stories] [--publish-status] [--dry-run] [--story-id <id>]
457
+ vibepro spec fingerprint [repo] --id <story-id> [--include-instructions] [--json]
458
+ vibepro spec write [repo] --id <story-id> [--from-stdin] [--input <file>] [--caller <name>] [--json]
459
+ vibepro spec show [repo] --id <story-id> [--clause <clause-id>] [--json]
460
+ vibepro spec drift [repo] --id <story-id> [--against <git-ref>] [--json]
461
+ `;
462
+
463
+ // Canonical set of top-level commands. Exported so the CLI smoke-test layer can
464
+ // assert every command is exercised end-to-end — a missing/broken handler import
465
+ // must fail a test before merge, not at runtime (the bug class behind #117/#118).
466
+ export const TOP_LEVEL_COMMANDS = [
467
+ 'version', 'help', 'init', 'config', 'doctor', 'graph', 'env',
468
+ 'harness', 'skills', 'codex', 'brainbase', 'pr', 'story', 'task',
469
+ 'journey', 'execute',
470
+ 'decision', 'verify', 'review', 'checkpoint', 'spec', 'report',
471
+ 'design-modernize', 'design-system', 'explore', 'performance',
472
+ 'nocodb', 'repo-status'
473
+ ];
474
+
475
+ export async function runCli(argv, io = {}) {
476
+ const stdout = io.stdout ?? null;
477
+ const stderr = io.stderr ?? null;
478
+ const [command, ...rest] = argv;
479
+
480
+ try {
481
+ if (!command || command === 'help' || command === '--help' || command === '-h') {
482
+ const language = getOption(rest, '--language') ?? getOption(argv, '--language');
483
+ write(stdout, renderHelp(language));
484
+ return { exitCode: 0, command: 'help' };
485
+ }
486
+
487
+ if (command === 'version' || command === '--version' || command === '-v') {
488
+ const version = await readPackageVersion();
489
+ write(stdout, `${version}\n`);
490
+ return { exitCode: 0, command: 'version', version };
491
+ }
492
+
493
+ if (command === 'init') {
494
+ const repoRoot = rest[0] ?? process.cwd();
495
+ const language = getOption(rest, '--language');
496
+ if (language) assertOutputLanguage(language);
497
+ const workspace = await initWorkspace(repoRoot, { language: language ?? undefined });
498
+ const outputLanguage = await readConfiguredOutputLanguage(repoRoot, language);
499
+ const baseBranch = await detectBaseBranch(repoRoot);
500
+ write(stdout, renderInitSummary({
501
+ language: outputLanguage,
502
+ workspaceDir: workspace.workspaceDir,
503
+ repoRoot,
504
+ baseBranch
505
+ }));
506
+ const storyId = getOption(rest, '--story-id');
507
+ if (storyId) {
508
+ const storyOptions = {
509
+ ...parseStoryOptions(rest),
510
+ story_id: storyId
511
+ };
512
+ const story = await addStory(repoRoot, storyOptions);
513
+ await selectStory(repoRoot, story.story_id);
514
+ write(stdout, localizedText(outputLanguage, {
515
+ ja: `Storyを追加しました: ${story.story_id}\nStoryを選択しました: ${story.story_id}\n`,
516
+ en: `Story added: ${story.story_id}\nStory selected: ${story.story_id}\n`
517
+ }));
518
+ return { exitCode: 0, command, workspace, story };
519
+ }
520
+ return { exitCode: 0, command, workspace };
521
+ }
522
+
523
+ if (command === 'config') {
524
+ const subcommand = rest[0];
525
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
526
+ if (subcommand === 'language') {
527
+ const result = await setOutputLanguage(repoRoot, getOption(rest, '--language'));
528
+ write(stdout, localizedText(result.language, {
529
+ ja: `人間向け出力言語を設定しました: ${result.language}\n`,
530
+ en: `Output language set: ${result.language}\n`
531
+ }));
532
+ return { exitCode: 0, command, subcommand, result };
533
+ }
534
+ write(stderr, `Unknown config command: ${subcommand ?? ''}\n\n${renderHelp()}`);
535
+ return { exitCode: 1, command };
536
+ }
537
+
538
+ if (command === 'skills') {
539
+ const subcommand = rest[0] ?? 'list';
540
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
541
+ if (subcommand === 'list') {
542
+ const skills = await listBundledSkills();
543
+ const result = { skills };
544
+ write(stdout, hasFlag(rest, '--json')
545
+ ? `${JSON.stringify(result, null, 2)}\n`
546
+ : renderSkillsList(skills));
547
+ return { exitCode: 0, command, subcommand, result };
548
+ }
549
+ if (subcommand === 'install') {
550
+ const result = await installBundledSkills(repoRoot, {
551
+ dryRun: hasFlag(rest, '--dry-run'),
552
+ force: hasFlag(rest, '--force')
553
+ });
554
+ write(stdout, hasFlag(rest, '--json')
555
+ ? `${JSON.stringify(result, null, 2)}\n`
556
+ : renderSkillsInstall(result));
557
+ return { exitCode: 0, command, subcommand, result };
558
+ }
559
+ if (subcommand === 'verify') {
560
+ const result = await verifyBundledSkills(repoRoot);
561
+ write(stdout, hasFlag(rest, '--json')
562
+ ? `${JSON.stringify(result, null, 2)}\n`
563
+ : renderSkillsVerify(result));
564
+ return { exitCode: 0, command, subcommand, result };
565
+ }
566
+ write(stderr, `Unknown skills command: ${subcommand ?? ''}\n\n${renderHelp()}`);
567
+ return { exitCode: 1, command };
568
+ }
569
+
570
+ if (command === 'codex') {
571
+ const subcommand = rest[0];
572
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
573
+ if (subcommand === 'install') {
574
+ const result = await installCodexInstructions(repoRoot, {
575
+ dryRun: hasFlag(rest, '--dry-run'),
576
+ force: hasFlag(rest, '--force')
577
+ });
578
+ write(stdout, hasFlag(rest, '--json')
579
+ ? `${JSON.stringify(result, null, 2)}\n`
580
+ : renderCodexInstall(result));
581
+ return { exitCode: 0, command, subcommand, result };
582
+ }
583
+ if (subcommand === 'verify') {
584
+ const result = await verifyCodexInstructions(repoRoot);
585
+ write(stdout, hasFlag(rest, '--json')
586
+ ? `${JSON.stringify(result, null, 2)}\n`
587
+ : renderCodexVerify(result));
588
+ return { exitCode: 0, command, subcommand, result };
589
+ }
590
+ write(stderr, `Unknown codex command: ${subcommand ?? ''}\n\n${renderHelp()}`);
591
+ return { exitCode: 1, command };
592
+ }
593
+
594
+ if (command === 'harness') {
595
+ const subcommand = rest[0];
596
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
597
+ if (!subcommand || subcommand === 'status' || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
598
+ const result = await scanAgentHarness(repoRoot);
599
+ write(stdout, hasFlag(rest, '--json')
600
+ ? `${JSON.stringify(result, null, 2)}\n`
601
+ : renderAgentHarnessStatus(result));
602
+ return { exitCode: 0, command, subcommand: 'status', result };
603
+ }
604
+ if (subcommand === 'map') {
605
+ const result = await generateAgentHarnessMap(repoRoot);
606
+ write(stdout, hasFlag(rest, '--json')
607
+ ? `${JSON.stringify(result, null, 2)}\n`
608
+ : renderAgentHarnessMapSummary(result));
609
+ return { exitCode: 0, command, subcommand, result };
610
+ }
611
+ if (subcommand === 'learn') {
612
+ const result = await recordSessionLearning(repoRoot, {
613
+ id: getOption(rest, '--learning-id') ?? getOption(rest, '--id'),
614
+ kind: getOption(rest, '--kind'),
615
+ summary: getOption(rest, '--summary'),
616
+ source: getOption(rest, '--source'),
617
+ evidence: getOption(rest, '--evidence'),
618
+ pattern: getOption(rest, '--pattern'),
619
+ status: getOption(rest, '--status'),
620
+ skillCandidate: getOption(rest, '--skill-candidate'),
621
+ targets: getOptions(rest, '--target')
622
+ });
623
+ write(stdout, hasFlag(rest, '--json')
624
+ ? `${JSON.stringify(result, null, 2)}\n`
625
+ : renderSessionLearningRecordSummary(result));
626
+ return { exitCode: 0, command, subcommand, result };
627
+ }
628
+ if (subcommand === 'review-learnings') {
629
+ const result = await reviewSessionLearnings(repoRoot);
630
+ write(stdout, hasFlag(rest, '--json')
631
+ ? `${JSON.stringify(result, null, 2)}\n`
632
+ : renderSessionLearningsReviewSummary(result));
633
+ return { exitCode: 0, command, subcommand, result };
634
+ }
635
+ write(stderr, `Unknown harness command: ${subcommand ?? ''}\n\n${renderHelp()}`);
636
+ return { exitCode: 1, command };
637
+ }
638
+
639
+ if (command === 'graph') {
640
+ const repoRoot = rest[0] ?? process.cwd();
641
+ const sourceDir = getOption(rest, '--from');
642
+ const result = await importGraphifyArtifacts(repoRoot, {
643
+ sourceDir,
644
+ runGraphify: hasFlag(rest, '--run-graphify'),
645
+ env: io.env
646
+ });
647
+ write(stdout, `graphify artifacts imported: ${result.graphifyDir}\n`);
648
+ return { exitCode: 0, command, result };
649
+ }
650
+
651
+ if (command === 'env') {
652
+ const subcommand = rest[0];
653
+ const repoRoot = rest[1] ?? process.cwd();
654
+ if (subcommand === 'graph') {
655
+ const graph = await deriveEnvironmentGraph(repoRoot, { write: !hasFlag(rest, '--no-write') });
656
+ write(stdout, hasFlag(rest, '--json')
657
+ ? `${JSON.stringify(graph, null, 2)}\n`
658
+ : renderEnvironmentGraphSummary(graph));
659
+ return { exitCode: 0, command, subcommand, result: graph };
660
+ }
661
+ write(stderr, `Unknown env command: ${subcommand ?? ''}\n\n${renderHelp()}`);
662
+ return { exitCode: 1, command };
663
+ }
664
+
665
+ if (command === 'doctor') {
666
+ const repoRoot = rest[0] && !rest[0].startsWith('--') ? rest[0] : process.cwd();
667
+ const result = await runDoctor(repoRoot, { fix: hasFlag(rest, '--fix') });
668
+ write(stdout, hasFlag(rest, '--json')
669
+ ? `${JSON.stringify(result, null, 2)}\n`
670
+ : renderDoctor(result));
671
+ return { exitCode: 0, command, result };
672
+ }
673
+
674
+ if (command === 'status') {
675
+ const repoRoot = rest[0] && !rest[0].startsWith('--') ? rest[0] : process.cwd();
676
+ const status = await getRepoStatus(repoRoot);
677
+ write(stdout, hasFlag(rest, '--json')
678
+ ? `${JSON.stringify(status, null, 2)}\n`
679
+ : renderRepoStatus(status));
680
+ return { exitCode: 0, command, status };
681
+ }
682
+
683
+ if (command === 'usage') {
684
+ const subcommand = rest[0];
685
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
686
+ if (subcommand === 'report') {
687
+ const result = await createUsageReport(repoRoot, {
688
+ since: getOption(rest, '--since'),
689
+ logs: getOptions(rest, '--log'),
690
+ codexLogs: getOptions(rest, '--codex-log'),
691
+ claudeLogs: getOptions(rest, '--claude-log'),
692
+ language: getOption(rest, '--language')
693
+ });
694
+ write(stdout, hasFlag(rest, '--json')
695
+ ? `${JSON.stringify(result, null, 2)}\n`
696
+ : renderUsageReport(result));
697
+ return { exitCode: 0, command, subcommand, result };
698
+ }
699
+ write(stderr, `Unknown usage command: ${subcommand ?? ''}\n\n${renderHelp()}`);
700
+ return { exitCode: 1, command };
701
+ }
702
+
703
+ if (command === 'diagnose') {
704
+ const repoRoot = rest[0] ?? process.cwd();
705
+ const runId = getOption(rest, '--run-id');
706
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
707
+ const result = await runDiagnosis(repoRoot, { runId, language });
708
+ write(stdout, localizedText(language, {
709
+ ja: `診断を作成しました: ${result.runDir}\n`,
710
+ en: `diagnosis created: ${result.runDir}\n`
711
+ }));
712
+ return { exitCode: 0, command, result };
713
+ }
714
+
715
+ if (command === 'design-system') {
716
+ const subcommand = rest[0] ?? 'derive';
717
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
718
+ if (subcommand === 'init') {
719
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
720
+ const result = await initDesignSystem(repoRoot, {
721
+ id: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
722
+ designSystemId: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
723
+ product: getOption(rest, '--product'),
724
+ language
725
+ });
726
+ write(stdout, hasFlag(rest, '--json')
727
+ ? `${JSON.stringify(result.result, null, 2)}\n`
728
+ : `${renderNativeDesignSystemSummary(result.result, language)}\nArtifacts: ${result.outDir}\n`);
729
+ return { exitCode: 0, command, subcommand, result };
730
+ }
731
+ if (subcommand === 'derive') {
732
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
733
+ const result = await deriveNativeDesignSystem(repoRoot, {
734
+ id: getOption(rest, '--id') ?? getOption(rest, '--design-system-id') ?? getOption(rest, '--product'),
735
+ designSystemId: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
736
+ product: getOption(rest, '--product'),
737
+ routes: parseDesignRoutes(rest),
738
+ brief: getOption(rest, '--brief'),
739
+ briefFile: getOption(rest, '--brief-file'),
740
+ baseUrl: getOption(rest, '--base-url'),
741
+ fromCode: hasFlag(rest, '--from-code'),
742
+ runGraphify: hasFlag(rest, '--run-graphify'),
743
+ graphifyOut: getOption(rest, '--from'),
744
+ language
745
+ });
746
+ write(stdout, hasFlag(rest, '--json')
747
+ ? `${JSON.stringify(result.result, null, 2)}\n`
748
+ : `${renderNativeDesignSystemSummary(result.result, language)}\nArtifacts: ${result.outDir}\n`);
749
+ return { exitCode: 0, command, subcommand, result };
750
+ }
751
+ if (subcommand === 'ingest') {
752
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
753
+ const result = await ingestExternalDesignSystemBundle(repoRoot, {
754
+ id: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
755
+ designSystemId: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
756
+ product: getOption(rest, '--product'),
757
+ bundleFile: getOption(rest, '--bundle') ?? getOption(rest, '--design-system-bundle'),
758
+ language
759
+ });
760
+ write(stdout, hasFlag(rest, '--json')
761
+ ? `${JSON.stringify(result.result, null, 2)}\n`
762
+ : `${renderNativeDesignSystemSummary(result.result, language)}\nArtifacts: ${result.outDir}\n`);
763
+ return { exitCode: 0, command, subcommand, result };
764
+ }
765
+ if (subcommand === 'export') {
766
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
767
+ const format = getOption(rest, '--format') ?? 'json';
768
+ const result = await exportDesignSystem(repoRoot, {
769
+ id: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
770
+ designSystemId: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
771
+ format,
772
+ language
773
+ });
774
+ if (hasFlag(rest, '--json') && result.result.format !== 'json') {
775
+ write(stdout, `${JSON.stringify(result.result, null, 2)}\n`);
776
+ } else {
777
+ write(stdout, result.result.content);
778
+ }
779
+ return { exitCode: 0, command, subcommand, result };
780
+ }
781
+ if (subcommand === 'ingest-brief') {
782
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
783
+ const result = await ingestVisualDesignBrief(repoRoot, {
784
+ id: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
785
+ designSystemId: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
786
+ product: getOption(rest, '--product'),
787
+ briefFile: getOption(rest, '--brief-file'),
788
+ language
789
+ });
790
+ write(stdout, hasFlag(rest, '--json')
791
+ ? `${JSON.stringify(result.result, null, 2)}\n`
792
+ : `${renderNativeDesignSystemSummary(result.result, language)}\nArtifacts: ${result.outDir}\n`);
793
+ return { exitCode: 0, command, subcommand, result };
794
+ }
795
+ if (subcommand === 'validate') {
796
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
797
+ const result = await validateDesignSystem(repoRoot, {
798
+ id: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
799
+ designSystemId: getOption(rest, '--id') ?? getOption(rest, '--design-system-id'),
800
+ storyId: getOption(rest, '--story-id') ?? getOption(rest, '--story'),
801
+ language
802
+ });
803
+ write(stdout, hasFlag(rest, '--json')
804
+ ? `${JSON.stringify(result.result, null, 2)}\n`
805
+ : `${renderDesignSystemValidationSummary(result.result, language)}\nArtifacts: ${result.outDir}\n`);
806
+ return { exitCode: 0, command, subcommand, result };
807
+ }
808
+ write(stderr, `Unknown design-system command: ${subcommand ?? ''}\n\n${renderHelp()}`);
809
+ return { exitCode: 1, command };
810
+ }
811
+
812
+ if (command === 'design-modernize') {
813
+ const subcommand = rest[0] ?? 'plan';
814
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
815
+ if (subcommand === 'plan') {
816
+ const result = await createDesignModernizePlan(repoRoot, {
817
+ storyId: getOption(rest, '--id') ?? getOption(rest, '--story-id') ?? 'design-modernize',
818
+ product: getOption(rest, '--product'),
819
+ routes: parseDesignRoutes(rest),
820
+ brief: getOption(rest, '--brief'),
821
+ baseUrl: getOption(rest, '--base-url'),
822
+ designSystemId: getOption(rest, '--design-system-id'),
823
+ designSystemTitle: getOption(rest, '--design-system-title'),
824
+ designSystemBundle: getOption(rest, '--design-system-bundle'),
825
+ sceneId: getOption(rest, '--scene-id'),
826
+ optionalReferenceStatus: 'not_required',
827
+ optionalReferenceNote: 'No external generator token is required; pass --design-system-bundle only when a reference system should constrain the design.'
828
+ });
829
+ write(stdout, hasFlag(rest, '--json')
830
+ ? `${JSON.stringify(result.plan, null, 2)}\n`
831
+ : `${renderDesignModernizePlan(result.plan)}\nArtifacts: ${result.outDir}\n`);
832
+ return { exitCode: 0, command, subcommand, result };
833
+ }
834
+ if (subcommand === 'derive-system') {
835
+ const result = await deriveProductDesignSystem(repoRoot, {
836
+ storyId: getOption(rest, '--id') ?? getOption(rest, '--story-id') ?? 'design-modernize',
837
+ product: getOption(rest, '--product'),
838
+ routes: parseDesignRoutes(rest),
839
+ brief: getOption(rest, '--brief'),
840
+ baseUrl: getOption(rest, '--base-url'),
841
+ designSystemId: getOption(rest, '--design-system-id'),
842
+ designSystemTitle: getOption(rest, '--design-system-title'),
843
+ designSystemBundle: getOption(rest, '--design-system-bundle')
844
+ });
845
+ write(stdout, hasFlag(rest, '--json')
846
+ ? `${JSON.stringify(result.result, null, 2)}\n`
847
+ : `${renderDerivedDesignSystemSummary(result.result)}\nArtifacts: ${result.outDir}\n`);
848
+ return { exitCode: 0, command, subcommand, result };
849
+ }
850
+ if (subcommand === 'capture') {
851
+ const result = await captureDesignModernizeScreens(repoRoot, {
852
+ storyId: getOption(rest, '--id') ?? getOption(rest, '--story-id') ?? 'design-modernize',
853
+ baseUrl: getOption(rest, '--base-url'),
854
+ routes: parseDesignRoutes(rest),
855
+ sampleHotelId: getOption(rest, '--sample-hotel-id'),
856
+ timeoutMs: parseNumberOption(rest, '--timeout-ms') ?? 30000
857
+ });
858
+ write(stdout, hasFlag(rest, '--json')
859
+ ? `${JSON.stringify(result.result, null, 2)}\n`
860
+ : renderCaptureSummary(result));
861
+ return { exitCode: 0, command, subcommand, result };
862
+ }
863
+ write(stderr, `Unknown design-modernize command: ${subcommand ?? ''}\n\n${renderHelp()}`);
864
+ return { exitCode: 1, command };
865
+ }
866
+
867
+ if (command === 'check') {
868
+ const packId = rest[0];
869
+ if (!packId || packId === 'list' || packId === '--help' || packId === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
870
+ const packs = listCheckPacks();
871
+ const lines = [
872
+ 'Available check packs:',
873
+ '',
874
+ ...packs.map((pack) => `- ${pack.id}: ${pack.title} (${pack.checks.join(', ')})`)
875
+ ];
876
+ write(stdout, `${lines.join('\n')}\n`);
877
+ return { exitCode: 0, command, subcommand: packId ?? 'list', packs };
878
+ }
879
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
880
+ const result = await runCheckPack(repoRoot, {
881
+ packId,
882
+ env: io.env,
883
+ runId: getOption(rest, '--run-id'),
884
+ storyId: getOption(rest, '--story-id') ?? getOption(rest, '--id'),
885
+ baseRef: getOption(rest, '--base'),
886
+ headRef: getOption(rest, '--head'),
887
+ strict: hasFlag(rest, '--strict'),
888
+ measure: hasFlag(rest, '--measure'),
889
+ includeHarness: hasFlag(rest, '--include-harness'),
890
+ includePublicDiscovery: hasFlag(rest, '--include-public-discovery'),
891
+ baseUrl: getOption(rest, '--base-url'),
892
+ pages: parseCsvOption(rest, '--pages'),
893
+ apis: parseCsvOption(rest, '--apis'),
894
+ samples: parseNumberOption(rest, '--samples') ?? 5,
895
+ build: hasFlag(rest, '--build'),
896
+ typecheck: !hasFlag(rest, '--no-typecheck'),
897
+ commands: getOptions(rest, '--command'),
898
+ startups: buildStartupOptions(rest),
899
+ prismaLog: getOption(rest, '--prisma-log'),
900
+ top: parseNumberOption(rest, '--top'),
901
+ coverageFile: getOption(rest, '--coverage-file')
902
+ });
903
+ write(stdout, hasFlag(rest, '--json')
904
+ ? `${JSON.stringify(result.check, null, 2)}\n`
905
+ : renderCheckPackSummary(result));
906
+ const exitCode = hasFlag(rest, '--fail-on-findings') && result.check.status !== 'pass' ? 1 : 0;
907
+ return { exitCode, command, subcommand: packId, result };
908
+ }
909
+
910
+ if (command === 'verify') {
911
+ const subcommand = rest[0];
912
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
913
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
914
+ write(stdout, renderHelp(getOption(rest, '--language')));
915
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
916
+ }
917
+ if (subcommand === 'flow') {
918
+ const storyId = getOption(rest, '--id');
919
+ const managedWorktreeContext = await assertManagedWorktreeCommandAllowed(repoRoot, {
920
+ storyId,
921
+ commandName: 'verify flow'
922
+ });
923
+ const result = await runFlowVerification(repoRoot, {
924
+ baseUrl: getOption(rest, '--base-url'),
925
+ storyId,
926
+ runId: getOption(rest, '--run-id'),
927
+ journeyId: getOption(rest, '--journey'),
928
+ allowMutation: hasFlag(rest, '--allow-mutation'),
929
+ headed: hasFlag(rest, '--headed'),
930
+ basicAuth: getOption(rest, '--basic-auth'),
931
+ basicAuthEnv: getOption(rest, '--basic-auth-env'),
932
+ env: io.env,
933
+ managedWorktreeWarning: buildManagedWorktreeCommandWarning(managedWorktreeContext)
934
+ });
935
+ await reconcileExecutionState(repoRoot, {
936
+ storyId: result.verification?.story_id ?? storyId,
937
+ target: 'pr_create'
938
+ }).catch(() => null);
939
+ write(stdout, hasFlag(rest, '--json')
940
+ ? `${JSON.stringify(result.verification, null, 2)}\n`
941
+ : renderFlowVerificationSummary(result));
942
+ return { exitCode: 0, command, subcommand, result };
943
+ }
944
+ if (subcommand === 'record') {
945
+ const storyId = getOption(rest, '--id') ?? getOption(rest, '--story-id');
946
+ const managedWorktreeContext = await assertManagedWorktreeCommandAllowed(repoRoot, {
947
+ storyId,
948
+ commandName: 'verify record'
949
+ });
950
+ const result = await recordVerificationEvidence(repoRoot, {
951
+ storyId,
952
+ kind: getOption(rest, '--kind'),
953
+ status: getOption(rest, '--status'),
954
+ command: getOption(rest, '--command'),
955
+ summary: getOption(rest, '--summary'),
956
+ artifact: getOption(rest, '--artifact'),
957
+ managedWorktreeContext: buildManagedWorktreeCommandBinding(managedWorktreeContext),
958
+ managedWorktreeWarning: buildManagedWorktreeCommandWarning(managedWorktreeContext)
959
+ });
960
+ await reconcileExecutionState(repoRoot, {
961
+ storyId: result.evidence.story_id,
962
+ target: 'pr_create'
963
+ }).catch(() => null);
964
+ write(stdout, hasFlag(rest, '--json')
965
+ ? `${JSON.stringify(result.evidence, null, 2)}\n`
966
+ : renderVerificationEvidenceSummary(result));
967
+ return { exitCode: 0, command, subcommand, result };
968
+ }
969
+ write(stderr, `Unknown verify command: ${subcommand ?? ''}\n\n${renderHelp()}`);
970
+ return { exitCode: 1, command };
971
+ }
972
+
973
+ if (command === 'review') {
974
+ const subcommand = rest[0];
975
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
976
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
977
+ write(stdout, renderHelp(getOption(rest, '--language')));
978
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
979
+ }
980
+ if (subcommand === 'prepare') {
981
+ const storyId = getOption(rest, '--id') ?? getOption(rest, '--story-id');
982
+ await assertManagedWorktreeCommandAllowed(repoRoot, {
983
+ storyId,
984
+ commandName: 'review prepare'
985
+ });
986
+ const result = await prepareAgentReview(repoRoot, {
987
+ storyId,
988
+ stage: getOption(rest, '--stage'),
989
+ roles: [
990
+ ...getOptions(rest, '--role'),
991
+ ...parseCsvOption(rest, '--roles')
992
+ ],
993
+ language: getOption(rest, '--language')
994
+ });
995
+ await reconcileExecutionState(repoRoot, {
996
+ storyId: result.review?.story_id ?? result.summary?.story_id,
997
+ target: 'pr_create'
998
+ }).catch(() => null);
999
+ write(stdout, hasFlag(rest, '--json')
1000
+ ? `${JSON.stringify(result, null, 2)}\n`
1001
+ : renderAgentReviewPrepareSummary(result));
1002
+ return { exitCode: 0, command, subcommand, result };
1003
+ }
1004
+ if (subcommand === 'start') {
1005
+ const storyId = getOption(rest, '--id') ?? getOption(rest, '--story-id');
1006
+ await assertManagedWorktreeCommandAllowed(repoRoot, {
1007
+ storyId,
1008
+ commandName: 'review start'
1009
+ });
1010
+ const result = await startAgentReviewLifecycle(repoRoot, {
1011
+ storyId,
1012
+ stage: getOption(rest, '--stage'),
1013
+ role: getOption(rest, '--role'),
1014
+ agentSystem: getOption(rest, '--agent-system') ?? getOption(rest, '--reviewer-system'),
1015
+ agentId: getOption(rest, '--agent-id'),
1016
+ agentThreadId: getOption(rest, '--agent-thread-id'),
1017
+ agentSessionId: getOption(rest, '--agent-session-id'),
1018
+ agentCallId: getOption(rest, '--agent-call-id') ?? getOption(rest, '--agent-tool-call-id'),
1019
+ agentModel: getOption(rest, '--agent-model'),
1020
+ timeoutMs: getOption(rest, '--timeout-ms'),
1021
+ replacementFor: getOption(rest, '--replacement-for'),
1022
+ lifecycleId: getOption(rest, '--lifecycle-id')
1023
+ });
1024
+ await reconcileExecutionState(repoRoot, {
1025
+ storyId: result.lifecycle.story_id,
1026
+ target: 'pr_create'
1027
+ }).catch(() => null);
1028
+ write(stdout, hasFlag(rest, '--json')
1029
+ ? `${JSON.stringify(result, null, 2)}\n`
1030
+ : renderAgentReviewLifecycleStartSummary(result));
1031
+ return { exitCode: 0, command, subcommand, result };
1032
+ }
1033
+ if (subcommand === 'close') {
1034
+ const storyId = getOption(rest, '--id') ?? getOption(rest, '--story-id');
1035
+ await assertManagedWorktreeCommandAllowed(repoRoot, {
1036
+ storyId,
1037
+ commandName: 'review close'
1038
+ });
1039
+ const result = await closeAgentReviewLifecycle(repoRoot, {
1040
+ storyId,
1041
+ stage: getOption(rest, '--stage'),
1042
+ role: getOption(rest, '--role'),
1043
+ agentSystem: getOption(rest, '--agent-system') ?? getOption(rest, '--reviewer-system'),
1044
+ agentId: getOption(rest, '--agent-id'),
1045
+ lifecycleId: getOption(rest, '--lifecycle-id'),
1046
+ closeReason: getOption(rest, '--close-reason'),
1047
+ closeEvidence: getOption(rest, '--close-evidence')
1048
+ });
1049
+ await reconcileExecutionState(repoRoot, {
1050
+ storyId: result.lifecycle.story_id,
1051
+ target: 'pr_create'
1052
+ }).catch(() => null);
1053
+ write(stdout, hasFlag(rest, '--json')
1054
+ ? `${JSON.stringify(result, null, 2)}\n`
1055
+ : renderAgentReviewLifecycleCloseSummary(result));
1056
+ return { exitCode: 0, command, subcommand, result };
1057
+ }
1058
+ if (subcommand === 'record') {
1059
+ const storyId = getOption(rest, '--id') ?? getOption(rest, '--story-id');
1060
+ const managedWorktreeContext = await assertManagedWorktreeCommandAllowed(repoRoot, {
1061
+ storyId,
1062
+ commandName: 'review record'
1063
+ });
1064
+ const inputPath = getOption(rest, '--input');
1065
+ const stdinText = hasFlag(rest, '--from-stdin')
1066
+ ? inputPath
1067
+ ? await readFile(path.resolve(inputPath), 'utf8')
1068
+ : await readStdin(io.stdin ?? process.stdin)
1069
+ : '';
1070
+ const result = await recordAgentReview(repoRoot, {
1071
+ storyId,
1072
+ stage: getOption(rest, '--stage'),
1073
+ role: getOption(rest, '--role'),
1074
+ status: getOption(rest, '--status'),
1075
+ summary: getOption(rest, '--summary'),
1076
+ findings: getOptions(rest, '--finding'),
1077
+ artifacts: getOptions(rest, '--artifact'),
1078
+ agentSystem: getOption(rest, '--agent-system') ?? getOption(rest, '--reviewer-system'),
1079
+ executionMode: getOption(rest, '--execution-mode'),
1080
+ agentId: getOption(rest, '--agent-id'),
1081
+ agentRole: getOption(rest, '--agent-role'),
1082
+ agentThreadId: getOption(rest, '--agent-thread-id'),
1083
+ agentSessionId: getOption(rest, '--agent-session-id'),
1084
+ agentCallId: getOption(rest, '--agent-call-id') ?? getOption(rest, '--agent-tool-call-id'),
1085
+ agentModel: getOption(rest, '--agent-model'),
1086
+ agentTranscript: getOption(rest, '--agent-transcript'),
1087
+ agentRequest: getOption(rest, '--agent-request'),
1088
+ agentClosed: hasFlag(rest, '--agent-closed') || hasFlag(rest, '--subagent-closed'),
1089
+ agentCloseEvidence: getOption(rest, '--agent-close-evidence') ?? getOption(rest, '--subagent-close-evidence'),
1090
+ agentCloseNote: getOption(rest, '--agent-close-note') ?? getOption(rest, '--subagent-close-note'),
1091
+ inspectionSummary: getOption(rest, '--inspection-summary'),
1092
+ inspectionEvidence: getOption(rest, '--inspection-evidence'),
1093
+ recordedBy: getOption(rest, '--recorded-by'),
1094
+ stdinText,
1095
+ managedWorktreeContext: buildManagedWorktreeCommandBinding(managedWorktreeContext),
1096
+ managedWorktreeWarning: buildManagedWorktreeCommandWarning(managedWorktreeContext)
1097
+ });
1098
+ await reconcileExecutionState(repoRoot, {
1099
+ storyId: result.review?.story_id ?? result.summary?.story_id,
1100
+ target: 'pr_create'
1101
+ }).catch(() => null);
1102
+ write(stdout, hasFlag(rest, '--json')
1103
+ ? `${JSON.stringify(result, null, 2)}\n`
1104
+ : renderAgentReviewRecordSummary(result));
1105
+ return { exitCode: 0, command, subcommand, result };
1106
+ }
1107
+ if (subcommand === 'status') {
1108
+ const storyId = getOption(rest, '--id') ?? getOption(rest, '--story-id');
1109
+ const managedWorktreeContext = await evaluateManagedWorktreeCommandContext(repoRoot, {
1110
+ storyId,
1111
+ commandName: 'review status'
1112
+ });
1113
+ const result = await getAgentReviewStatus(repoRoot, {
1114
+ storyId,
1115
+ stage: getOption(rest, '--stage'),
1116
+ all: hasFlag(rest, '--all'),
1117
+ history: hasFlag(rest, '--history')
1118
+ });
1119
+ void managedWorktreeContext;
1120
+ write(stdout, hasFlag(rest, '--json')
1121
+ ? `${JSON.stringify(result, null, 2)}\n`
1122
+ : renderAgentReviewStatusSummary(result));
1123
+ return { exitCode: 0, command, subcommand, result };
1124
+ }
1125
+ write(stderr, `Unknown review command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1126
+ return { exitCode: 1, command };
1127
+ }
1128
+
1129
+ if (command === 'decision') {
1130
+ const subcommand = rest[0];
1131
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1132
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1133
+ write(stdout, renderHelp(getOption(rest, '--language')));
1134
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1135
+ }
1136
+ if (subcommand === 'record') {
1137
+ const storyId = getOption(rest, '--id');
1138
+ const isManagedWorktreeWaiver = getOption(rest, '--type') === 'waiver'
1139
+ && getOption(rest, '--source') === 'gate:managed_worktree';
1140
+ const managedWorktreeContext = isManagedWorktreeWaiver
1141
+ ? await evaluateManagedWorktreeCommandContext(repoRoot, {
1142
+ storyId,
1143
+ commandName: 'decision record'
1144
+ })
1145
+ : await assertManagedWorktreeCommandAllowed(repoRoot, {
1146
+ storyId,
1147
+ commandName: 'decision record'
1148
+ });
1149
+ const result = await recordDecision(repoRoot, {
1150
+ storyId,
1151
+ type: getOption(rest, '--type'),
1152
+ source: getOption(rest, '--source'),
1153
+ sourceStatus: getOption(rest, '--source-status'),
1154
+ summary: getOption(rest, '--summary'),
1155
+ reason: getOption(rest, '--reason'),
1156
+ artifact: getOption(rest, '--artifact'),
1157
+ reviewer: getOption(rest, '--reviewer'),
1158
+ status: getOption(rest, '--status'),
1159
+ secretLocation: getOption(rest, '--secret-location'),
1160
+ secretAction: getOption(rest, '--secret-action'),
1161
+ stdinText: hasFlag(rest, '--from-stdin') ? await readStdin(stdin) : '',
1162
+ managedWorktreeWarning: buildManagedWorktreeCommandWarning(managedWorktreeContext)
1163
+ });
1164
+ await reconcileExecutionState(repoRoot, {
1165
+ storyId: result.decision?.story_id ?? storyId,
1166
+ target: 'pr_create'
1167
+ }).catch(() => null);
1168
+ write(stdout, hasFlag(rest, '--json')
1169
+ ? `${JSON.stringify(result, null, 2)}\n`
1170
+ : renderDecisionRecordSummary(result));
1171
+ return { exitCode: 0, command, subcommand, result };
1172
+ }
1173
+ if (subcommand === 'status') {
1174
+ const result = await getDecisionStatus(repoRoot, {
1175
+ storyId: getOption(rest, '--id')
1176
+ });
1177
+ write(stdout, hasFlag(rest, '--json')
1178
+ ? `${JSON.stringify(result, null, 2)}\n`
1179
+ : renderDecisionStatusSummary(result));
1180
+ return { exitCode: 0, command, subcommand, result };
1181
+ }
1182
+ write(stderr, `Unknown decision command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1183
+ return { exitCode: 1, command };
1184
+ }
1185
+
1186
+ if (command === 'checkpoint') {
1187
+ const stage = rest[0] && !rest[0].startsWith('--') ? rest[0] : null;
1188
+ const repoIndex = stage ? 1 : 0;
1189
+ const repoRoot = rest[repoIndex] && !rest[repoIndex].startsWith('--') ? rest[repoIndex] : process.cwd();
1190
+ if (!stage || stage === '--help' || stage === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1191
+ const result = { checkpoints: listCheckpointStages() };
1192
+ write(stdout, hasFlag(rest, '--json')
1193
+ ? `${JSON.stringify(result, null, 2)}\n`
1194
+ : renderCheckpointList(result));
1195
+ return { exitCode: 0, command, subcommand: stage ?? 'help', result };
1196
+ }
1197
+ const result = await runCheckpoint(repoRoot, {
1198
+ stage,
1199
+ storyId: getOption(rest, '--story-id') ?? getOption(rest, '--id'),
1200
+ taskId: getOption(rest, '--task'),
1201
+ groupId: getOption(rest, '--group'),
1202
+ baseRef: getOption(rest, '--base'),
1203
+ headRef: getOption(rest, '--head'),
1204
+ branchName: getOption(rest, '--branch'),
1205
+ strict: hasFlag(rest, '--strict'),
1206
+ allowExtraFiles: hasFlag(rest, '--allow-extra-files'),
1207
+ language: getOption(rest, '--language')
1208
+ });
1209
+ write(stdout, hasFlag(rest, '--json')
1210
+ ? `${JSON.stringify(result, null, 2)}\n`
1211
+ : renderCheckpointSummary(result));
1212
+ return { exitCode: result.status === 'passed' ? 0 : 2, command, subcommand: stage, result };
1213
+ }
1214
+
1215
+ if (command === 'execute') {
1216
+ const subcommand = rest[0];
1217
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1218
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1219
+ write(stdout, renderHelp(getOption(rest, '--language')));
1220
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1221
+ }
1222
+ const executionOptions = {
1223
+ storyId: getOption(rest, '--story-id') ?? getOption(rest, '--id'),
1224
+ target: getOption(rest, '--target') ?? 'pr_create',
1225
+ baseRef: getOption(rest, '--base'),
1226
+ branchName: getOption(rest, '--branch'),
1227
+ worktreePath: getOption(rest, '--worktree-path'),
1228
+ taskId: getOption(rest, '--task'),
1229
+ groupId: getOption(rest, '--group')
1230
+ };
1231
+ if (subcommand === 'start') {
1232
+ const result = await startExecution(repoRoot, executionOptions);
1233
+ write(stdout, hasFlag(rest, '--json')
1234
+ ? `${JSON.stringify(result.state, null, 2)}\n`
1235
+ : renderExecutionStateSummary(result));
1236
+ return { exitCode: 0, command, subcommand, result };
1237
+ }
1238
+ if (subcommand === 'status') {
1239
+ const result = await getExecutionStatus(repoRoot, executionOptions);
1240
+ write(stdout, hasFlag(rest, '--json')
1241
+ ? `${JSON.stringify(result.state, null, 2)}\n`
1242
+ : renderExecutionStateSummary(result));
1243
+ return { exitCode: 0, command, subcommand, result };
1244
+ }
1245
+ if (subcommand === 'next') {
1246
+ const result = await getExecutionNext(repoRoot, executionOptions);
1247
+ write(stdout, hasFlag(rest, '--json')
1248
+ ? `${JSON.stringify(result.next, null, 2)}\n`
1249
+ : renderExecutionNextSummary(result));
1250
+ return { exitCode: 0, command, subcommand, result };
1251
+ }
1252
+ if (subcommand === 'reconcile') {
1253
+ const result = await reconcileExecutionState(repoRoot, executionOptions);
1254
+ write(stdout, hasFlag(rest, '--json')
1255
+ ? `${JSON.stringify(result.state, null, 2)}\n`
1256
+ : renderExecutionStateSummary(result));
1257
+ return { exitCode: 0, command, subcommand, result };
1258
+ }
1259
+ if (subcommand === 'merge') {
1260
+ const storyId = executionOptions.storyId ?? await resolveSelectedStoryId(repoRoot, 'execute merge');
1261
+ await assertManagedWorktreeCommandAllowed(repoRoot, {
1262
+ storyId,
1263
+ commandName: 'execute merge'
1264
+ });
1265
+ const result = await executeMerge(repoRoot, {
1266
+ ...executionOptions,
1267
+ storyId,
1268
+ strategy: getOption(rest, '--strategy'),
1269
+ deleteBranch: hasFlag(rest, '--delete-branch'),
1270
+ pr: getOption(rest, '--pr'),
1271
+ dryRun: hasFlag(rest, '--dry-run'),
1272
+ env: io.env
1273
+ });
1274
+ write(stdout, hasFlag(rest, '--json')
1275
+ ? `${JSON.stringify(result.merge, null, 2)}\n`
1276
+ : renderPrMergeSummary(result));
1277
+ await updateExecutionStateFromPrMerge(repoRoot, result, {
1278
+ target: executionOptions.target,
1279
+ baseRef: executionOptions.baseRef,
1280
+ storyId
1281
+ }).catch(() => null);
1282
+ return {
1283
+ exitCode: result.merge.status === 'blocked' ? 2 : result.merge.status === 'failed' ? 1 : 0,
1284
+ command,
1285
+ subcommand,
1286
+ result
1287
+ };
1288
+ }
1289
+ write(stderr, `Unknown execute command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1290
+ return { exitCode: 1, command };
1291
+ }
1292
+
1293
+ if (command === 'explore') {
1294
+ const subcommand = rest[0];
1295
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1296
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1297
+ write(stdout, renderHelp(getOption(rest, '--language')));
1298
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1299
+ }
1300
+ if (subcommand === 'prepare') {
1301
+ const result = await prepareExploreEvidence(repoRoot, {
1302
+ storyId: getOption(rest, '--id') ?? getOption(rest, '--story-id'),
1303
+ topic: getOption(rest, '--topic'),
1304
+ roles: getOptions(rest, '--role'),
1305
+ language: getOption(rest, '--language')
1306
+ });
1307
+ write(stdout, hasFlag(rest, '--json')
1308
+ ? `${JSON.stringify(result, null, 2)}\n`
1309
+ : renderExplorePrepareSummary(result));
1310
+ return { exitCode: 0, command, subcommand, result };
1311
+ }
1312
+ if (subcommand === 'record') {
1313
+ const inputPath = getOption(rest, '--input');
1314
+ const stdinText = hasFlag(rest, '--from-stdin')
1315
+ ? inputPath
1316
+ ? await readFile(path.resolve(inputPath), 'utf8')
1317
+ : await readStdin(io.stdin ?? process.stdin)
1318
+ : '';
1319
+ const result = await recordExploreEvidence(repoRoot, {
1320
+ storyId: getOption(rest, '--id') ?? getOption(rest, '--story-id'),
1321
+ role: getOption(rest, '--role'),
1322
+ status: getOption(rest, '--status'),
1323
+ summary: getOption(rest, '--summary'),
1324
+ findings: getOptions(rest, '--finding'),
1325
+ artifacts: getOptions(rest, '--artifact'),
1326
+ agentSystem: getOption(rest, '--agent-system'),
1327
+ executionMode: getOption(rest, '--execution-mode'),
1328
+ agentId: getOption(rest, '--agent-id'),
1329
+ agentModel: getOption(rest, '--agent-model'),
1330
+ agentTranscript: getOption(rest, '--agent-transcript'),
1331
+ recordedBy: getOption(rest, '--recorded-by'),
1332
+ stdinText
1333
+ });
1334
+ write(stdout, hasFlag(rest, '--json')
1335
+ ? `${JSON.stringify(result, null, 2)}\n`
1336
+ : renderExploreRecordSummary(result));
1337
+ return { exitCode: 0, command, subcommand, result };
1338
+ }
1339
+ if (subcommand === 'status') {
1340
+ const result = await getExploreEvidenceStatus(repoRoot, {
1341
+ storyId: getOption(rest, '--id') ?? getOption(rest, '--story-id')
1342
+ });
1343
+ write(stdout, hasFlag(rest, '--json')
1344
+ ? `${JSON.stringify(result, null, 2)}\n`
1345
+ : renderExploreStatusSummary(result));
1346
+ return { exitCode: 0, command, subcommand, result };
1347
+ }
1348
+ write(stderr, `Unknown explore command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1349
+ return { exitCode: 1, command };
1350
+ }
1351
+
1352
+ if (command === 'measure') {
1353
+ const subcommand = rest[0];
1354
+ if (subcommand === 'compare') {
1355
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1356
+ const result = await comparePerformanceMeasurements(repoRoot, {
1357
+ before: getOption(rest, '--before'),
1358
+ after: getOption(rest, '--after')
1359
+ });
1360
+ write(stdout, hasFlag(rest, '--json')
1361
+ ? `${JSON.stringify(result.comparison, null, 2)}\n`
1362
+ : result.markdown);
1363
+ return { exitCode: 0, command, subcommand, result };
1364
+ }
1365
+ const repoRoot = rest[0] && !rest[0].startsWith('--') ? rest[0] : process.cwd();
1366
+ const result = await runPerformanceMeasurement(repoRoot, {
1367
+ runId: getOption(rest, '--run-id'),
1368
+ baseUrl: getOption(rest, '--base-url'),
1369
+ pages: parseCsvOption(rest, '--pages'),
1370
+ apis: parseCsvOption(rest, '--apis'),
1371
+ samples: parseNumberOption(rest, '--samples') ?? 5,
1372
+ build: hasFlag(rest, '--build'),
1373
+ typecheck: !hasFlag(rest, '--no-typecheck'),
1374
+ commands: getOptions(rest, '--command'),
1375
+ startups: buildStartupOptions(rest),
1376
+ prismaLog: getOption(rest, '--prisma-log')
1377
+ });
1378
+ write(stdout, hasFlag(rest, '--json')
1379
+ ? `${JSON.stringify(result.measurement, null, 2)}\n`
1380
+ : renderPerformanceSummary(result));
1381
+ return { exitCode: 0, command, result };
1382
+ }
1383
+
1384
+ if (command === 'performance') {
1385
+ const subcommand = rest[0];
1386
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1387
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1388
+ write(stdout, renderHelp(getOption(rest, '--language')));
1389
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1390
+ }
1391
+ if (subcommand === 'define') {
1392
+ const result = await definePerformanceMetric(repoRoot, {
1393
+ storyId: getOption(rest, '--id') ?? getOption(rest, '--story-id'),
1394
+ metricId: getOption(rest, '--metric-id'),
1395
+ userStory: getOption(rest, '--user-story'),
1396
+ startCondition: getOption(rest, '--start-condition'),
1397
+ completionCondition: getOption(rest, '--completion-condition'),
1398
+ intermediateMarkers: getOptions(rest, '--intermediate-marker'),
1399
+ timeoutMs: parseNumberOption(rest, '--timeout-ms'),
1400
+ failureClassifications: getOptions(rest, '--failure-classification'),
1401
+ evidenceSources: getOptions(rest, '--evidence-source'),
1402
+ comparisonPolicy: getOption(rest, '--comparison-policy'),
1403
+ readinessKind: getOption(rest, '--readiness-kind')
1404
+ });
1405
+ write(stdout, hasFlag(rest, '--json')
1406
+ ? `${JSON.stringify(result.metric, null, 2)}\n`
1407
+ : renderPerformanceDefineSummary(result));
1408
+ return { exitCode: 0, command, subcommand, result };
1409
+ }
1410
+ if (subcommand === 'record') {
1411
+ const result = await recordPerformanceRun(repoRoot, {
1412
+ storyId: getOption(rest, '--id') ?? getOption(rest, '--story-id'),
1413
+ metricId: getOption(rest, '--metric-id'),
1414
+ runId: getOption(rest, '--run-id'),
1415
+ label: getOption(rest, '--label'),
1416
+ status: getOption(rest, '--status'),
1417
+ durationMs: parseNumberOption(rest, '--duration-ms'),
1418
+ startedAt: getOption(rest, '--started-at'),
1419
+ completedAt: getOption(rest, '--completed-at'),
1420
+ completionCondition: getOption(rest, '--completion-condition'),
1421
+ markers: getOptions(rest, '--marker'),
1422
+ evidenceSources: getOptions(rest, '--evidence-source'),
1423
+ notes: getOption(rest, '--notes')
1424
+ });
1425
+ write(stdout, hasFlag(rest, '--json')
1426
+ ? `${JSON.stringify(result.run, null, 2)}\n`
1427
+ : renderPerformanceRecordSummary(result));
1428
+ return { exitCode: 0, command, subcommand, result };
1429
+ }
1430
+ if (subcommand === 'compare') {
1431
+ const result = await compareStoryPerformance(repoRoot, {
1432
+ storyId: getOption(rest, '--id') ?? getOption(rest, '--story-id'),
1433
+ metricId: getOption(rest, '--metric-id'),
1434
+ beforeLabel: getOption(rest, '--before-label'),
1435
+ afterLabel: getOption(rest, '--after-label')
1436
+ });
1437
+ write(stdout, hasFlag(rest, '--json')
1438
+ ? `${JSON.stringify(result.comparison, null, 2)}\n`
1439
+ : renderPerformanceEvidenceSummary(result.comparison));
1440
+ return { exitCode: 0, command, subcommand, result };
1441
+ }
1442
+ write(stderr, `Unknown performance command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1443
+ return { exitCode: 1, command };
1444
+ }
1445
+
1446
+ if (command === 'story') {
1447
+ const subcommand = rest[0];
1448
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1449
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1450
+ write(stdout, renderHelp(getOption(rest, '--language')));
1451
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1452
+ }
1453
+ if (subcommand === 'list') {
1454
+ const result = await listStories(repoRoot, { includeArchived: hasFlag(rest, '--all') });
1455
+ write(stdout, renderStoryList(result));
1456
+ return { exitCode: 0, command, subcommand, result };
1457
+ }
1458
+ if (subcommand === 'add') {
1459
+ const story = await addStory(repoRoot, parseStoryOptions(rest));
1460
+ write(stdout, `Story added: ${story.story_id}\n`);
1461
+ return { exitCode: 0, command, subcommand, story };
1462
+ }
1463
+ if (subcommand === 'select') {
1464
+ const story = await selectStory(repoRoot, getOption(rest, '--id'));
1465
+ write(stdout, `Story selected: ${story.story_id}\n`);
1466
+ return { exitCode: 0, command, subcommand, story };
1467
+ }
1468
+ if (subcommand === 'archive') {
1469
+ const story = await archiveStory(repoRoot, getOption(rest, '--id'));
1470
+ write(stdout, `Story archived: ${story.story_id}\n`);
1471
+ return { exitCode: 0, command, subcommand, story };
1472
+ }
1473
+ if (subcommand === 'runs') {
1474
+ const result = await getStoryRuns(repoRoot, getOption(rest, '--id'));
1475
+ write(stdout, renderStoryRuns(result));
1476
+ return { exitCode: 0, command, subcommand, result };
1477
+ }
1478
+ if (subcommand === 'status') {
1479
+ const result = await getStoryStatus(repoRoot, getOption(rest, '--id'));
1480
+ write(stdout, renderStoryStatus(result));
1481
+ return { exitCode: 0, command, subcommand, result };
1482
+ }
1483
+ if (subcommand === 'report') {
1484
+ const result = await createStoryReport(repoRoot, getOption(rest, '--id'));
1485
+ write(stdout, `Story report created: ${result.reportPath}\n`);
1486
+ return { exitCode: 0, command, subcommand, result };
1487
+ }
1488
+ if (subcommand === 'diagnose') {
1489
+ const story = await selectStory(repoRoot, getOption(rest, '--id'));
1490
+ write(stdout, `Story selected: ${story.story_id}\n`);
1491
+ const graph = await importGraphifyArtifacts(repoRoot, {
1492
+ sourceDir: getOption(rest, '--from'),
1493
+ runGraphify: hasFlag(rest, '--run-graphify'),
1494
+ env: io.env
1495
+ });
1496
+ write(stdout, `graphify artifacts imported: ${graph.graphifyDir}\n`);
1497
+ const diagnosis = await runDiagnosis(repoRoot, { runId: getOption(rest, '--run-id') });
1498
+ write(stdout, `diagnosis created: ${diagnosis.runDir}\n`);
1499
+ const report = await createStoryReport(repoRoot, story.story_id);
1500
+ write(stdout, `Story report created: ${report.reportPath}\n`);
1501
+ const status = await getStoryStatus(repoRoot, story.story_id);
1502
+ write(stdout, renderStoryStatus(status));
1503
+ return { exitCode: 0, command, subcommand, result: { story, graph, diagnosis, report, status } };
1504
+ }
1505
+ if (subcommand === 'derive') {
1506
+ let graph = null;
1507
+ if (hasFlag(rest, '--run-graphify') || getOption(rest, '--from')) {
1508
+ graph = await importGraphifyArtifacts(repoRoot, {
1509
+ sourceDir: getOption(rest, '--from'),
1510
+ runGraphify: hasFlag(rest, '--run-graphify'),
1511
+ env: io.env
1512
+ });
1513
+ if (!hasFlag(rest, '--json')) write(stdout, `graphify artifacts imported: ${graph.graphifyDir}\n`);
1514
+ }
1515
+ const result = await deriveStories(repoRoot, {
1516
+ fromRunId: getOption(rest, '--from-run'),
1517
+ preset: getOption(rest, '--preset')
1518
+ });
1519
+ const outputResult = graph ? { ...result, graph } : result;
1520
+ write(stdout, hasFlag(rest, '--json')
1521
+ ? `${JSON.stringify(result.catalog, null, 2)}\n`
1522
+ : renderStoryDeriveSummary(result));
1523
+ return { exitCode: 0, command, subcommand, result: outputResult };
1524
+ }
1525
+ if (subcommand === 'map') {
1526
+ const result = await readStoryMap(repoRoot);
1527
+ write(stdout, hasFlag(rest, '--json')
1528
+ ? `${JSON.stringify(result.catalog, null, 2)}\n`
1529
+ : renderStoryMap(result));
1530
+ return { exitCode: 0, command, subcommand, result };
1531
+ }
1532
+ if (subcommand === 'plan') {
1533
+ const result = await createStoryPlan(repoRoot, { limit: parseNumberOption(rest, '--limit') });
1534
+ write(stdout, hasFlag(rest, '--json')
1535
+ ? `${JSON.stringify(result.plan, null, 2)}\n`
1536
+ : renderStoryPlanSummary(result));
1537
+ return { exitCode: 0, command, subcommand, result };
1538
+ }
1539
+ write(stderr, `Unknown story command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1540
+ return { exitCode: 1, command };
1541
+ }
1542
+
1543
+ if (command === 'journey') {
1544
+ const subcommand = rest[0];
1545
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1546
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1547
+ write(stdout, renderHelp(getOption(rest, '--language')));
1548
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1549
+ }
1550
+ if (subcommand === 'derive') {
1551
+ const result = await deriveJourneyMap(repoRoot, {
1552
+ journeyId: getOption(rest, '--id')
1553
+ });
1554
+ write(stdout, hasFlag(rest, '--json')
1555
+ ? `${JSON.stringify(result.journey, null, 2)}\n`
1556
+ : renderJourneyMap(result));
1557
+ return { exitCode: 0, command, subcommand, result };
1558
+ }
1559
+ if (subcommand === 'map') {
1560
+ const status = await getJourneyStatus(repoRoot);
1561
+ if (status.status === 'missing') {
1562
+ write(stderr, `${status.reason}\n`);
1563
+ return { exitCode: 2, command, subcommand, status };
1564
+ }
1565
+ write(stdout, hasFlag(rest, '--json')
1566
+ ? `${JSON.stringify(status.journey, null, 2)}\n`
1567
+ : renderJourneyMap(status.journey));
1568
+ return { exitCode: 0, command, subcommand, result: status.journey };
1569
+ }
1570
+ if (subcommand === 'status') {
1571
+ const status = await getJourneyStatus(repoRoot);
1572
+ write(stdout, hasFlag(rest, '--json')
1573
+ ? `${JSON.stringify(status, null, 2)}\n`
1574
+ : renderJourneyStatus(status));
1575
+ return { exitCode: 0, command, subcommand, result: status };
1576
+ }
1577
+ write(stderr, `Unknown journey command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1578
+ return { exitCode: 1, command };
1579
+ }
1580
+
1581
+ if (command === 'task') {
1582
+ const subcommand = rest[0];
1583
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1584
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1585
+ write(stdout, renderHelp(getOption(rest, '--language')));
1586
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1587
+ }
1588
+ if (subcommand === 'create') {
1589
+ if (!hasFlag(rest, '--from-plan')) throw new Error('task create currently requires --from-plan');
1590
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
1591
+ const result = await createTasksFromPlan(repoRoot, {
1592
+ storyId: getOption(rest, '--id'),
1593
+ taskId: getOption(rest, '--task'),
1594
+ limit: parseNumberOption(rest, '--limit'),
1595
+ language
1596
+ });
1597
+ write(stdout, hasFlag(rest, '--json')
1598
+ ? `${JSON.stringify(result, null, 2)}\n`
1599
+ : renderTaskCreateSummary(result, language));
1600
+ return { exitCode: 0, command, subcommand, result };
1601
+ }
1602
+ if (subcommand === 'list') {
1603
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
1604
+ const result = await listTasks(repoRoot, { storyId: getOption(rest, '--id') });
1605
+ write(stdout, renderTaskList(result, language));
1606
+ return { exitCode: 0, command, subcommand, result };
1607
+ }
1608
+ if (subcommand === 'show') {
1609
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
1610
+ const result = await showTask(repoRoot, {
1611
+ storyId: getOption(rest, '--id'),
1612
+ taskId: getOption(rest, '--task')
1613
+ });
1614
+ write(stdout, renderTaskShow(result, language));
1615
+ return { exitCode: 0, command, subcommand, result };
1616
+ }
1617
+ if (subcommand === 'brief') {
1618
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
1619
+ const result = await createTaskBrief(repoRoot, {
1620
+ storyId: getOption(rest, '--id'),
1621
+ taskId: getOption(rest, '--task'),
1622
+ groupId: getOption(rest, '--group'),
1623
+ language
1624
+ });
1625
+ write(stdout, localizedText(language, {
1626
+ ja: `Taskブリーフィングを作成しました: ${result.artifacts.markdown}\n`,
1627
+ en: `Task briefing created: ${result.artifacts.markdown}\n`
1628
+ }));
1629
+ return { exitCode: 0, command, subcommand, result };
1630
+ }
1631
+ if (subcommand === 'plan') {
1632
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
1633
+ const result = await createTaskPlan(repoRoot, {
1634
+ storyId: getOption(rest, '--id'),
1635
+ taskId: getOption(rest, '--task'),
1636
+ groupId: getOption(rest, '--group'),
1637
+ language
1638
+ });
1639
+ write(stdout, localizedText(language, {
1640
+ ja: `Task計画を作成しました: ${result.artifacts.markdown}\n`,
1641
+ en: `Task plan created: ${result.artifacts.markdown}\n`
1642
+ }));
1643
+ return { exitCode: 0, command, subcommand, result };
1644
+ }
1645
+ if (subcommand === 'handoff') {
1646
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
1647
+ const result = await createTaskHandoff(repoRoot, {
1648
+ storyId: getOption(rest, '--id'),
1649
+ taskId: getOption(rest, '--task'),
1650
+ groupId: getOption(rest, '--group'),
1651
+ language
1652
+ });
1653
+ write(stdout, localizedText(language, {
1654
+ ja: `Task handoffを作成しました: ${result.artifacts.markdown}\n`,
1655
+ en: `Task handoff created: ${result.artifacts.markdown}\n`
1656
+ }));
1657
+ return { exitCode: 0, command, subcommand, result };
1658
+ }
1659
+ if (subcommand === 'execute') {
1660
+ const storyId = getOption(rest, '--id') ?? getOption(rest, '--story-id') ?? await resolveSelectedStoryId(repoRoot);
1661
+ const managedWorktreeContext = await assertManagedWorktreeCommandAllowed(repoRoot, {
1662
+ storyId,
1663
+ commandName: 'task execute'
1664
+ });
1665
+ const language = await resolveHumanOutputLanguage(repoRoot, { language: getOption(rest, '--language') });
1666
+ const result = await createTaskExecution(repoRoot, {
1667
+ storyId,
1668
+ taskId: getOption(rest, '--task'),
1669
+ groupId: getOption(rest, '--group'),
1670
+ baseRef: getOption(rest, '--base'),
1671
+ dryRunPrCreate: hasFlag(rest, '--dry-run-pr'),
1672
+ language,
1673
+ managedWorktreeWarning: buildManagedWorktreeCommandWarning(managedWorktreeContext)
1674
+ });
1675
+ write(stdout, hasFlag(rest, '--json')
1676
+ ? `${JSON.stringify(result.execution, null, 2)}\n`
1677
+ : localizedText(language, {
1678
+ ja: `Task実行セッションを作成しました: ${result.artifacts.markdown}\n`,
1679
+ en: `Task execution session created: ${result.artifacts.markdown}\n`
1680
+ }));
1681
+ return { exitCode: 0, command, subcommand, result };
1682
+ }
1683
+ write(stderr, `Unknown task command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1684
+ return { exitCode: 1, command };
1685
+ }
1686
+
1687
+ if (command === 'pr') {
1688
+ const subcommand = rest[0];
1689
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1690
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1691
+ write(stdout, renderHelp(getOption(rest, '--language')));
1692
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1693
+ }
1694
+ if (subcommand === 'prepare') {
1695
+ const jsonOutput = hasFlag(rest, '--json');
1696
+ const progressOutput = jsonOutput || hasFlag(rest, '--progress');
1697
+ const storyId = getOption(rest, '--story-id') ?? await resolveSelectedStoryId(repoRoot, 'pr prepare');
1698
+ await assertManagedWorktreeCommandAllowed(repoRoot, {
1699
+ storyId,
1700
+ commandName: 'pr prepare'
1701
+ });
1702
+ const result = await preparePullRequest(repoRoot, {
1703
+ storyId,
1704
+ taskId: getOption(rest, '--task'),
1705
+ groupId: getOption(rest, '--group'),
1706
+ baseRef: getOption(rest, '--base'),
1707
+ headRef: getOption(rest, '--head'),
1708
+ branchName: getOption(rest, '--branch'),
1709
+ maxReviewableFiles: parseNumberOption(rest, '--max-files'),
1710
+ stageTimeoutMs: parseNumberOption(rest, '--stage-timeout-ms'),
1711
+ progressReporter: progressOutput ? (event) => write(stderr, `${renderPrPrepareProgressEvent(event)}\n`) : null,
1712
+ strict: hasFlag(rest, '--strict'),
1713
+ allowExtraFiles: hasFlag(rest, '--allow-extra-files'),
1714
+ language: getOption(rest, '--language')
1715
+ });
1716
+ write(stdout, jsonOutput
1717
+ ? `${JSON.stringify(result.preparation, null, 2)}\n`
1718
+ : renderPrPrepareSummary(result));
1719
+ await updateExecutionStateFromPrPrepare(repoRoot, result, {
1720
+ target: 'pr_create',
1721
+ baseRef: getOption(rest, '--base')
1722
+ }).catch(() => null);
1723
+ return { exitCode: 0, command, subcommand, result };
1724
+ }
1725
+ if (subcommand === 'ship') {
1726
+ const jsonOutput = hasFlag(rest, '--json');
1727
+ const progressOutput = jsonOutput || hasFlag(rest, '--progress');
1728
+ const storyId = getOption(rest, '--story-id') ?? await resolveSelectedStoryId(repoRoot, 'pr ship');
1729
+ await assertManagedWorktreeCommandAllowed(repoRoot, {
1730
+ storyId,
1731
+ commandName: 'pr ship'
1732
+ });
1733
+ const result = await shipPullRequest(repoRoot, {
1734
+ storyId,
1735
+ taskId: getOption(rest, '--task'),
1736
+ groupId: getOption(rest, '--group'),
1737
+ baseRef: getOption(rest, '--base'),
1738
+ prBase: getOption(rest, '--base'),
1739
+ headRef: getOption(rest, '--head-ref'),
1740
+ headBranch: getOption(rest, '--head'),
1741
+ branchName: getOption(rest, '--branch'),
1742
+ maxReviewableFiles: parseNumberOption(rest, '--max-files'),
1743
+ stageTimeoutMs: parseNumberOption(rest, '--stage-timeout-ms'),
1744
+ progressReporter: progressOutput ? (event) => write(stderr, `${renderPrPrepareProgressEvent(event)}\n`) : null,
1745
+ title: getOption(rest, '--title'),
1746
+ dryRun: hasFlag(rest, '--dry-run'),
1747
+ allowNeedsVerification: hasFlag(rest, '--allow-needs-verification'),
1748
+ verificationWaiver: getOption(rest, '--verification-waiver'),
1749
+ strict: hasFlag(rest, '--strict'),
1750
+ allowExtraFiles: hasFlag(rest, '--allow-extra-files'),
1751
+ language: getOption(rest, '--language'),
1752
+ env: io.env
1753
+ });
1754
+ write(stdout, jsonOutput
1755
+ ? `${JSON.stringify(result.ship, null, 2)}\n`
1756
+ : renderPrShipSummary(result));
1757
+ await updateExecutionStateFromPrPrepare(repoRoot, result, {
1758
+ target: 'pr_create',
1759
+ baseRef: getOption(rest, '--base')
1760
+ }).catch(() => null);
1761
+ if (result.execution) {
1762
+ await updateExecutionStateFromPrCreate(repoRoot, result, {
1763
+ target: 'pr_create',
1764
+ baseRef: getOption(rest, '--base')
1765
+ }).catch(() => null);
1766
+ }
1767
+ return { exitCode: 0, command, subcommand, result };
1768
+ }
1769
+ if (subcommand === 'create') {
1770
+ const jsonOutput = hasFlag(rest, '--json');
1771
+ const progressOutput = jsonOutput || hasFlag(rest, '--progress');
1772
+ const storyId = getOption(rest, '--story-id') ?? await resolveSelectedStoryId(repoRoot, 'pr create');
1773
+ await assertManagedWorktreeCommandAllowed(repoRoot, {
1774
+ storyId,
1775
+ commandName: 'pr create'
1776
+ });
1777
+ const result = await createPullRequest(repoRoot, {
1778
+ storyId,
1779
+ taskId: getOption(rest, '--task'),
1780
+ groupId: getOption(rest, '--group'),
1781
+ baseRef: getOption(rest, '--base'),
1782
+ prBase: getOption(rest, '--base'),
1783
+ headRef: getOption(rest, '--head-ref'),
1784
+ headBranch: getOption(rest, '--head'),
1785
+ branchName: getOption(rest, '--branch'),
1786
+ maxReviewableFiles: parseNumberOption(rest, '--max-files'),
1787
+ stageTimeoutMs: parseNumberOption(rest, '--stage-timeout-ms'),
1788
+ progressReporter: progressOutput ? (event) => write(stderr, `${renderPrPrepareProgressEvent(event)}\n`) : null,
1789
+ title: getOption(rest, '--title'),
1790
+ dryRun: hasFlag(rest, '--dry-run'),
1791
+ allowNeedsVerification: hasFlag(rest, '--allow-needs-verification'),
1792
+ verificationWaiver: getOption(rest, '--verification-waiver'),
1793
+ strict: hasFlag(rest, '--strict'),
1794
+ allowExtraFiles: hasFlag(rest, '--allow-extra-files'),
1795
+ language: getOption(rest, '--language'),
1796
+ env: io.env
1797
+ });
1798
+ write(stdout, jsonOutput
1799
+ ? `${JSON.stringify(result.execution, null, 2)}\n`
1800
+ : renderPrCreateSummary(result));
1801
+ await updateExecutionStateFromPrCreate(repoRoot, result, {
1802
+ target: 'pr_create',
1803
+ baseRef: getOption(rest, '--base')
1804
+ }).catch(() => null);
1805
+ return { exitCode: 0, command, subcommand, result };
1806
+ }
1807
+ write(stderr, `Unknown pr command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1808
+ return { exitCode: 1, command };
1809
+ }
1810
+
1811
+ if (command === 'brainbase') {
1812
+ const repoRoot = rest[0] ?? process.cwd();
1813
+ if (hasFlag(rest, '--sync-stories')) {
1814
+ const syncResult = await syncStoriesFromNocoDB(repoRoot, {
1815
+ env: io.env,
1816
+ fetch: io.fetch
1817
+ });
1818
+ write(stdout, `Portfolio dashboard stories synced: ${syncResult.stories.length}\n`);
1819
+ }
1820
+ const result = await createBrainbaseImport(repoRoot);
1821
+ write(stdout, `Portfolio dashboard import state created: ${result.importStatePath}\n`);
1822
+ if (hasFlag(rest, '--publish-status')) {
1823
+ const publishResult = await publishStatusToNocoDB(repoRoot, {
1824
+ env: io.env,
1825
+ fetch: io.fetch,
1826
+ dryRun: hasFlag(rest, '--dry-run'),
1827
+ storyId: getOption(rest, '--story-id')
1828
+ });
1829
+ write(stdout, publishResult.dryRun
1830
+ ? `Portfolio dashboard story status preview created: ${publishResult.storyId}\n`
1831
+ : `Portfolio dashboard story status published: ${publishResult.storyId}\n`);
1832
+ }
1833
+ return { exitCode: 0, command, result };
1834
+ }
1835
+
1836
+ if (command === 'spec') {
1837
+ const subcommand = rest[0];
1838
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1839
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1840
+ write(stdout, renderHelp(getOption(rest, '--language')));
1841
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1842
+ }
1843
+ const storyId = getOption(rest, '--id') ?? getOption(rest, '--story-id');
1844
+
1845
+ if (subcommand === 'fingerprint') {
1846
+ if (!storyId) throw new Error('--id <story-id> is required for spec fingerprint');
1847
+ const fingerprint = await buildSpecFingerprint(repoRoot, {
1848
+ storyId,
1849
+ includeInstructions: hasFlag(rest, '--include-instructions')
1850
+ });
1851
+ write(stdout, `${JSON.stringify(fingerprint, null, 2)}\n`);
1852
+ return { exitCode: 0, command, subcommand, fingerprint };
1853
+ }
1854
+
1855
+ if (subcommand === 'write') {
1856
+ if (!storyId) throw new Error('--id <story-id> is required for spec write');
1857
+ const inputPath = getOption(rest, '--input');
1858
+ const fromStdin = hasFlag(rest, '--from-stdin') || !inputPath;
1859
+ const caller = getOption(rest, '--caller') ?? 'unknown';
1860
+ const raw = inputPath
1861
+ ? await readFile(path.resolve(inputPath), 'utf8')
1862
+ : await readStdin(io.stdin ?? process.stdin);
1863
+ if (!raw.trim()) throw new Error('spec write received empty input');
1864
+ let parsed;
1865
+ try {
1866
+ parsed = JSON.parse(raw);
1867
+ } catch (error) {
1868
+ throw new Error(`spec write: input is not valid JSON: ${error.message}`);
1869
+ }
1870
+ const validation = await validateSpec(repoRoot, parsed, { expectedStoryId: storyId });
1871
+ if (!validation.ok) {
1872
+ write(stdout, `${JSON.stringify({ ok: false, errors: validation.errors, warnings: validation.warnings }, null, 2)}\n`);
1873
+ return { exitCode: 2, command, subcommand, validation };
1874
+ }
1875
+ const previousSpec = await readInferredSpec(repoRoot, storyId);
1876
+ const seeded = {
1877
+ ...parsed,
1878
+ schema_version: '0.1.0',
1879
+ story_id: storyId,
1880
+ generated_at: parsed.generated_at ?? new Date().toISOString(),
1881
+ generated_by: {
1882
+ caller,
1883
+ stage: parsed.generated_by?.stage ?? 'ai_synthesis'
1884
+ },
1885
+ previous_spec_id: previousSpec ? `${previousSpec.generated_at ?? ''}` : null
1886
+ };
1887
+ const stabilized = stabilizeClauseIds(seeded, previousSpec);
1888
+ await writeInferredSpec(repoRoot, storyId, stabilized);
1889
+ write(stdout, `${JSON.stringify({ ok: true, story_id: storyId, clauses: stabilized.clauses.length, warnings: validation.warnings }, null, 2)}\n`);
1890
+ return { exitCode: 0, command, subcommand, spec: stabilized };
1891
+ }
1892
+
1893
+ if (subcommand === 'show') {
1894
+ if (!storyId) throw new Error('--id <story-id> is required for spec show');
1895
+ const spec = await readInferredSpec(repoRoot, storyId);
1896
+ if (!spec) {
1897
+ write(stdout, `${JSON.stringify({ story_id: storyId, found: false }, null, 2)}\n`);
1898
+ return { exitCode: 0, command, subcommand, spec: null };
1899
+ }
1900
+ const clauseId = getOption(rest, '--clause');
1901
+ const projection = clauseId
1902
+ ? { ...spec, clauses: spec.clauses.filter((entry) => entry.id === clauseId) }
1903
+ : spec;
1904
+ write(stdout, `${JSON.stringify(projection, null, 2)}\n`);
1905
+ return { exitCode: 0, command, subcommand, spec: projection };
1906
+ }
1907
+
1908
+ if (subcommand === 'drift') {
1909
+ if (!storyId) throw new Error('--id <story-id> is required for spec drift');
1910
+ const drift = await buildSpecDrift(repoRoot, {
1911
+ storyId,
1912
+ againstRef: getOption(rest, '--against')
1913
+ });
1914
+ await writeDrift(repoRoot, storyId, drift);
1915
+ await writeDriftMarkdown(repoRoot, storyId, renderDriftMarkdown(drift));
1916
+ write(stdout, hasFlag(rest, '--json')
1917
+ ? `${JSON.stringify(drift, null, 2)}\n`
1918
+ : renderDriftMarkdown(drift));
1919
+ return { exitCode: 0, command, subcommand, drift };
1920
+ }
1921
+
1922
+ write(stderr, `Unknown spec command: ${subcommand ?? ''}\n\n${renderHelp()}`);
1923
+ return { exitCode: 1, command };
1924
+ }
1925
+
1926
+ if (command === 'report') {
1927
+ const subcommand = rest[0];
1928
+ const repoRoot = rest[1] && !rest[1].startsWith('--') ? rest[1] : process.cwd();
1929
+ if (!subcommand || subcommand === '--help' || subcommand === '-h' || hasFlag(rest, '--help') || hasFlag(rest, '-h')) {
1930
+ write(stdout, renderHelp(getOption(rest, '--language')));
1931
+ return { exitCode: 0, command, subcommand: subcommand ?? 'help' };
1932
+ }
1933
+ const kind = getOption(rest, '--kind');
1934
+ const storyId = getOption(rest, '--id') ?? getOption(rest, '--story-id');
1935
+ if (!kind || !REPORT_KINDS.has(kind)) {
1936
+ throw new Error(`--kind is required (supported: ${[...REPORT_KINDS].join('|')})`);
1937
+ }
1938
+ if (!storyId) throw new Error('--id <story-id> is required');
1939
+
1940
+ if (subcommand === 'fingerprint') {
1941
+ const fingerprint = await buildReportFingerprint(repoRoot, {
1942
+ kind,
1943
+ storyId,
1944
+ baseRef: getOption(rest, '--base'),
1945
+ taskId: getOption(rest, '--task'),
1946
+ groupId: getOption(rest, '--group'),
1947
+ branchName: getOption(rest, '--branch'),
1948
+ includeInstructions: hasFlag(rest, '--include-instructions')
1949
+ });
1950
+ write(stdout, `${JSON.stringify(fingerprint, null, 2)}\n`);
1951
+ return { exitCode: 0, command, subcommand, fingerprint };
1952
+ }
1953
+
1954
+ if (subcommand === 'write') {
1955
+ const caller = getOption(rest, '--caller') ?? 'unknown';
1956
+ const inputPath = getOption(rest, '--input');
1957
+ const raw = inputPath
1958
+ ? await readFile(path.resolve(inputPath), 'utf8')
1959
+ : await readStdin(io.stdin ?? process.stdin);
1960
+ if (!raw.trim()) throw new Error('report write received empty input');
1961
+ let parsed;
1962
+ try {
1963
+ parsed = JSON.parse(raw);
1964
+ } catch (error) {
1965
+ throw new Error(`report write: input is not valid JSON: ${error.message}`);
1966
+ }
1967
+ const fingerprint = await buildReportFingerprint(repoRoot, {
1968
+ kind,
1969
+ storyId,
1970
+ baseRef: getOption(rest, '--base'),
1971
+ taskId: getOption(rest, '--task'),
1972
+ groupId: getOption(rest, '--group')
1973
+ });
1974
+ const validation = await validateReportNarrative(repoRoot, parsed, fingerprint, { expectedStoryId: storyId });
1975
+ if (!validation.ok) {
1976
+ write(stdout, `${JSON.stringify({ ok: false, errors: validation.errors, warnings: validation.warnings }, null, 2)}\n`);
1977
+ return { exitCode: 2, command, subcommand, validation };
1978
+ }
1979
+ const previousNarrative = await readNarrative(repoRoot, storyId, kind);
1980
+ const seeded = {
1981
+ ...parsed,
1982
+ schema_version: '0.1.0',
1983
+ story_id: storyId,
1984
+ kind,
1985
+ generated_at: parsed.generated_at ?? new Date().toISOString(),
1986
+ generated_by: {
1987
+ caller,
1988
+ stage: parsed.generated_by?.stage ?? 'ai_synthesis'
1989
+ },
1990
+ previous_report_id: previousNarrative ? (previousNarrative.generated_at ?? null) : null,
1991
+ inputs_digest: parsed.inputs_digest ?? fingerprint.inputs_digest
1992
+ };
1993
+ const stabilized = stabilizeTalkingPointIds(seeded, previousNarrative);
1994
+ await writeNarrative(repoRoot, storyId, kind, stabilized);
1995
+ write(stdout, `${JSON.stringify({ ok: true, story_id: storyId, kind, slots: stabilized.narrative_slots.length, warnings: validation.warnings }, null, 2)}\n`);
1996
+ return { exitCode: 0, command, subcommand, narrative: stabilized };
1997
+ }
1998
+
1999
+ if (subcommand === 'show') {
2000
+ const narrative = await readNarrative(repoRoot, storyId, kind);
2001
+ if (!narrative) {
2002
+ write(stdout, `${JSON.stringify({ story_id: storyId, kind, found: false }, null, 2)}\n`);
2003
+ return { exitCode: 0, command, subcommand, narrative: null };
2004
+ }
2005
+ write(stdout, `${JSON.stringify(narrative, null, 2)}\n`);
2006
+ return { exitCode: 0, command, subcommand, narrative };
2007
+ }
2008
+
2009
+ write(stderr, `Unknown report command: ${subcommand ?? ''}\n\n${renderHelp()}`);
2010
+ return { exitCode: 1, command };
2011
+ }
2012
+
2013
+ write(stderr, `Unknown command: ${command}\n\n${renderHelp()}`);
2014
+ return { exitCode: 1, command };
2015
+ } catch (error) {
2016
+ write(stderr, `${error.message}\n`);
2017
+ return { exitCode: 1, command };
2018
+ }
2019
+ }
2020
+
2021
+ function getOption(args, name) {
2022
+ const index = args.indexOf(name);
2023
+ if (index === -1) return null;
2024
+ return args[index + 1] ?? null;
2025
+ }
2026
+
2027
+ function renderHelp(language = null) {
2028
+ return normalizeOutputLanguage(language) === 'en' ? HELP_EN : HELP_JA;
2029
+ }
2030
+
2031
+ function renderCheckpointList(result) {
2032
+ return [
2033
+ '# VibePro Checkpoints',
2034
+ '',
2035
+ ...result.checkpoints.flatMap((checkpoint) => [
2036
+ `- ${checkpoint.stage}: ${checkpoint.label}`,
2037
+ ` ${checkpoint.description}`
2038
+ ]),
2039
+ ''
2040
+ ].join('\n');
2041
+ }
2042
+
2043
+ function renderInitSummary({ language, workspaceDir, repoRoot, baseBranch }) {
2044
+ const prPrepareCommand = `vibepro pr prepare ${shellPath(repoRoot)} --base ${baseBranch ?? '<base-branch>'}`;
2045
+ return localizedText(language, {
2046
+ ja: [
2047
+ `VibePro workspaceを初期化しました: ${workspaceDir}`,
2048
+ '',
2049
+ '.vibepro/ は診断・Story・PR gate・レビュー証跡を保存する作業台です。アプリ本体の実装とは分けて扱います。',
2050
+ `人間向け出力言語: ${language}`,
2051
+ `base branch候補: ${baseBranch ?? '未検出。origin/main, origin/develop, main, develop など実リポジトリの既定branchを指定してください。'}`,
2052
+ '',
2053
+ '次にやること:',
2054
+ '1. README全体を読む前に、まず `vibepro help` の「基本コマンド」を確認する',
2055
+ `2. PR前の道標を作る: ${prPrepareCommand} --story-id <story-id>`,
2056
+ '3. 生成された `.vibepro/pr/<story-id>/review-cockpit.html` と `pr-body.md` を見る',
2057
+ '4. AIエージェントには `pr-body.md`, `gate-dag.html`, `split-plan.html` を渡す',
2058
+ ''
2059
+ ].join('\n'),
2060
+ en: [
2061
+ `VibePro workspace initialized: ${workspaceDir}`,
2062
+ '',
2063
+ '.vibepro/ is the workspace for diagnosis, Story, PR gate, and review evidence. It is separate from application source changes.',
2064
+ `Human output language: ${language}`,
2065
+ `Base branch candidate: ${baseBranch ?? 'not detected. Use the repository default such as origin/main, origin/develop, main, or develop.'}`,
2066
+ '',
2067
+ 'Next steps:',
2068
+ '1. Start with `vibepro help` before reading the full README.',
2069
+ `2. Create the PR guide: ${prPrepareCommand} --story-id <story-id>`,
2070
+ '3. Open `.vibepro/pr/<story-id>/review-cockpit.html` and `pr-body.md`.',
2071
+ '4. Hand `pr-body.md`, `gate-dag.html`, and `split-plan.html` to the coding agent.',
2072
+ ''
2073
+ ].join('\n')
2074
+ });
2075
+ }
2076
+
2077
+ async function detectBaseBranch(repoRoot) {
2078
+ const root = path.resolve(repoRoot);
2079
+ const originHead = await gitOptional(root, ['symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD']);
2080
+ if (originHead) return originHead.replace(/^origin\//, 'origin/');
2081
+ for (const ref of ['origin/main', 'origin/develop', 'main', 'develop', 'master']) {
2082
+ if (await gitRefExists(root, ref)) return ref;
2083
+ }
2084
+ return null;
2085
+ }
2086
+
2087
+ async function readConfiguredOutputLanguage(repoRoot, fallback = null) {
2088
+ try {
2089
+ const config = JSON.parse(await readFile(path.join(repoRoot, '.vibepro', 'config.json'), 'utf8'));
2090
+ return normalizeOutputLanguage(config?.output?.language ?? fallback);
2091
+ } catch {
2092
+ return normalizeOutputLanguage(fallback);
2093
+ }
2094
+ }
2095
+
2096
+ async function resolveSelectedStoryId(repoRoot, commandName = 'task execute') {
2097
+ const config = JSON.parse(await readFile(path.join(getWorkspaceDir(repoRoot), 'config.json'), 'utf8'));
2098
+ const { currentStory } = resolveStoryContext(config);
2099
+ if (!currentStory?.story_id) {
2100
+ throw new Error(`${commandName} requires --id <story-id> or a configured current story`);
2101
+ }
2102
+ return currentStory.story_id;
2103
+ }
2104
+
2105
+ async function gitRefExists(repoRoot, ref) {
2106
+ try {
2107
+ await execFileAsync('git', ['rev-parse', '--verify', '--quiet', ref], { cwd: repoRoot, encoding: 'utf8' });
2108
+ return true;
2109
+ } catch {
2110
+ return false;
2111
+ }
2112
+ }
2113
+
2114
+ async function gitOptional(repoRoot, args) {
2115
+ try {
2116
+ const { stdout } = await execFileAsync('git', args, { cwd: repoRoot, encoding: 'utf8' });
2117
+ return stdout.trim();
2118
+ } catch {
2119
+ return '';
2120
+ }
2121
+ }
2122
+
2123
+ function shellPath(filePath) {
2124
+ if (!filePath || filePath === process.cwd()) return '.';
2125
+ return /\s/.test(filePath) ? JSON.stringify(filePath) : filePath;
2126
+ }
2127
+
2128
+ function getOptions(args, name) {
2129
+ const values = [];
2130
+ for (let index = 0; index < args.length; index += 1) {
2131
+ if (args[index] === name && args[index + 1]) values.push(args[index + 1]);
2132
+ }
2133
+ return values;
2134
+ }
2135
+
2136
+ function hasFlag(args, name) {
2137
+ return args.includes(name);
2138
+ }
2139
+
2140
+ function parseCsvOption(args, name) {
2141
+ const value = getOption(args, name);
2142
+ if (!value) return [];
2143
+ return value.split(',').map((item) => item.trim()).filter(Boolean);
2144
+ }
2145
+
2146
+ function parseDesignRoutes(args) {
2147
+ return [
2148
+ ...parseCsvOption(args, '--routes'),
2149
+ ...getOptions(args, '--route')
2150
+ ].filter(Boolean);
2151
+ }
2152
+
2153
+ function parseNumberOption(args, name) {
2154
+ const value = getOption(args, name);
2155
+ if (value === null) return null;
2156
+ const number = Number(value);
2157
+ if (!Number.isFinite(number)) throw new Error(`${name} must be a number`);
2158
+ return number;
2159
+ }
2160
+
2161
+ function renderPrPrepareProgressEvent(event) {
2162
+ const stage = event.stage ?? 'unknown';
2163
+ if (event.event === 'stage_start') {
2164
+ return event.timeout_ms
2165
+ ? `[vibepro pr prepare] start ${stage} timeout_ms=${event.timeout_ms}`
2166
+ : `[vibepro pr prepare] start ${stage} timeout_ms=disabled`;
2167
+ }
2168
+ if (event.event === 'stage_complete') {
2169
+ return `[vibepro pr prepare] done ${stage} duration_ms=${event.duration_ms}`;
2170
+ }
2171
+ if (event.event === 'stage_timeout') {
2172
+ return `[vibepro pr prepare] timeout ${stage} duration_ms=${event.duration_ms}: ${event.error}`;
2173
+ }
2174
+ if (event.event === 'stage_failed') {
2175
+ return `[vibepro pr prepare] failed ${stage} duration_ms=${event.duration_ms}: ${event.error}`;
2176
+ }
2177
+ return `[vibepro pr prepare] ${event.event ?? 'progress'} ${stage}`;
2178
+ }
2179
+
2180
+ function buildStartupOptions(args) {
2181
+ const scripts = getOptions(args, '--startup-script');
2182
+ const readyPattern = getOption(args, '--ready-pattern');
2183
+ const timeoutMs = parseNumberOption(args, '--startup-timeout') ?? 30000;
2184
+ return scripts.map((script) => ({
2185
+ id: `startup:${script}`,
2186
+ script,
2187
+ readyPattern,
2188
+ timeoutMs
2189
+ }));
2190
+ }
2191
+
2192
+ function write(stream, text) {
2193
+ if (stream) stream.write(text);
2194
+ }
2195
+
2196
+ async function readStdin(stream) {
2197
+ if (!stream || stream.isTTY) return '';
2198
+ const chunks = [];
2199
+ for await (const chunk of stream) {
2200
+ chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
2201
+ }
2202
+ return Buffer.concat(chunks).toString('utf8');
2203
+ }
2204
+
2205
+ async function readPackageVersion() {
2206
+ try {
2207
+ const pkgPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'package.json');
2208
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
2209
+ return pkg.version ?? 'unknown';
2210
+ } catch {
2211
+ return 'unknown';
2212
+ }
2213
+ }