paseo-req-kit 0.0.0-stage → 1.7.0-beta.3

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 (130) hide show
  1. package/CHANGELOG.md +217 -0
  2. package/LICENSE +21 -0
  3. package/README.md +141 -2
  4. package/SCOPE.md +11 -0
  5. package/bin/locate.mjs +46 -0
  6. package/bin/req-doctor.mjs +21 -0
  7. package/bin/req.mjs +175 -0
  8. package/bin/start-pipeline +21 -0
  9. package/bin/start-pipeline.mjs +22 -0
  10. package/config.example.json +101 -0
  11. package/contracts/README.md +18 -0
  12. package/contracts/feature-files.md +87 -0
  13. package/contracts/notifier-event.md +71 -0
  14. package/contracts/result-file.md +17 -0
  15. package/contracts/spec.md +34 -0
  16. package/contracts/state.md +44 -0
  17. package/contracts/verify.md +39 -0
  18. package/docs/adr/0001-step-agents-under-origin-chat.md +15 -0
  19. package/docs/adr/0002-no-directory-switch-in-chat.md +15 -0
  20. package/docs/adr/0003-script-engine-not-hub.md +15 -0
  21. package/docs/adr/0004-result-files.md +15 -0
  22. package/docs/adr/0005-no-default-branch-local-commits-only.md +15 -0
  23. package/docs/adr/0006-skill-install-targets.md +15 -0
  24. package/docs/adr/0007-own-interview-skill.md +15 -0
  25. package/docs/adr/0008-entries-do-not-call-entries.md +15 -0
  26. package/docs/adr/0009-versioned-install-layout.md +15 -0
  27. package/docs/adr/0010-generated-feature-overview.md +15 -0
  28. package/docs/adr/0011-resume-after-completion.md +15 -0
  29. package/docs/adr/0012-review-panel.md +18 -0
  30. package/docs/adr/0013-no-release-tooling.md +15 -0
  31. package/docs/adr/0014-notifier-slot.md +19 -0
  32. package/docs/adr/0015-incremental-continue.md +17 -0
  33. package/docs/adr/0016-upgrade-from-tags.md +19 -0
  34. package/docs/adr/0017-use-paseo-capabilities.md +18 -0
  35. package/docs/adr/0018-own-worktree-option.md +21 -0
  36. package/docs/adr/0019-parallel-orchestration.md +21 -0
  37. package/docs/adr/0020-scheduling-through-the-chat.md +21 -0
  38. package/docs/adr/0021-npm-version-manager.md +19 -0
  39. package/docs/adr/0022-develop-and-verify-phases.md +32 -0
  40. package/docs/adr/README.md +26 -0
  41. package/docs/guide//344/275/277/347/224/250/350/200/205/346/214/207/345/215/227.md +78 -0
  42. package/docs/guide//345/256/211/350/243/205/344/270/216/345/215/207/347/272/247.md +348 -0
  43. package/docs/guide//347/273/264/346/212/244/350/200/205/346/214/207/345/215/227.md +85 -0
  44. package/docs/guide//351/241/271/347/233/256/345/256/232/345/210/266/344/270/216/350/277/220/350/241/214/350/247/204/345/210/231.md +251 -0
  45. package/engine/prompts/00-shared-context.md +100 -0
  46. package/engine/prompts/02-spec-review.md +46 -0
  47. package/engine/prompts/03-design.md +50 -0
  48. package/engine/prompts/03a-design-proposal.md +30 -0
  49. package/engine/prompts/04-design-review.md +72 -0
  50. package/engine/prompts/04b-design-revise.md +34 -0
  51. package/engine/prompts/05-implement.md +66 -0
  52. package/engine/prompts/06-code-review.md +66 -0
  53. package/engine/prompts/07-review-fix.md +41 -0
  54. package/engine/prompts/07b-handoff.md +45 -0
  55. package/engine/prompts/08-service-start.md +59 -0
  56. package/engine/prompts/09-verify.md +90 -0
  57. package/engine/prompts/10-defect-fix.md +41 -0
  58. package/engine/prompts/10b-reverify.md +43 -0
  59. package/engine/prompts/11-report.md +93 -0
  60. package/engine/prompts/browser-verify.md +13 -0
  61. package/engine/prompts/incremental.md +21 -0
  62. package/engine/prompts/panel-member.md +38 -0
  63. package/engine/prompts/paseo-services.md +9 -0
  64. package/engine/prompts/verify-deployed.md +14 -0
  65. package/engine/src/activity.ts +87 -0
  66. package/engine/src/cli-args.ts +204 -0
  67. package/engine/src/config.ts +479 -0
  68. package/engine/src/configure.ts +237 -0
  69. package/engine/src/control.ts +230 -0
  70. package/engine/src/daemon-check.ts +104 -0
  71. package/engine/src/daemon-version.ts +66 -0
  72. package/engine/src/digest.ts +133 -0
  73. package/engine/src/doctor.ts +435 -0
  74. package/engine/src/eval.ts +324 -0
  75. package/engine/src/features.ts +268 -0
  76. package/engine/src/git.ts +30 -0
  77. package/engine/src/launch.ts +405 -0
  78. package/engine/src/locations.ts +55 -0
  79. package/engine/src/main.ts +313 -0
  80. package/engine/src/messages.ts +308 -0
  81. package/engine/src/notifiers.ts +76 -0
  82. package/engine/src/notify-test.ts +97 -0
  83. package/engine/src/paseo-json.ts +73 -0
  84. package/engine/src/paseo-profiles.ts +133 -0
  85. package/engine/src/paseo-runtime.ts +437 -0
  86. package/engine/src/paths.ts +10 -0
  87. package/engine/src/pipeline.ts +1126 -0
  88. package/engine/src/preflight.ts +298 -0
  89. package/engine/src/prompts.ts +72 -0
  90. package/engine/src/queue.ts +61 -0
  91. package/engine/src/runtime-loader.ts +14 -0
  92. package/engine/src/runtime.ts +155 -0
  93. package/engine/src/schedule-time.ts +51 -0
  94. package/engine/src/smoke-test.ts +202 -0
  95. package/engine/src/spec-review.ts +100 -0
  96. package/engine/src/state.ts +278 -0
  97. package/engine/src/steps.ts +242 -0
  98. package/engine/src/usage.ts +67 -0
  99. package/engine/src/worktree.ts +72 -0
  100. package/install.mjs +928 -0
  101. package/notifiers/im-template.mjs +58 -0
  102. package/package.json +67 -4
  103. package/principles/INDEX.md +11 -0
  104. package/skills/acceptance-criteria/SKILL.md +52 -0
  105. package/skills/principle-delete-before-add/SKILL.md +33 -0
  106. package/skills/principle-dont-block-on-humans/SKILL.md +35 -0
  107. package/skills/principle-fix-root-cause/SKILL.md +33 -0
  108. package/skills/principle-prove-it-works/SKILL.md +33 -0
  109. package/skills/principle-report-honestly/SKILL.md +33 -0
  110. package/skills/principle-speak-to-reader/SKILL.md +33 -0
  111. package/skills/principle-this-requirement-only/SKILL.md +33 -0
  112. package/skills/req/SKILL.md +99 -0
  113. package/skills/req/playbooks/accept.md +65 -0
  114. package/skills/req/playbooks/change.md +51 -0
  115. package/skills/req/playbooks/incident.md +55 -0
  116. package/skills/req/playbooks/new.md +83 -0
  117. package/skills/req/playbooks/small.md +48 -0
  118. package/skills/req/playbooks/status.md +39 -0
  119. package/skills/req/playbooks/takeover.md +51 -0
  120. package/skills/req/playbooks/verify.md +57 -0
  121. package/skills/req-doctor/SKILL.md +36 -0
  122. package/skills/req-retro/SKILL.md +79 -0
  123. package/skills/req-setup/SKILL.md +101 -0
  124. package/skills/req-setup/templates/paseo/copy-local-files.mjs +32 -0
  125. package/skills/req-setup/templates/paseo/with-port.mjs +36 -0
  126. package/skills/requirement-interview/SKILL.md +94 -0
  127. package/skills/requirement-kickoff/SKILL.md +17 -0
  128. package/skills/result-report/SKILL.md +34 -0
  129. package/skills/spec-writing/SKILL.md +41 -0
  130. package/skills/spec-writing/spec-template.md +106 -0
@@ -0,0 +1,9 @@
1
+ # Paseo 管理的服务
2
+
3
+ 这个项目在 `paseo.json` 中定义了服务,流水线已经在本步骤开始前,通过 Paseo 带着当前的代码把它们启动好了(见 `<pipeline-context>` 中"Paseo 管理的服务"几行)。Paseo 为每个工作区分配独立的端口,同时进行的其他需求不会和你冲突。
4
+
5
+ - **直接使用给出的地址**访问服务,不要再自己启动同一个服务的另一份(会占用端口、读到旧代码)。
6
+ - 服务**没有启动成功**或**健康检查没有通过**时,先读给出的输出文件找原因。原因在这次的代码改动里的,按本步骤的要求处理;属于环境问题的,如实记录。需要时可以按项目的验证方法自己另起一份(换一个空闲端口),用完关掉。
7
+ - 你修改了代码、而服务不会自动加载修改时:如果你有 Paseo 的工作区脚本工具(`stop_workspace_script`、`start_workspace_script`),用它们重启;没有的话,按项目的验证方法另起一份带着新代码的服务来确认,用完关掉。流水线在下一个需要服务的步骤开始前,会重新启动这些服务。
8
+ - **不需要关闭** Paseo 管理的服务,流水线会在验证结束后停止它们。
9
+ - 在产物中写启动方法时,写 `paseo.json` 中的脚本名和对应的命令,并注明"由 Paseo 管理"。
@@ -0,0 +1,14 @@
1
+ # 在已部署的测试环境中验证
2
+
3
+ 这一次的测试验证不在本地启动服务,而是对已经部署好的测试环境进行(`<pipeline-context>` 中有"验证环境:已部署的测试环境")。环境的情况写在功能目录的 `test-env.md` 中:访问地址、测试账号和口令从哪里取、可以使用的测试数据、不能碰的东西。
4
+
5
+ 规则:
6
+
7
+ - **先读 `test-env.md`,只使用其中列出的地址、账号和数据。** 文件里没有写的环境、账号、数据,一律不要用;需要而没有的,把相关 AC 记为"无法验证",并写明需要补充什么。
8
+ - **绝不连接生产环境,绝不使用生产凭据。** 地址看起来像生产环境,或者 `test-env.md` 里写了生产环境的内容时,停止端到端验证,在 `verification.md` 中写明原因,结果文件的 `result` 写 `"blocked"`。
9
+ - 口令、令牌按 `test-env.md` 写明的位置读取(例如环境变量、本机的配置文件),不要把它们写进任何产物、日志或提交。
10
+ - 测试环境是共享的。只做 `test-env.md` 允许的写操作;自己造的数据,结束后按 `test-env.md` 的方法清理,清理不了的在 `verification.md` 中列出。
11
+ - 先确认测试环境上运行的就是这次要测的版本:按 `test-env.md` 写的方式查看版本号、提交号或部署时间,与 `handoff.md` 中的提交范围对照。对不上时,不要继续测;在 `verification.md` 中写明,结果文件的 `result` 写 `"blocked"`。
12
+ - 单元测试和其他自动化检查仍然在本地运行。
13
+
14
+ **缺陷修复和复验**:修复只发生在本地代码里,不会自动出现在测试环境中。修复步骤在本地用测试证明修复有效;复验步骤能在本地验证的就在本地验证(单元测试、本地运行),只能在测试环境里确认的,标为"待重新部署后复验",并写明需要重新部署哪个提交。
@@ -0,0 +1,87 @@
1
+ // Turns an agent's Paseo timeline into a few plain-language lines for "做到哪了":
2
+ // what it ran, read or changed most recently. Shapes follow
3
+ // @getpaseo/protocol agent-types.ts (AgentTimelineItem, ToolCallDetail).
4
+
5
+ import type { ActivityItem } from "./runtime.js";
6
+
7
+ interface TimelineEntry {
8
+ timestamp: string;
9
+ item: unknown;
10
+ }
11
+
12
+ const clip = (text: string, max = 80) => {
13
+ const one = text.replace(/\s+/g, " ").trim();
14
+ return one.length > max ? `${one.slice(0, max - 1)}…` : one;
15
+ };
16
+
17
+ function shortPath(file: string): string {
18
+ const parts = file.split(/[\\/]/).filter(Boolean);
19
+ return parts.length > 3 ? `…/${parts.slice(-3).join("/")}` : parts.join("/");
20
+ }
21
+
22
+ function describeTool(item: Record<string, unknown>): string | null {
23
+ const detail = (item.detail ?? {}) as Record<string, unknown>;
24
+ const failed = item.status === "failed" ? "(失败)" : "";
25
+ switch (detail.type) {
26
+ case "shell":
27
+ return `运行命令:${clip(String(detail.command ?? ""))}${failed}`;
28
+ case "read":
29
+ return `阅读 ${shortPath(String(detail.filePath ?? ""))}`;
30
+ case "edit":
31
+ return `修改 ${shortPath(String(detail.filePath ?? ""))}${failed}`;
32
+ case "write":
33
+ return `写入 ${shortPath(String(detail.filePath ?? ""))}${failed}`;
34
+ case "search":
35
+ return `搜索:${clip(String(detail.query ?? ""), 50)}`;
36
+ case "fetch":
37
+ return `访问 ${clip(String(detail.url ?? ""), 60)}`;
38
+ case "sub_agent":
39
+ return `子任务:${clip(String(detail.description ?? detail.subAgentType ?? ""), 60)}`;
40
+ default: {
41
+ const name = String(item.name ?? "");
42
+ if (name.startsWith("browser_")) return `操作浏览器(${name.slice("browser_".length)})`;
43
+ return name ? `使用工具 ${name}${failed}` : null;
44
+ }
45
+ }
46
+ }
47
+
48
+ function describeItem(item: unknown): string | null {
49
+ if (typeof item !== "object" || item === null) return null;
50
+ const it = item as Record<string, unknown>;
51
+ switch (it.type) {
52
+ case "tool_call":
53
+ return describeTool(it);
54
+ case "assistant_message":
55
+ return typeof it.text === "string" && it.text.trim() ? `说明:${clip(it.text)}` : null;
56
+ case "todo": {
57
+ const items = Array.isArray(it.items) ? (it.items as Array<{ completed?: boolean }>) : [];
58
+ return items.length ? `待办清单:完成 ${items.filter((t) => t.completed).length}/${items.length}` : null;
59
+ }
60
+ case "error":
61
+ return `出错:${clip(String(it.message ?? ""))}`;
62
+ default:
63
+ return null;
64
+ }
65
+ }
66
+
67
+ /** The last `limit` describable entries, oldest first, with repeats of the same line collapsed. */
68
+ export function summarizeActivity(entries: TimelineEntry[], limit: number): ActivityItem[] {
69
+ const out: ActivityItem[] = [];
70
+ for (const entry of entries) {
71
+ const text = describeItem(entry.item);
72
+ if (!text) continue;
73
+ if (out.at(-1)?.text === text) out[out.length - 1] = { at: entry.timestamp, text };
74
+ else out.push({ at: entry.timestamp, text });
75
+ }
76
+ return out.slice(-limit);
77
+ }
78
+
79
+ /** Lines for the status output, e.g. " 10:42 运行命令:npm test". */
80
+ export function describeActivity(items: ActivityItem[], now = new Date()): string[] {
81
+ return items.map((a) => {
82
+ const at = new Date(a.at);
83
+ const minutes = Math.round((now.getTime() - at.getTime()) / 60_000);
84
+ const when = Number.isNaN(minutes) ? "" : minutes < 1 ? "刚刚" : minutes < 60 ? `${minutes} 分钟前` : `${Math.floor(minutes / 60)} 小时前`;
85
+ return ` ${when ? `${when}:` : ""}${a.text}`;
86
+ });
87
+ }
@@ -0,0 +1,204 @@
1
+ import path from "node:path";
2
+ import { parseArgs } from "node:util";
3
+ import { type DefectHandling, PHASE_MODES, type PhaseMode, type ProfileName, type VerifyEnvironment } from "./config.js";
4
+ import { LAUNCHER } from "./locations.js";
5
+ import type { LaunchArgs } from "./preflight.js";
6
+
7
+ export { LAUNCHER };
8
+
9
+ export const USAGE = `用法:
10
+ start-pipeline --feature <功能目录> [--profile quick|standard] [--resume | --restart]
11
+ start-pipeline [--feature <功能目录>] --status 查看进度(不写 --feature 时:工作区里只有一个需求运行过就看它)
12
+ start-pipeline --feature <功能目录> --stop 停止(之后可用 --resume 继续)
13
+ start-pipeline --list 列出这个项目所有工作区中的需求
14
+ start-pipeline --feature <功能目录> --spec-review 确认前请另一个模型独立审阅需求说明草稿(后台进行,结果发回对话)
15
+ start-pipeline --feature <功能目录> --worktree 为这个需求新建 Paseo worktree 工作区并在那里开发(对话目录不变)
16
+ start-pipeline --feature <功能目录> --at 22:00 预约在指定时间开始(也可以写"明天 8:30""2026-10-14 08:30""+2h")
17
+ start-pipeline --feature <功能目录> --phases gated 分阶段:研发完成后停下等待测试验证(continuous | gated | develop-only)
18
+ start-pipeline --feature <功能目录> --phase verify 开始测试验证阶段(研发阶段完成后;可以加 --at 预约)
19
+
20
+ 其他选项:[--repo <仓库目录>] [--origin-agent <agent ID>] [--config <配置文件>]
21
+
22
+ --feature 功能目录,相对于仓库目录,例如 docs/features/20261012-sms-login;
23
+ 也可以只写目录名(20261012-sms-login)或其中能唯一确定的一部分(sms-login)
24
+ --repo 仓库目录。在 Paseo 的聊天中运行时默认为该聊天的工作目录,否则为当前目录
25
+ --origin-agent 发起对话的 agent ID。在 Paseo 的聊天中运行时默认为该聊天(PASEO_AGENT_ID),
26
+ 流水线会把进展和结果发回这个对话;在聊天之外运行且不传时,只写日志。
27
+ --profile 档位:quick(快速,小改动)或 standard(标准)。不传时读取 spec 中的"档位",
28
+ 再不行用配置中的 defaultProfile
29
+ --resume 从上次中断处继续
30
+ --restart 丢弃上次的结果,完全从零开始(不参考上次的方案和代码)
31
+ --phases 执行方式:continuous(连续,默认)、gated(分阶段)、develop-only(只做研发)。
32
+ 不传时读取 spec 中的"执行方式",再不行用配置中的 phases.mode
33
+ --phase verify 开始测试验证阶段(等同于 --resume;研发完成、待测试验证时单独的 --resume 效果相同)
34
+ --verify-at 分阶段时,研发完成后在这个时间自动开始测试验证(写法同 --at)
35
+ --verify-env 测试验证的环境:local(本地启动服务)或 deployed(已部署的测试环境,需要 test-env.md)
36
+ --defects 测试验证发现缺陷时:fix(当场修复并复验)、handback(退回研发阶段修复)、report(只记录)
37
+ --config 配置文件,默认为 ~/.req-kit/config.json(可用环境变量 REQ_KIT_HOME 改变目录)`;
38
+
39
+ export function parseLaunchArgs(argv: string[]): LaunchArgs {
40
+ const { values } = parseArgs({
41
+ args: argv,
42
+ options: {
43
+ feature: { type: "string" },
44
+ repo: { type: "string" },
45
+ "origin-agent": { type: "string" },
46
+ resume: { type: "boolean", default: false },
47
+ restart: { type: "boolean", default: false },
48
+ config: { type: "string" },
49
+ profile: { type: "string" },
50
+ status: { type: "boolean", default: false },
51
+ stop: { type: "boolean", default: false },
52
+ list: { type: "boolean", default: false },
53
+ "spec-review": { type: "boolean", default: false },
54
+ worktree: { type: "boolean", default: false },
55
+ "source-repo": { type: "string" },
56
+ "workspace-id": { type: "string" },
57
+ at: { type: "string" },
58
+ phases: { type: "string" },
59
+ phase: { type: "string" },
60
+ "verify-at": { type: "string" },
61
+ "verify-env": { type: "string" },
62
+ defects: { type: "string" },
63
+ help: { type: "boolean", default: false },
64
+ },
65
+ strict: true,
66
+ });
67
+ if (values.help) {
68
+ console.log(USAGE);
69
+ process.exit(0);
70
+ }
71
+ if (!values.feature && !values.list && !values.status) {
72
+ throw new Error(`缺少 --feature\n\n${USAGE}`);
73
+ }
74
+ const profile = values.profile?.trim().toLowerCase();
75
+ if (profile !== undefined && profile !== "quick" && profile !== "standard") {
76
+ throw new Error(`--profile 只能是 quick 或 standard,收到 "${values.profile}"`);
77
+ }
78
+ if ([values.status, values.stop, values.resume, values.restart, values["spec-review"]].filter(Boolean).length > 1) {
79
+ throw new Error("--status、--stop、--resume、--restart、--spec-review 一次只能用一个");
80
+ }
81
+ if (values.at && (values.status || values.stop || values["spec-review"] || values.resume)) {
82
+ throw new Error("--at 只用于启动(预约开始时间)");
83
+ }
84
+ if (values.worktree && (values.status || values.stop || values["spec-review"] || values.resume)) {
85
+ throw new Error("--worktree 只用于第一次启动(或 --restart)");
86
+ }
87
+ const phases = values.phases?.trim().toLowerCase();
88
+ if (phases !== undefined && !PHASE_MODES.includes(phases as PhaseMode)) {
89
+ throw new Error(`--phases 只能是 ${PHASE_MODES.join("、")},收到 "${values.phases}"`);
90
+ }
91
+ const phase = values.phase?.trim().toLowerCase();
92
+ if (phase !== undefined && phase !== "verify") throw new Error(`--phase 只能是 verify,收到 "${values.phase}"`);
93
+ if (phase && (values.status || values.stop || values["spec-review"] || values.restart || values.worktree)) {
94
+ throw new Error("--phase verify 只用于开始(或继续)测试验证阶段");
95
+ }
96
+ const verifyEnv = values["verify-env"]?.trim().toLowerCase();
97
+ if (verifyEnv !== undefined && verifyEnv !== "local" && verifyEnv !== "deployed") {
98
+ throw new Error(`--verify-env 只能是 local 或 deployed,收到 "${values["verify-env"]}"`);
99
+ }
100
+ const defects = values.defects?.trim().toLowerCase();
101
+ if (defects !== undefined && !["fix", "handback", "report"].includes(defects)) {
102
+ throw new Error(`--defects 只能是 fix、handback 或 report,收到 "${values.defects}"`);
103
+ }
104
+ // In a Paseo chat, the chat itself is the origin unless told otherwise.
105
+ const origin = (values["origin-agent"] ?? process.env.PASEO_AGENT_ID)?.trim();
106
+ return {
107
+ // In a Paseo chat, the chat's working directory is the repository.
108
+ repo: path.resolve(values.repo ?? process.env.PASEO_AGENT_CWD ?? process.cwd()),
109
+ featureDir: values.feature ?? "",
110
+ originAgentId: origin ? origin : null,
111
+ resume: (values.resume ?? false) || phase === "verify",
112
+ restart: values.restart ?? false,
113
+ configPath: values.config ? path.resolve(values.config) : null,
114
+ profile: (profile as ProfileName | undefined) ?? null,
115
+ status: values.status ?? false,
116
+ stop: values.stop ?? false,
117
+ list: values.list ?? false,
118
+ specReview: values["spec-review"] ?? false,
119
+ worktree: values.worktree ?? false,
120
+ sourceRepo: values["source-repo"] ? path.resolve(values["source-repo"]) : null,
121
+ workspaceId: values["workspace-id"] ?? null,
122
+ at: values.at ?? null,
123
+ phases: (phases as PhaseMode | undefined) ?? null,
124
+ phase: phase === "verify" ? "verify" : null,
125
+ verifyAt: values["verify-at"] ?? null,
126
+ verifyEnv: (verifyEnv as VerifyEnvironment | undefined) ?? null,
127
+ defects: (defects as DefectHandling | undefined) ?? null,
128
+ };
129
+ }
130
+
131
+ /** Double quotes work in bash, PowerShell and cmd alike; backslash paths are always quoted for bash. */
132
+ function quote(value: string): string {
133
+ return /^[\w@%+=:,./-]+$/.test(value) ? value : `"${value.replace(/"/g, '\\"')}"`;
134
+ }
135
+
136
+ /**
137
+ * The command a chat agent runs to continue this pipeline. The origin chat is not
138
+ * named: run from a chat, that chat becomes the origin; run elsewhere, the previous
139
+ * origin is kept.
140
+ */
141
+ /** The command a chat agent runs to see progress (same shape as resumeCommand). */
142
+ export function statusCommand(args: { repo: string; featureDir: string; configPath: string | null }): string {
143
+ return resumeCommand(args).replace(/ --resume$/, " --status");
144
+ }
145
+
146
+ /** The full start command, for a start scheduled for later (--at). */
147
+ export function startCommand(args: {
148
+ repo: string;
149
+ featureDir: string;
150
+ configPath: string | null;
151
+ profile: string | null;
152
+ worktree: boolean;
153
+ phases?: string | null;
154
+ verifyAt?: string | null;
155
+ }): string {
156
+ const parts = ["node", LAUNCHER, "--repo", args.repo, "--feature", args.featureDir];
157
+ if (args.configPath) parts.push("--config", args.configPath);
158
+ if (args.profile) parts.push("--profile", args.profile);
159
+ if (args.worktree) parts.push("--worktree");
160
+ if (args.phases) parts.push("--phases", args.phases);
161
+ if (args.verifyAt) parts.push("--verify-at", args.verifyAt);
162
+ return parts.map(quote).join(" ");
163
+ }
164
+
165
+ /** The command that starts the verify phase once development is done. */
166
+ export function verifyCommand(args: {
167
+ repo: string;
168
+ featureDir: string;
169
+ configPath: string | null;
170
+ verifyEnv?: string | null;
171
+ defects?: string | null;
172
+ }): string {
173
+ const extra = [args.verifyEnv ? ` --verify-env ${args.verifyEnv}` : "", args.defects ? ` --defects ${args.defects}` : ""].join("");
174
+ return `${resumeCommand(args)} --phase verify${extra}`;
175
+ }
176
+
177
+ export function resumeCommand(args: { repo: string; featureDir: string; configPath: string | null }): string {
178
+ const parts = ["node", LAUNCHER, "--repo", args.repo, "--feature", args.featureDir];
179
+ if (args.configPath) parts.push("--config", args.configPath);
180
+ parts.push("--resume");
181
+ return parts.map(quote).join(" ");
182
+ }
183
+
184
+ /**
185
+ * Arguments for the pipeline process, rebuilt from what the launcher resolved (short
186
+ * feature names, a requirement's own worktree), so both processes see the same thing.
187
+ */
188
+ export function launchArgv(args: LaunchArgs): string[] {
189
+ const out = ["--repo", args.repo, "--feature", args.featureDir];
190
+ if (args.originAgentId) out.push("--origin-agent", args.originAgentId);
191
+ if (args.configPath) out.push("--config", args.configPath);
192
+ if (args.profile) out.push("--profile", args.profile);
193
+ if (args.resume) out.push("--resume");
194
+ if (args.restart) out.push("--restart");
195
+ if (args.sourceRepo) out.push("--source-repo", args.sourceRepo);
196
+ if (args.workspaceId) out.push("--workspace-id", args.workspaceId);
197
+ if (args.specReview) out.push("--spec-review");
198
+ if (args.phases) out.push("--phases", args.phases);
199
+ if (args.phase) out.push("--phase", args.phase);
200
+ if (args.verifyAt) out.push("--verify-at", args.verifyAt);
201
+ if (args.verifyEnv) out.push("--verify-env", args.verifyEnv);
202
+ if (args.defects) out.push("--defects", args.defects);
203
+ return out;
204
+ }