@tea-agent/loop-agent 0.1.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 (264) hide show
  1. package/AGENTS.md +121 -0
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +144 -0
  4. package/bin/loop-agent.js +21 -0
  5. package/dist/adapters/aimax.js +91 -0
  6. package/dist/adapters/context.js +32 -0
  7. package/dist/adapters/index.js +28 -0
  8. package/dist/adapters/loop-agent.js +98 -0
  9. package/dist/adapters/types.js +1 -0
  10. package/dist/cli/catalog.js +259 -0
  11. package/dist/cli/help.js +55 -0
  12. package/dist/cli/index.js +3 -0
  13. package/dist/cli/program.js +505 -0
  14. package/dist/cli.js +12 -0
  15. package/dist/commands/closeout.js +13 -0
  16. package/dist/commands/coverage-audit.js +14 -0
  17. package/dist/commands/cursor-prompt.js +222 -0
  18. package/dist/commands/cursor-worker.js +43 -0
  19. package/dist/commands/dag-approve.js +102 -0
  20. package/dist/commands/dag-final-verification.js +76 -0
  21. package/dist/commands/dag-init-hybrid.js +56 -0
  22. package/dist/commands/dag-reconcile-tasks.js +51 -0
  23. package/dist/commands/dag-reject.js +91 -0
  24. package/dist/commands/dag-report.js +177 -0
  25. package/dist/commands/dag-resume.js +34 -0
  26. package/dist/commands/dag-run-task.js +470 -0
  27. package/dist/commands/dag-validate.js +186 -0
  28. package/dist/commands/dag-workflow-compile.js +91 -0
  29. package/dist/commands/dag-workflow-plan.js +130 -0
  30. package/dist/commands/dag-workflow-validate.js +66 -0
  31. package/dist/commands/delegate.js +132 -0
  32. package/dist/commands/docs-archive.js +5 -0
  33. package/dist/commands/docs-audit.js +5 -0
  34. package/dist/commands/doctor.js +50 -0
  35. package/dist/commands/goal.js +92 -0
  36. package/dist/commands/handoff-check.js +5 -0
  37. package/dist/commands/harvest.js +44 -0
  38. package/dist/commands/inspect.js +11 -0
  39. package/dist/commands/instructions.js +195 -0
  40. package/dist/commands/knowledge.js +64 -0
  41. package/dist/commands/loop-benchmark.js +72 -0
  42. package/dist/commands/loop.js +241 -0
  43. package/dist/commands/new-task.js +5 -0
  44. package/dist/commands/pi-prompt.js +181 -0
  45. package/dist/commands/pi-reuse-benchmark.js +153 -0
  46. package/dist/commands/plan-list.js +5 -0
  47. package/dist/commands/promote-run.js +29 -0
  48. package/dist/commands/reference-index.js +16 -0
  49. package/dist/commands/run-dag.js +184 -0
  50. package/dist/commands/spine.js +38 -0
  51. package/dist/commands/stats.js +84 -0
  52. package/dist/commands/status.js +56 -0
  53. package/dist/commands/study-init.js +192 -0
  54. package/dist/commands/workflow.js +259 -0
  55. package/dist/commands/worktree-create.js +31 -0
  56. package/dist/commands/worktree-list.js +5 -0
  57. package/dist/commands/worktree-remove.js +26 -0
  58. package/dist/cursor-worker-entry.js +8 -0
  59. package/dist/executors/config-core.js +55 -0
  60. package/dist/executors/config.js +2 -0
  61. package/dist/executors/cursor-artifacts.js +33 -0
  62. package/dist/executors/cursor-execution-log.js +81 -0
  63. package/dist/executors/cursor-executor-artifacts.js +135 -0
  64. package/dist/executors/cursor-executor.js +468 -0
  65. package/dist/executors/cursor-run.js +115 -0
  66. package/dist/executors/cursor-tool.js +94 -0
  67. package/dist/executors/cursor-worker-client.js +213 -0
  68. package/dist/executors/cursor-worker-protocol.js +18 -0
  69. package/dist/executors/cursor-worker-server.js +54 -0
  70. package/dist/executors/cursor-worker.js +3 -0
  71. package/dist/executors/cursor.js +6 -0
  72. package/dist/executors/dag-cursor-executor.js +88 -0
  73. package/dist/executors/dag-pi-executor.js +322 -0
  74. package/dist/executors/dag-static-executor.js +45 -0
  75. package/dist/executors/dag.js +4 -0
  76. package/dist/executors/index.js +8 -0
  77. package/dist/executors/model-routing.js +60 -0
  78. package/dist/executors/pi-event-serializer.js +43 -0
  79. package/dist/executors/pi-executor.js +606 -0
  80. package/dist/executors/pi-reuse-benchmark.js +316 -0
  81. package/dist/executors/pi-runtime-reuse.js +29 -0
  82. package/dist/executors/pi-sdk-executor.js +255 -0
  83. package/dist/executors/pi-sdk.js +1 -0
  84. package/dist/executors/pi.js +3 -0
  85. package/dist/executors/shell-executor.js +300 -0
  86. package/dist/executors/shell-presets.js +47 -0
  87. package/dist/executors/shell-verification.js +251 -0
  88. package/dist/executors/shell-write-guard.js +126 -0
  89. package/dist/executors/shell.js +3 -0
  90. package/dist/executors/static.js +1 -0
  91. package/dist/governance/checks.js +434 -0
  92. package/dist/governance/harness.js +9 -0
  93. package/dist/governance/index.js +3 -0
  94. package/dist/governance/manifest-types.js +128 -0
  95. package/dist/governance/manifest.js +2 -0
  96. package/dist/governance/path-guard.js +69 -0
  97. package/dist/governance/path-guards.js +2 -0
  98. package/dist/governance/profiles.js +3 -0
  99. package/dist/governance/requirement-coverage.js +425 -0
  100. package/dist/governance/skill-safety.js +135 -0
  101. package/dist/governance/spine-audit.js +152 -0
  102. package/dist/records/closeout.js +2 -0
  103. package/dist/records/harvest.js +236 -0
  104. package/dist/records/index.js +3 -0
  105. package/dist/records/one-shot-runs.js +421 -0
  106. package/dist/records/promotion.js +199 -0
  107. package/dist/shared/artifacts-core.js +88 -0
  108. package/dist/shared/artifacts.js +2 -0
  109. package/dist/shared/context-files.js +32 -0
  110. package/dist/shared/context.js +2 -0
  111. package/dist/shared/copy-dir.js +17 -0
  112. package/dist/shared/git-progress.js +165 -0
  113. package/dist/shared/index.js +5 -0
  114. package/dist/shared/logger.js +23 -0
  115. package/dist/shared/one-shot-prompt-args.js +98 -0
  116. package/dist/shared/path-refs.js +31 -0
  117. package/dist/shared/prompts.js +26 -0
  118. package/dist/shared/reference-context.js +238 -0
  119. package/dist/shared/timeout-policy.js +19 -0
  120. package/dist/shared/timeout.js +1 -0
  121. package/dist/shared/types.js +5 -0
  122. package/dist/task/config-types.js +97 -0
  123. package/dist/task/config.js +2 -0
  124. package/dist/task/delegate.js +220 -0
  125. package/dist/task/goal-audit.js +51 -0
  126. package/dist/task/goal-policy.js +8 -0
  127. package/dist/task/goal.js +3 -0
  128. package/dist/task/ids.js +1 -0
  129. package/dist/task/index.js +9 -0
  130. package/dist/task/lifecycle.js +1 -0
  131. package/dist/task/paths.js +1 -0
  132. package/dist/task/read-model.js +149 -0
  133. package/dist/task/runtime.js +699 -0
  134. package/dist/task/source-state.js +1 -0
  135. package/dist/task/state.js +55 -0
  136. package/dist/task/subagent-guidance.js +1 -0
  137. package/dist/task/workflow-state-types.js +92 -0
  138. package/dist/task/worktree-cleanup.js +140 -0
  139. package/dist/task/worktree.js +171 -0
  140. package/dist/workflows/dag/authoring.js +8 -0
  141. package/dist/workflows/dag/authority-surface.js +138 -0
  142. package/dist/workflows/dag/canvas-observer.js +474 -0
  143. package/dist/workflows/dag/decision-envelope.js +502 -0
  144. package/dist/workflows/dag/decision-evidence.js +153 -0
  145. package/dist/workflows/dag/decision-gates.js +1 -0
  146. package/dist/workflows/dag/executor-registry.js +25 -0
  147. package/dist/workflows/dag/facts.js +4 -0
  148. package/dist/workflows/dag/failure-category.js +111 -0
  149. package/dist/workflows/dag/final-verification.js +180 -0
  150. package/dist/workflows/dag/governance-constants.js +5 -0
  151. package/dist/workflows/dag/governance-profile.js +405 -0
  152. package/dist/workflows/dag/index.js +6 -0
  153. package/dist/workflows/dag/init-hybrid.js +855 -0
  154. package/dist/workflows/dag/knowledge-curator.js +162 -0
  155. package/dist/workflows/dag/lifecycle.js +484 -0
  156. package/dist/workflows/dag/prompt-source.js +88 -0
  157. package/dist/workflows/dag/prompt.js +130 -0
  158. package/dist/workflows/dag/reconcile-tasks.js +404 -0
  159. package/dist/workflows/dag/recovery-recommendation.js +226 -0
  160. package/dist/workflows/dag/repair-artifact.js +136 -0
  161. package/dist/workflows/dag/report.js +1019 -0
  162. package/dist/workflows/dag/runner.js +1677 -0
  163. package/dist/workflows/dag/runtime.js +5 -0
  164. package/dist/workflows/dag/skill-instructions.js +471 -0
  165. package/dist/workflows/dag/skills.js +41 -0
  166. package/dist/workflows/dag/spec.js +3 -0
  167. package/dist/workflows/dag/topo.js +30 -0
  168. package/dist/workflows/dag/types.js +275 -0
  169. package/dist/workflows/dag/upstream-artifacts.js +95 -0
  170. package/dist/workflows/dag/validate.js +527 -0
  171. package/dist/workflows/dynamic/artifacts.js +65 -0
  172. package/dist/workflows/dynamic/compile.js +360 -0
  173. package/dist/workflows/dynamic/compileTypes.js +1 -0
  174. package/dist/workflows/dynamic/errors.js +5 -0
  175. package/dist/workflows/dynamic/index.js +7 -0
  176. package/dist/workflows/dynamic/profiles.js +156 -0
  177. package/dist/workflows/dynamic/spec.js +114 -0
  178. package/dist/workflows/dynamic/validate.js +275 -0
  179. package/dist/workflows/loop/actions.js +1334 -0
  180. package/dist/workflows/loop/benchmark.js +510 -0
  181. package/dist/workflows/loop/closeout.js +134 -0
  182. package/dist/workflows/loop/context.js +48 -0
  183. package/dist/workflows/loop/events.js +25 -0
  184. package/dist/workflows/loop/hash.js +32 -0
  185. package/dist/workflows/loop/index.js +8 -0
  186. package/dist/workflows/loop/paths.js +17 -0
  187. package/dist/workflows/loop/rounds.js +81 -0
  188. package/dist/workflows/loop/signals.js +55 -0
  189. package/dist/workflows/loop/state.js +116 -0
  190. package/dist/workflows/loop/templates.js +54 -0
  191. package/dist/workflows/loop/types.js +28 -0
  192. package/docs/README.md +62 -0
  193. package/docs/agent-dag-recovery-playbook.md +158 -0
  194. package/docs/agent-dag-runner.md +40 -0
  195. package/docs/cursor-executor-usage.md +25 -0
  196. package/docs/decisions/README.md +3 -0
  197. package/docs/design/README.md +36 -0
  198. package/docs/development-principles.md +71 -0
  199. package/docs/dynamic-workflow-dag-engine-roadmap.md +1749 -0
  200. package/docs/exec-plans/README.md +6 -0
  201. package/docs/exec-plans/active/README.md +5 -0
  202. package/docs/exec-plans/completed/README.md +5 -0
  203. package/docs/feature-workflow.md +184 -0
  204. package/docs/harness-methodology-debugging.md +153 -0
  205. package/docs/harness-methodology-tdd.md +130 -0
  206. package/docs/harness-methodology-verification.md +27 -0
  207. package/docs/loop-agent-harness.md +42 -0
  208. package/docs/progress/README.md +3 -0
  209. package/docs/reports/README.md +3 -0
  210. package/docs/templates/adr.md +60 -0
  211. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -0
  212. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -0
  213. package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -0
  214. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -0
  215. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -0
  216. package/docs/templates/agent-dag-report.schema.json +423 -0
  217. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -0
  218. package/docs/templates/agent-dag.base.json +195 -0
  219. package/docs/templates/agent-dag.final-verification.json +190 -0
  220. package/docs/templates/agent-dag.schema.json +316 -0
  221. package/docs/templates/agent-dag.supervised-implementation.json +500 -0
  222. package/docs/templates/exec-plan.md +64 -0
  223. package/docs/templates/feature-spec.md +53 -0
  224. package/docs/templates/hybrid-dag.json +193 -0
  225. package/docs/templates/progress-log.md +17 -0
  226. package/docs/templates/project-start-checklist.md +9 -0
  227. package/docs/templates/qa-report.md +42 -0
  228. package/docs/templates/sprint-contract.md +29 -0
  229. package/docs/verification-matrix.md +30 -0
  230. package/examples/decision-gate-agent-dag.json +123 -0
  231. package/examples/example-dag.json +51 -0
  232. package/examples/hybrid-loop-agent-dag.json +194 -0
  233. package/harness.json +92 -0
  234. package/package.json +61 -0
  235. package/skills/ai-engineering-context/SKILL.md +48 -0
  236. package/skills/loop-agent/SKILL.md +260 -0
  237. package/skills/loop-agent/references/README.md +63 -0
  238. package/skills/loop-agent/references/command-reference.md +315 -0
  239. package/skills/loop-agent/references/harness-policy.md +258 -0
  240. package/skills/loop-agent/references/hybrid-dag.md +216 -0
  241. package/skills/loop-agent/references/learned/README.md +21 -0
  242. package/skills/loop-agent/references/model-routing.md +36 -0
  243. package/skills/loop-agent/references/multi-worktree.md +54 -0
  244. package/skills/loop-agent/references/one-shot-runs.md +85 -0
  245. package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -0
  246. package/skills/loop-agent/references/pi-prompt.md +23 -0
  247. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +83 -0
  248. package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -0
  249. package/skills/loop-agent/references/task-workflow.md +84 -0
  250. package/skills/loop-agent/references/verification-and-failure-handling.md +74 -0
  251. package/skills/requesting-code-review/SKILL.md +101 -0
  252. package/skills/requesting-code-review/code-reviewer.md +168 -0
  253. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  254. package/skills/systematic-debugging/SKILL.md +296 -0
  255. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  256. package/skills/systematic-debugging/condition-based-waiting.md +115 -0
  257. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  258. package/skills/systematic-debugging/find-polluter.sh +63 -0
  259. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  260. package/skills/systematic-debugging/test-academic.md +14 -0
  261. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  262. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  263. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  264. package/skills/verification-before-completion/SKILL.md +154 -0
@@ -0,0 +1,241 @@
1
+ import { appendLoopSignal, appendLoopRound, generateLoopCloseout, initializeLoop, loadLoopState, parseLoopSignalType, rewriteLoopContext, runLoopAuto, runLoopCursorFix, runLoopDagAction, runLoopPiReview, runLoopShellVerification, runLoopWorkflowAction, } from "../workflows/loop/index.js";
2
+ function usage() {
3
+ return [
4
+ "usage: loop <init|status|run|record-round|add-signal|closeout> <task-id> [options]",
5
+ " loop init <task-id> [--recover] [--max-rounds N]",
6
+ " loop status <task-id> [--json]",
7
+ " loop run <task-id> --action shell-verify [--command <cmd>]... [--timeout-ms N] [--cwd <repo-relative-dir>]",
8
+ " loop run <task-id> --action pi-review [--provider <id>] [--model <id>] [--timeout-ms N] [--cwd <repo-relative-dir>]",
9
+ " loop run <task-id> --action cursor-fix [--model <id>] [--timeout-ms N] [--cwd <repo-relative-dir>]",
10
+ " loop run <task-id> --action dag [--execute] [--dag <path>] [--run-id <id>] [--max-concurrent N] [--cwd <repo-relative-dir>] [--no-cursor]",
11
+ " loop run <task-id> --action workflow [--profile pr-review|--workflow <path>] [--execute] [--run-id <id>] [--changed-file <path>]... [--cwd <repo-relative-dir>]",
12
+ " loop run <task-id> --auto [--max-rounds N] [--allow-cursor-fix]",
13
+ " loop record-round <task-id> --action <name> --result <summary> --lesson <summary> --next <summary> --decision <continue|pause|blocked|complete> [--verification <summary>] [--failure-category <category>] [--ref <path>]...",
14
+ " loop add-signal <task-id> --type <human_followup|approval|scope_changed|review_feedback> --message <text> [--urgent] [--ref <path>]...",
15
+ " loop closeout <task-id>",
16
+ ].join("\n");
17
+ }
18
+ function readFlag(args, flag) {
19
+ const index = args.indexOf(flag);
20
+ if (index < 0)
21
+ return undefined;
22
+ const value = args[index + 1];
23
+ if (!value)
24
+ throw new Error(`${flag} requires a value`);
25
+ return value;
26
+ }
27
+ function readRepeatedFlag(args, flag) {
28
+ const values = [];
29
+ for (let index = 0; index < args.length; index += 1) {
30
+ if (args[index] === flag) {
31
+ const value = args[index + 1];
32
+ if (!value)
33
+ throw new Error(`${flag} requires a value`);
34
+ values.push(value);
35
+ index += 1;
36
+ }
37
+ }
38
+ return values;
39
+ }
40
+ function requireFlag(args, flag) {
41
+ const value = readFlag(args, flag);
42
+ if (!value)
43
+ throw new Error(`${flag} is required\n${usage()}`);
44
+ return value;
45
+ }
46
+ function parseDecision(value) {
47
+ if (value === "continue" ||
48
+ value === "pause" ||
49
+ value === "blocked" ||
50
+ value === "complete") {
51
+ return value;
52
+ }
53
+ throw new Error(`invalid loop decision: ${value}`);
54
+ }
55
+ function parseRecordRoundArgs(args) {
56
+ return {
57
+ action: requireFlag(args, "--action"),
58
+ refs: readRepeatedFlag(args, "--ref"),
59
+ result: requireFlag(args, "--result"),
60
+ verification: readFlag(args, "--verification"),
61
+ lesson: requireFlag(args, "--lesson"),
62
+ next: requireFlag(args, "--next"),
63
+ decision: parseDecision(requireFlag(args, "--decision")),
64
+ failureCategory: readFlag(args, "--failure-category"),
65
+ completedCriteria: readRepeatedFlag(args, "--completed-criterion"),
66
+ };
67
+ }
68
+ export async function runLoop(repoRoot, args) {
69
+ const [subcommand, taskId, ...rest] = args;
70
+ if (!subcommand || !taskId)
71
+ throw new Error(usage());
72
+ if (subcommand === "init") {
73
+ const maxRoundsRaw = readFlag(rest, "--max-rounds");
74
+ let maxRounds;
75
+ if (maxRoundsRaw) {
76
+ const parsed = Number.parseInt(maxRoundsRaw, 10);
77
+ if (!Number.isInteger(parsed) || parsed <= 0) {
78
+ throw new Error("--max-rounds must be a positive integer");
79
+ }
80
+ maxRounds = parsed;
81
+ }
82
+ const result = await initializeLoop(repoRoot, taskId, {
83
+ recover: rest.includes("--recover"),
84
+ maxRounds,
85
+ });
86
+ console.log(JSON.stringify({
87
+ taskId,
88
+ created: result.created,
89
+ status: result.state.status,
90
+ round: result.state.round,
91
+ loopDir: result.paths.loopDir,
92
+ }, null, 2));
93
+ return;
94
+ }
95
+ if (subcommand === "status") {
96
+ const state = await loadLoopState(repoRoot, taskId);
97
+ if (rest.includes("--json")) {
98
+ console.log(JSON.stringify(state, null, 2));
99
+ return;
100
+ }
101
+ console.log([
102
+ `taskId: ${state.taskId}`,
103
+ `status: ${state.status}`,
104
+ `round: ${state.round}/${state.maxRounds}`,
105
+ `lastDecision: ${state.lastDecision}`,
106
+ `lastAction: ${state.lastAction}`,
107
+ `sourceStale: ${state.freshness.sourceStale}`,
108
+ `openFrontier: ${state.openFrontier.join("; ")}`,
109
+ ].join("\n"));
110
+ return;
111
+ }
112
+ if (subcommand === "record-round") {
113
+ const entry = await appendLoopRound(repoRoot, taskId, parseRecordRoundArgs(rest));
114
+ await rewriteLoopContext(repoRoot, taskId);
115
+ console.log(JSON.stringify(entry, null, 2));
116
+ return;
117
+ }
118
+ if (subcommand === "add-signal") {
119
+ const entry = await appendLoopSignal(repoRoot, taskId, {
120
+ type: parseLoopSignalType(requireFlag(rest, "--type")),
121
+ message: requireFlag(rest, "--message"),
122
+ urgent: rest.includes("--urgent"),
123
+ refs: readRepeatedFlag(rest, "--ref"),
124
+ });
125
+ console.log(JSON.stringify(entry, null, 2));
126
+ return;
127
+ }
128
+ if (subcommand === "closeout") {
129
+ const record = await generateLoopCloseout(repoRoot, taskId);
130
+ console.log(JSON.stringify(record, null, 2));
131
+ return;
132
+ }
133
+ if (subcommand === "run") {
134
+ if (rest.includes("--auto")) {
135
+ const maxRoundsRaw = readFlag(rest, "--max-rounds");
136
+ let maxRounds;
137
+ if (maxRoundsRaw) {
138
+ const parsed = Number.parseInt(maxRoundsRaw, 10);
139
+ if (!Number.isInteger(parsed) || parsed <= 0) {
140
+ throw new Error("--max-rounds must be a positive integer");
141
+ }
142
+ maxRounds = parsed;
143
+ }
144
+ const record = await runLoopAuto(repoRoot, taskId, {
145
+ maxRounds,
146
+ allowCursorFix: rest.includes("--allow-cursor-fix"),
147
+ });
148
+ console.log(JSON.stringify(record, null, 2));
149
+ return;
150
+ }
151
+ const action = readFlag(rest, "--action");
152
+ const timeoutRaw = readFlag(rest, "--timeout-ms");
153
+ let timeoutMs;
154
+ if (timeoutRaw) {
155
+ const parsed = Number.parseInt(timeoutRaw, 10);
156
+ if (!Number.isInteger(parsed) || parsed <= 0) {
157
+ throw new Error("--timeout-ms must be a positive integer");
158
+ }
159
+ timeoutMs = parsed;
160
+ }
161
+ if (action === "pi-review") {
162
+ const record = await runLoopPiReview(repoRoot, taskId, {
163
+ timeoutMs,
164
+ cwd: readFlag(rest, "--cwd"),
165
+ provider: readFlag(rest, "--provider"),
166
+ model: readFlag(rest, "--model"),
167
+ });
168
+ console.log(JSON.stringify(record, null, 2));
169
+ return;
170
+ }
171
+ if (action === "cursor-fix") {
172
+ const record = await runLoopCursorFix(repoRoot, taskId, {
173
+ timeoutMs,
174
+ cwd: readFlag(rest, "--cwd"),
175
+ model: readFlag(rest, "--model"),
176
+ });
177
+ console.log(JSON.stringify(record, null, 2));
178
+ return;
179
+ }
180
+ if (action === "dag") {
181
+ const maxConcurrentRaw = readFlag(rest, "--max-concurrent");
182
+ let maxConcurrent;
183
+ if (maxConcurrentRaw) {
184
+ const parsed = Number.parseInt(maxConcurrentRaw, 10);
185
+ if (!Number.isInteger(parsed) || parsed <= 0) {
186
+ throw new Error("--max-concurrent must be a positive integer");
187
+ }
188
+ maxConcurrent = parsed;
189
+ }
190
+ const record = await runLoopDagAction(repoRoot, taskId, {
191
+ mode: rest.includes("--execute") ? "execute" : "review",
192
+ cwd: readFlag(rest, "--cwd"),
193
+ dagPath: readFlag(rest, "--dag"),
194
+ runId: readFlag(rest, "--run-id"),
195
+ maxConcurrent,
196
+ noCursor: rest.includes("--no-cursor"),
197
+ });
198
+ console.log(JSON.stringify(record, null, 2));
199
+ return;
200
+ }
201
+ if (action === "workflow") {
202
+ const maxConcurrentRaw = readFlag(rest, "--max-concurrent");
203
+ let maxConcurrent;
204
+ if (maxConcurrentRaw) {
205
+ const parsed = Number.parseInt(maxConcurrentRaw, 10);
206
+ if (!Number.isInteger(parsed) || parsed <= 0) {
207
+ throw new Error("--max-concurrent must be a positive integer");
208
+ }
209
+ maxConcurrent = parsed;
210
+ }
211
+ const profile = readFlag(rest, "--profile");
212
+ if (profile && profile !== "pr-review") {
213
+ throw new Error(`unsupported workflow profile: ${profile}`);
214
+ }
215
+ const record = await runLoopWorkflowAction(repoRoot, taskId, {
216
+ mode: rest.includes("--execute") ? "execute" : "review",
217
+ cwd: readFlag(rest, "--cwd"),
218
+ profile: profile,
219
+ workflowPath: readFlag(rest, "--workflow"),
220
+ runId: readFlag(rest, "--run-id"),
221
+ maxConcurrent,
222
+ base: readFlag(rest, "--base"),
223
+ head: readFlag(rest, "--head"),
224
+ changedFiles: readRepeatedFlag(rest, "--changed-file"),
225
+ });
226
+ console.log(JSON.stringify(record, null, 2));
227
+ return;
228
+ }
229
+ if (action !== "shell-verify") {
230
+ throw new Error("loop run currently supports --action shell-verify|pi-review|cursor-fix|dag|workflow");
231
+ }
232
+ const record = await runLoopShellVerification(repoRoot, taskId, {
233
+ commands: readRepeatedFlag(rest, "--command"),
234
+ timeoutMs,
235
+ cwd: readFlag(rest, "--cwd"),
236
+ });
237
+ console.log(JSON.stringify(record, null, 2));
238
+ return;
239
+ }
240
+ throw new Error(usage());
241
+ }
@@ -0,0 +1,5 @@
1
+ import { createTask } from '../task/runtime.js';
2
+ export async function runNewTask(repoRoot, taskId, title) {
3
+ const taskDir = await createTask(repoRoot, taskId, title);
4
+ console.log(taskDir);
5
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Pi Prompt — one-shot CLI for Pi SDK short tasks.
3
+ *
4
+ * Usage:
5
+ * pi-prompt "Fix lint errors"
6
+ * pi-prompt --stdin
7
+ * pi-prompt --file task.md
8
+ * pi-prompt --model gpt-5.5 --provider wizard-local "Deep analysis"
9
+ * pi-prompt --cwd /path --timeout 120000 "task"
10
+ */
11
+ import { DEFAULT_TIMEOUT_MS } from "../executors/pi-executor.js";
12
+ import { createOneShotPromptParseAccum, formatUnknownFlagError, parseOneShotCommonFlag, printOneShotHelpAndExit, resolveOneShotCwd, resolveOneShotTask, } from "../shared/one-shot-prompt-args.js";
13
+ import { executeSingleSdkAttempt } from "../executors/pi-sdk-executor.js";
14
+ export const DEFAULT_PI_PROVIDER = "wizard-local";
15
+ export const DEFAULT_PI_MODEL = "glm-5.2";
16
+ export const DEFAULT_PI_TOOLS = [
17
+ "read",
18
+ "bash",
19
+ "ast_grep_search",
20
+ "lsp_diagnostics",
21
+ "subagent",
22
+ ];
23
+ const PI_PROMPT_USAGE = [
24
+ "usage: pi-prompt [options] [task]",
25
+ "",
26
+ ' pi-prompt "Fix lint errors in src/" One-shot task',
27
+ " pi-prompt --stdin Read task from stdin",
28
+ " pi-prompt --file task.md Read task from a file",
29
+ ' pi-prompt --model gpt-5.5 "Deep analysis" Use a high model',
30
+ ' pi-prompt --cwd /path --timeout 60000 "task" Custom cwd + timeout',
31
+ "",
32
+ "options:",
33
+ " --stdin Read task from stdin (ignores positional task)",
34
+ " --file <path> Read task from a file",
35
+ " --cwd <path> Working directory (default: cwd)",
36
+ ` --provider <id> Provider id (default: ${DEFAULT_PI_PROVIDER})`,
37
+ ` --model <id> Model id (default: ${DEFAULT_PI_MODEL})`,
38
+ " --thinking <level> Thinking level (provider-specific)",
39
+ ` --timeout <ms> Timeout in ms (default: ${DEFAULT_TIMEOUT_MS})`,
40
+ ` --tools <csv> Tool names (default: ${DEFAULT_PI_TOOLS.join(",")})`,
41
+ " --system <text> Base system prompt",
42
+ " --append-system-prompt <text> Append to system prompt",
43
+ " --help, -h Show this help",
44
+ ].join("\n");
45
+ function parseToolsCsv(value) {
46
+ return value
47
+ .split(",")
48
+ .map((tool) => tool.trim())
49
+ .filter((tool) => tool.length > 0);
50
+ }
51
+ function buildSystemPrompt(args) {
52
+ const parts = [args.system, args.appendSystemPrompt].filter((part) => Boolean(part?.trim()));
53
+ if (parts.length === 0) {
54
+ return "You are running as a one-shot Pi SDK prompt from loop-agent pi-prompt. Keep the response concise and task-focused.";
55
+ }
56
+ return parts.join("\n\n");
57
+ }
58
+ export function parsePiPromptArgs(args) {
59
+ const accum = createOneShotPromptParseAccum();
60
+ let provider;
61
+ let model;
62
+ let thinking;
63
+ let tools;
64
+ let system;
65
+ let appendSystemPrompt;
66
+ for (let i = 0; i < args.length; i += 1) {
67
+ const common = parseOneShotCommonFlag(args, i, accum);
68
+ if (common.kind === "help") {
69
+ printOneShotHelpAndExit(PI_PROMPT_USAGE);
70
+ }
71
+ if (common.kind === "consumed") {
72
+ i = common.nextIndex;
73
+ continue;
74
+ }
75
+ const arg = args[i];
76
+ if (arg === "--provider") {
77
+ provider = args[i + 1];
78
+ if (!provider)
79
+ throw new Error("--provider requires a provider id");
80
+ i += 1;
81
+ }
82
+ else if (arg === "--model") {
83
+ model = args[i + 1];
84
+ if (!model)
85
+ throw new Error("--model requires a model id");
86
+ i += 1;
87
+ }
88
+ else if (arg === "--thinking") {
89
+ thinking = args[i + 1];
90
+ if (!thinking)
91
+ throw new Error("--thinking requires a level");
92
+ i += 1;
93
+ }
94
+ else if (arg === "--tools") {
95
+ const val = args[i + 1];
96
+ if (!val)
97
+ throw new Error("--tools requires a comma-separated list");
98
+ tools = parseToolsCsv(val);
99
+ if (tools.length === 0) {
100
+ throw new Error("--tools requires at least one tool name");
101
+ }
102
+ i += 1;
103
+ }
104
+ else if (arg === "--system") {
105
+ system = args[i + 1];
106
+ if (!system)
107
+ throw new Error("--system requires text");
108
+ i += 1;
109
+ }
110
+ else if (arg === "--append-system-prompt") {
111
+ appendSystemPrompt = args[i + 1];
112
+ if (!appendSystemPrompt) {
113
+ throw new Error("--append-system-prompt requires text");
114
+ }
115
+ i += 1;
116
+ }
117
+ else {
118
+ throw formatUnknownFlagError(arg, PI_PROMPT_USAGE);
119
+ }
120
+ }
121
+ const task = resolveOneShotTask(accum, PI_PROMPT_USAGE);
122
+ return {
123
+ task,
124
+ cwd: resolveOneShotCwd(accum.cwd),
125
+ provider,
126
+ model,
127
+ thinking,
128
+ timeoutMs: accum.timeoutMs,
129
+ tools,
130
+ system,
131
+ appendSystemPrompt,
132
+ };
133
+ }
134
+ export async function runPiPrompt(args) {
135
+ const effectiveCwd = args.cwd ?? process.cwd();
136
+ const provider = args.provider ?? DEFAULT_PI_PROVIDER;
137
+ const model = args.model ?? DEFAULT_PI_MODEL;
138
+ const timeoutMs = args.timeoutMs ?? DEFAULT_TIMEOUT_MS;
139
+ const toolNames = args.tools ?? [...DEFAULT_PI_TOOLS];
140
+ const prompt = buildSystemPrompt(args);
141
+ const startedAt = Date.now();
142
+ process.stderr.write(`[pi-prompt] running "${args.task.slice(0, 80)}${args.task.length > 80 ? "..." : ""}" (provider=${provider}, model=${model}, cwd=${effectiveCwd})\n`);
143
+ const result = await executeSingleSdkAttempt({
144
+ repoRoot: effectiveCwd,
145
+ prompt,
146
+ userMessage: args.task,
147
+ attachedFiles: [],
148
+ toolNames,
149
+ timeoutMs,
150
+ step: "implement",
151
+ modelConfig: {
152
+ provider,
153
+ model,
154
+ thinking: args.thinking,
155
+ },
156
+ });
157
+ const elapsed = ((Date.now() - startedAt) / 1000).toFixed(1);
158
+ if (result.ok) {
159
+ process.stderr.write(`[pi-prompt] done in ${elapsed}s\n`);
160
+ process.stdout.write(result.assistantText);
161
+ if (!result.assistantText.endsWith("\n"))
162
+ process.stdout.write("\n");
163
+ return;
164
+ }
165
+ const detail = result.stderr?.trim()
166
+ ? result.stderr.trim()
167
+ : excerptOutput(result.stdout);
168
+ process.stderr.write(`[pi-prompt] FAILED in ${elapsed}s (${result.failureCategory}):\n${detail}\n`);
169
+ process.exit(1);
170
+ }
171
+ function excerptOutput(stdout, maxChars = 2000) {
172
+ const trimmed = stdout.trim();
173
+ if (!trimmed)
174
+ return "(no output)";
175
+ if (trimmed.length <= maxChars)
176
+ return trimmed;
177
+ return `${trimmed.slice(0, maxChars)}…`;
178
+ }
179
+ export function printPiPromptUsage() {
180
+ console.log(PI_PROMPT_USAGE);
181
+ }
@@ -0,0 +1,153 @@
1
+ import path from "node:path";
2
+ import { buildPiReuseBenchmarkResult, formatPiReuseBenchmarkMarkdown, } from "../executors/pi-reuse-benchmark.js";
3
+ export function parsePiReuseBenchmarkArgs(args) {
4
+ let reportPath;
5
+ let approvalPath;
6
+ let offExecutorPath;
7
+ let onExecutorPath;
8
+ let offTaskId;
9
+ let onTaskId;
10
+ let json = false;
11
+ let markdown = false;
12
+ for (let i = 0; i < args.length; i += 1) {
13
+ const arg = args[i];
14
+ if (arg === "--json") {
15
+ json = true;
16
+ continue;
17
+ }
18
+ if (arg === "--markdown") {
19
+ markdown = true;
20
+ continue;
21
+ }
22
+ if (arg === "--report") {
23
+ reportPath = args[++i];
24
+ if (!reportPath) {
25
+ throw new Error("pi-reuse-benchmark --report requires a path");
26
+ }
27
+ continue;
28
+ }
29
+ if (arg.startsWith("--report=")) {
30
+ reportPath = arg.slice("--report=".length);
31
+ continue;
32
+ }
33
+ if (arg === "--approval") {
34
+ approvalPath = args[++i];
35
+ if (!approvalPath) {
36
+ throw new Error("pi-reuse-benchmark --approval requires a path");
37
+ }
38
+ continue;
39
+ }
40
+ if (arg.startsWith("--approval=")) {
41
+ approvalPath = arg.slice("--approval=".length);
42
+ continue;
43
+ }
44
+ if (arg === "--off-executor") {
45
+ offExecutorPath = args[++i];
46
+ if (!offExecutorPath) {
47
+ throw new Error("pi-reuse-benchmark --off-executor requires a path");
48
+ }
49
+ continue;
50
+ }
51
+ if (arg.startsWith("--off-executor=")) {
52
+ offExecutorPath = arg.slice("--off-executor=".length);
53
+ continue;
54
+ }
55
+ if (arg === "--on-executor") {
56
+ onExecutorPath = args[++i];
57
+ if (!onExecutorPath) {
58
+ throw new Error("pi-reuse-benchmark --on-executor requires a path");
59
+ }
60
+ continue;
61
+ }
62
+ if (arg.startsWith("--on-executor=")) {
63
+ onExecutorPath = arg.slice("--on-executor=".length);
64
+ continue;
65
+ }
66
+ if (arg === "--off-task") {
67
+ offTaskId = args[++i];
68
+ if (!offTaskId) {
69
+ throw new Error("pi-reuse-benchmark --off-task requires a task id");
70
+ }
71
+ continue;
72
+ }
73
+ if (arg.startsWith("--off-task=")) {
74
+ offTaskId = arg.slice("--off-task=".length);
75
+ continue;
76
+ }
77
+ if (arg === "--on-task") {
78
+ onTaskId = args[++i];
79
+ if (!onTaskId) {
80
+ throw new Error("pi-reuse-benchmark --on-task requires a task id");
81
+ }
82
+ continue;
83
+ }
84
+ if (arg.startsWith("--on-task=")) {
85
+ onTaskId = arg.slice("--on-task=".length);
86
+ continue;
87
+ }
88
+ if (arg === "--help" || arg === "-h") {
89
+ printPiReuseBenchmarkUsage();
90
+ process.exit(0);
91
+ }
92
+ throw new Error(`unknown pi-reuse-benchmark argument: ${arg}`);
93
+ }
94
+ if (!json && !markdown) {
95
+ json = true;
96
+ }
97
+ return {
98
+ reportPath,
99
+ approvalPath,
100
+ offExecutorPath,
101
+ onExecutorPath,
102
+ offTaskId,
103
+ onTaskId,
104
+ json,
105
+ markdown,
106
+ };
107
+ }
108
+ export function printPiReuseBenchmarkUsage() {
109
+ console.log(`usage: pi-reuse-benchmark [options]
110
+
111
+ Deterministic Pi runtime reuse benchmark/decision summary (no live Pi calls).
112
+
113
+ Options:
114
+ --report <path> Benchmark report markdown (approval status)
115
+ --approval <path> Explicit approval JSON artifact
116
+ --off-executor <path> Baseline executor.jsonl (reuse off)
117
+ --on-executor <path> Treatment executor.jsonl (reuse on)
118
+ --off-task <task-id> Resolve baseline from .harness/tasks/<id>/logs/executor.jsonl
119
+ --on-task <task-id> Resolve treatment from .harness/tasks/<id>/logs/executor.jsonl
120
+ --json Emit JSON (default when no format flag is set)
121
+ --markdown Emit Markdown summary
122
+ -h, --help Show this help
123
+
124
+ Recommendations: defer | maintain-opt-in | eligible-for-human-review
125
+ Never changes CODE_AGENT_PI_REUSE_RUNTIME default (off).`);
126
+ }
127
+ function resolveRepoRelative(repoRoot, filePath) {
128
+ if (!filePath)
129
+ return undefined;
130
+ return path.isAbsolute(filePath) ? filePath : path.resolve(repoRoot, filePath);
131
+ }
132
+ export async function runPiReuseBenchmark(repoRoot, rawArgs) {
133
+ const parsed = parsePiReuseBenchmarkArgs(rawArgs);
134
+ const input = {
135
+ repoRoot,
136
+ reportPath: resolveRepoRelative(repoRoot, parsed.reportPath),
137
+ approvalPath: resolveRepoRelative(repoRoot, parsed.approvalPath),
138
+ offExecutorPath: resolveRepoRelative(repoRoot, parsed.offExecutorPath),
139
+ onExecutorPath: resolveRepoRelative(repoRoot, parsed.onExecutorPath),
140
+ offTaskId: parsed.offTaskId,
141
+ onTaskId: parsed.onTaskId,
142
+ };
143
+ const result = await buildPiReuseBenchmarkResult(input);
144
+ if (parsed.json) {
145
+ console.log(JSON.stringify(result, null, 2));
146
+ }
147
+ if (parsed.markdown) {
148
+ if (parsed.json) {
149
+ console.log("");
150
+ }
151
+ process.stdout.write(formatPiReuseBenchmarkMarkdown(result));
152
+ }
153
+ }
@@ -0,0 +1,5 @@
1
+ import { listActivePlans } from '../governance/checks.js';
2
+ export async function runPlanList(repoRoot) {
3
+ const plans = await listActivePlans(repoRoot);
4
+ console.log(JSON.stringify({ plans }, null, 2));
5
+ }
@@ -0,0 +1,29 @@
1
+ import { promoteRunEvidence } from '../records/promotion.js';
2
+ export function parsePromoteRunArgs(args) {
3
+ const [taskId, ...rest] = args;
4
+ if (!taskId)
5
+ throw new Error('usage: promote-run <task-id> --run-id <run-id>');
6
+ let runId;
7
+ for (let i = 0; i < rest.length; i += 1) {
8
+ const arg = rest[i];
9
+ if (arg === '--run-id') {
10
+ runId = rest[++i];
11
+ if (!runId)
12
+ throw new Error('promote-run --run-id requires a value');
13
+ continue;
14
+ }
15
+ if (arg.startsWith('--run-id=')) {
16
+ runId = arg.slice('--run-id='.length);
17
+ continue;
18
+ }
19
+ throw new Error(`unknown promote-run flag: ${arg}`);
20
+ }
21
+ if (!runId)
22
+ throw new Error('usage: promote-run <task-id> --run-id <run-id>');
23
+ return { taskId, runId };
24
+ }
25
+ export async function runPromoteRun(repoRoot, args) {
26
+ const parsed = parsePromoteRunArgs(args);
27
+ const result = await promoteRunEvidence(repoRoot, parsed.taskId, parsed.runId);
28
+ console.log(JSON.stringify(result, null, 2));
29
+ }
@@ -0,0 +1,16 @@
1
+ /** Feature-study reference index command (compatibility-only). */
2
+ import { buildReferenceIndex } from '../shared/reference-context.js';
3
+ import { loadTaskConfig } from '../task/runtime.js';
4
+ export async function runReferenceIndex(repoRoot, taskId) {
5
+ const taskConfig = await loadTaskConfig(repoRoot, taskId);
6
+ const index = await buildReferenceIndex(repoRoot, taskId, taskConfig);
7
+ const fileCount = index.repos.reduce((sum, repo) => sum + repo.files.length, 0);
8
+ console.log(JSON.stringify({
9
+ taskId,
10
+ manifestPath: `${repoRoot}/.harness/tasks/${taskId}/source/references/index.json`,
11
+ repoCount: index.repos.length,
12
+ indexedFileCount: fileCount,
13
+ docCount: index.docs.length,
14
+ truncatedRepos: index.repos.filter((repo) => repo.truncated).map((repo) => repo.name),
15
+ }, null, 2));
16
+ }