@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,92 @@
1
+ /**
2
+ * Legacy task goal command surface (compatibility-only / deprecated-candidate).
3
+ * Public CLI retained; prefer Agent DAG for new autonomous implementation work.
4
+ */
5
+ import { clearTaskGoal, getTaskGoal, setTaskGoal, updateTaskGoalStatus, } from '../task/runtime.js';
6
+ import { loadHarnessManifest } from '../governance/harness.js';
7
+ import { assertGoalsFeatureEnabled } from '../task/goal-policy.js';
8
+ function parseTokenBudget(args) {
9
+ const index = args.indexOf('--token-budget');
10
+ if (index < 0)
11
+ return { rest: args };
12
+ const next = args[index + 1];
13
+ if (next === undefined) {
14
+ throw new Error('usage: goal set <taskId> <objective> [--token-budget <number>]');
15
+ }
16
+ if (next.toLowerCase() === 'null') {
17
+ return {
18
+ tokenBudget: null,
19
+ rest: args.filter((_arg, i) => i !== index && i !== index + 1),
20
+ };
21
+ }
22
+ const value = Number.parseInt(next, 10);
23
+ if (!Number.isFinite(value) || value <= 0) {
24
+ throw new Error('token budget must be a positive integer or "null"');
25
+ }
26
+ return {
27
+ tokenBudget: value,
28
+ rest: args.filter((_arg, i) => i !== index && i !== index + 1),
29
+ };
30
+ }
31
+ export async function runGoal(repoRoot, command, args) {
32
+ const manifest = await loadHarnessManifest(repoRoot);
33
+ assertGoalsFeatureEnabled(manifest);
34
+ if (!command) {
35
+ throw new Error('usage: goal <set|get|pause|resume|clear> ...');
36
+ }
37
+ if (command === 'set') {
38
+ const parsed = parseTokenBudget(args);
39
+ const forceReplace = parsed.rest.includes('--force');
40
+ const filtered = parsed.rest.filter((arg) => arg !== '--force');
41
+ const [taskId, ...objectiveParts] = filtered;
42
+ if (!taskId || objectiveParts.length === 0) {
43
+ throw new Error('usage: goal set <taskId> <objective> [--token-budget <number>] [--force]');
44
+ }
45
+ const objective = objectiveParts.join(' ').trim();
46
+ const existing = await getTaskGoal(repoRoot, taskId);
47
+ if (existing.objective
48
+ && existing.objective !== objective
49
+ && !forceReplace) {
50
+ throw new Error('goal already exists with a different objective; rerun with --force to replace existing goal objective');
51
+ }
52
+ const goal = await setTaskGoal(repoRoot, taskId, objective, {
53
+ status: 'active',
54
+ tokenBudget: parsed.tokenBudget,
55
+ });
56
+ console.log(JSON.stringify({ taskId, goal }, null, 2));
57
+ return;
58
+ }
59
+ if (command === 'get') {
60
+ const [taskId] = args;
61
+ if (!taskId)
62
+ throw new Error('usage: goal get <taskId>');
63
+ const goal = await getTaskGoal(repoRoot, taskId);
64
+ console.log(JSON.stringify({ taskId, goal }, null, 2));
65
+ return;
66
+ }
67
+ if (command === 'pause') {
68
+ const [taskId] = args;
69
+ if (!taskId)
70
+ throw new Error('usage: goal pause <taskId>');
71
+ const goal = await updateTaskGoalStatus(repoRoot, taskId, 'paused');
72
+ console.log(JSON.stringify({ taskId, goal }, null, 2));
73
+ return;
74
+ }
75
+ if (command === 'resume') {
76
+ const [taskId] = args;
77
+ if (!taskId)
78
+ throw new Error('usage: goal resume <taskId>');
79
+ const goal = await updateTaskGoalStatus(repoRoot, taskId, 'active');
80
+ console.log(JSON.stringify({ taskId, goal }, null, 2));
81
+ return;
82
+ }
83
+ if (command === 'clear') {
84
+ const [taskId] = args;
85
+ if (!taskId)
86
+ throw new Error('usage: goal clear <taskId>');
87
+ await clearTaskGoal(repoRoot, taskId);
88
+ console.log(JSON.stringify({ taskId, cleared: true }, null, 2));
89
+ return;
90
+ }
91
+ throw new Error('usage: goal <set|get|pause|resume|clear> ...');
92
+ }
@@ -0,0 +1,5 @@
1
+ import { checkTaskHandoff } from '../governance/checks.js';
2
+ export async function runHandoffCheck(repoRoot, taskId) {
3
+ const result = await checkTaskHandoff(repoRoot, taskId);
4
+ console.log(JSON.stringify(result, null, 2));
5
+ }
@@ -0,0 +1,44 @@
1
+ import { harvestTask } from '../records/harvest.js';
2
+ import { loadHarnessManifest } from '../governance/harness.js';
3
+ export function parseHarvestArgs(args) {
4
+ if (args.length === 0) {
5
+ throw new Error('usage: harvest <taskId> [--squash] [--no-archive] [--keep-worktree]');
6
+ }
7
+ let taskId;
8
+ let squash = false;
9
+ let archive = true;
10
+ let keepWorktree = false;
11
+ for (const arg of args) {
12
+ if (arg === '--squash') {
13
+ squash = true;
14
+ }
15
+ else if (arg === '--no-archive') {
16
+ archive = false;
17
+ }
18
+ else if (arg === '--keep-worktree') {
19
+ keepWorktree = true;
20
+ }
21
+ else if (!arg.startsWith('--')) {
22
+ if (taskId)
23
+ throw new Error(`unexpected positional argument: ${arg}`);
24
+ taskId = arg;
25
+ }
26
+ else {
27
+ throw new Error(`unknown harvest flag: ${arg}`);
28
+ }
29
+ }
30
+ if (!taskId) {
31
+ throw new Error('harvest requires <taskId> as the first positional argument');
32
+ }
33
+ return { taskId, squash, archive, keepWorktree };
34
+ }
35
+ export async function runHarvest(repoRoot, rawArgs) {
36
+ const parsed = parseHarvestArgs(rawArgs);
37
+ const manifest = await loadHarnessManifest(repoRoot);
38
+ const result = await harvestTask(repoRoot, manifest, parsed.taskId, {
39
+ squash: parsed.squash,
40
+ archive: parsed.archive,
41
+ keepWorktree: parsed.keepWorktree,
42
+ });
43
+ console.log(JSON.stringify(result, null, 2));
44
+ }
@@ -0,0 +1,11 @@
1
+ export async function runInspect(adapter, repoRoot) {
2
+ const manifest = await adapter.loadHarnessManifest(repoRoot);
3
+ console.log(JSON.stringify({
4
+ project: manifest.project,
5
+ governanceRoot: manifest.governanceRoot,
6
+ adapter: adapter.name,
7
+ entrypoints: manifest.entrypoints,
8
+ artifacts: manifest.artifacts,
9
+ scripts: manifest.scripts,
10
+ }, null, 2));
11
+ }
@@ -0,0 +1,195 @@
1
+ import { access, readdir } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { getArtifactPath, getArtifactRelativePath, } from '../shared/artifacts-core.js';
4
+ import { defaultHybridDagOutputPath } from '../workflows/dag/init-hybrid.js';
5
+ import { getTaskDir, getTaskPaths, loadTaskConfig } from '../task/runtime.js';
6
+ const STAGES = new Set([
7
+ 'source',
8
+ 'dag-draft',
9
+ 'task-artifacts',
10
+ 'promotion',
11
+ 'closeout',
12
+ ]);
13
+ export function parseInstructionsArgs(args) {
14
+ const [stageRaw, ...rest] = args;
15
+ if (!stageRaw || !STAGES.has(stageRaw)) {
16
+ throw new Error('usage: instructions <source|dag-draft|task-artifacts|promotion|closeout> --task <task-id> [--json]');
17
+ }
18
+ let taskId;
19
+ let json = false;
20
+ for (let i = 0; i < rest.length; i += 1) {
21
+ const arg = rest[i];
22
+ if (arg === '--task') {
23
+ taskId = rest[++i];
24
+ if (!taskId)
25
+ throw new Error('instructions --task requires a task id');
26
+ continue;
27
+ }
28
+ if (arg.startsWith('--task=')) {
29
+ taskId = arg.slice('--task='.length);
30
+ continue;
31
+ }
32
+ if (arg === '--json') {
33
+ json = true;
34
+ continue;
35
+ }
36
+ throw new Error(`unknown instructions flag: ${arg}`);
37
+ }
38
+ if (!taskId)
39
+ throw new Error('instructions requires --task <task-id>');
40
+ return { stage: stageRaw, taskId, json };
41
+ }
42
+ export async function runInstructions(repoRoot, args) {
43
+ const parsed = parseInstructionsArgs(args);
44
+ const doc = await buildInstructions(repoRoot, parsed.stage, parsed.taskId);
45
+ if (!parsed.json) {
46
+ console.log(formatInstructionsMarkdown(doc));
47
+ return;
48
+ }
49
+ console.log(JSON.stringify(doc, null, 2));
50
+ }
51
+ export async function buildInstructions(repoRoot, stage, taskId) {
52
+ const taskDir = getTaskDir(repoRoot, taskId);
53
+ const paths = getTaskPaths(repoRoot, taskId);
54
+ const taskConfig = await loadTaskConfig(repoRoot, taskId);
55
+ const allowedEditRoots = taskConfig.allowedPaths.length > 0
56
+ ? taskConfig.allowedPaths
57
+ : [path.relative(repoRoot, taskDir).replace(/\\/g, '/')];
58
+ const writePolicy = {
59
+ mode: stage === 'promotion'
60
+ ? 'run-evidence-read-only'
61
+ : stage === 'closeout'
62
+ ? 'docs-progress'
63
+ : 'task-owned',
64
+ allowedEditRoots,
65
+ forbiddenPaths: taskConfig.forbiddenPaths,
66
+ };
67
+ const base = {
68
+ artifactId: stage,
69
+ taskId,
70
+ existingFiles: await listExistingFiles(taskDir),
71
+ writePolicy,
72
+ };
73
+ if (stage === 'source') {
74
+ return refreshInstructionBlockers(withMissingDependencies({
75
+ ...base,
76
+ outputPath: paths.sourceDir,
77
+ dependencies: [paths.taskConfigPath],
78
+ template: { requiredHeadings: ['# 需求', '# 执行约束'] },
79
+ completionCriteria: [
80
+ 'source/需求.md exists with concrete requirements',
81
+ 'source/执行约束.md exists when path or behavioral constraints matter',
82
+ ],
83
+ }));
84
+ }
85
+ if (stage === 'dag-draft') {
86
+ return refreshInstructionBlockers(withMissingDependencies({
87
+ ...base,
88
+ outputPath: defaultHybridDagOutputPath(taskId),
89
+ dependencies: [
90
+ paths.taskConfigPath,
91
+ path.join(paths.sourceDir, '需求.md'),
92
+ ],
93
+ template: { requiredHeadings: ['version', 'title', 'objective', 'tasks', 'executorModels'] },
94
+ completionCriteria: [
95
+ 'dag draft validates with dag validate --strict-models --strict-governance',
96
+ 'writeSet is narrow and matches task constraints',
97
+ ],
98
+ }));
99
+ }
100
+ if (stage === 'promotion') {
101
+ return refreshInstructionBlockers(withMissingDependencies({
102
+ ...base,
103
+ outputPath: path.join(taskDir, 'artifacts'),
104
+ dependencies: [
105
+ path.join(repoRoot, '.harness', 'dag-runs', 'completed'),
106
+ path.join(repoRoot, '.harness', 'runs', 'completed'),
107
+ ],
108
+ template: { requiredHeadings: ['# 修改记录', '# 验证结果'] },
109
+ completionCriteria: [
110
+ 'completed run facts remain read-only',
111
+ 'task artifacts summarize real run evidence without inventing verification',
112
+ ],
113
+ }));
114
+ }
115
+ if (stage === 'closeout') {
116
+ return refreshInstructionBlockers(withMissingDependencies({
117
+ ...base,
118
+ outputPath: path.join(repoRoot, 'docs', 'progress', `${taskId}.md`),
119
+ dependencies: [
120
+ getArtifactPath(taskDir, 'implement'),
121
+ getArtifactPath(taskDir, 'verify'),
122
+ ],
123
+ template: { requiredHeadings: ['# Progress', '## Summary', '## Verification', '## Remaining Risk'] },
124
+ completionCriteria: [
125
+ 'records commands actually run and their outcome',
126
+ 'links remaining risks or follow-up tasks',
127
+ ],
128
+ }));
129
+ }
130
+ const artifactSteps = ['analyze', 'plan', 'implement', 'verify', 'retrospective'];
131
+ return refreshInstructionBlockers(withMissingDependencies({
132
+ ...base,
133
+ outputPath: path.join(taskDir, 'artifacts'),
134
+ dependencies: [paths.taskConfigPath, paths.statePath],
135
+ template: {
136
+ requiredHeadings: artifactSteps.map((step) => getArtifactRelativePath(step)),
137
+ },
138
+ completionCriteria: [
139
+ 'artifact content is substantive and not the new-task template',
140
+ 'verification artifact names exact commands and pass/fail results',
141
+ ],
142
+ }));
143
+ }
144
+ function withMissingDependencies(doc) {
145
+ return {
146
+ ...doc,
147
+ blocked: false,
148
+ missingDependencies: [],
149
+ };
150
+ }
151
+ async function listExistingFiles(root) {
152
+ try {
153
+ const names = await readdir(root, { recursive: true });
154
+ return names
155
+ .map((name) => String(name))
156
+ .filter((name) => !name.includes('/logs/'))
157
+ .sort();
158
+ }
159
+ catch {
160
+ return [];
161
+ }
162
+ }
163
+ export async function refreshInstructionBlockers(doc) {
164
+ const missing = [];
165
+ for (const dep of doc.dependencies) {
166
+ try {
167
+ await access(dep);
168
+ }
169
+ catch {
170
+ missing.push(dep);
171
+ }
172
+ }
173
+ return {
174
+ ...doc,
175
+ blocked: missing.length > 0,
176
+ missingDependencies: missing,
177
+ };
178
+ }
179
+ function formatInstructionsMarkdown(doc) {
180
+ const lines = [
181
+ `# Instructions: ${doc.artifactId}`,
182
+ '',
183
+ `- task: \`${doc.taskId}\``,
184
+ `- output: \`${doc.outputPath}\``,
185
+ `- write policy: \`${doc.writePolicy.mode}\``,
186
+ `- blocked: ${doc.blocked ? 'yes' : 'no'}`,
187
+ '',
188
+ '## Dependencies',
189
+ ...doc.dependencies.map((dep) => `- \`${dep}\``),
190
+ '',
191
+ '## Completion Criteria',
192
+ ...doc.completionCriteria.map((criterion) => `- ${criterion}`),
193
+ ];
194
+ return `${lines.join('\n')}\n`;
195
+ }
@@ -0,0 +1,64 @@
1
+ import path from "node:path";
2
+ import { curateKnowledgePatterns } from "../workflows/dag/knowledge-curator.js";
3
+ export function parseKnowledgeArgs(args) {
4
+ const [command, ...rest] = args;
5
+ if (command !== "curate") {
6
+ throw new Error("usage: knowledge curate [--json|--markdown] [--output <path>]");
7
+ }
8
+ let outputPath;
9
+ let json = false;
10
+ let markdown = false;
11
+ for (let i = 0; i < rest.length; i += 1) {
12
+ const arg = rest[i];
13
+ if (arg === "--json") {
14
+ json = true;
15
+ continue;
16
+ }
17
+ if (arg === "--markdown") {
18
+ markdown = true;
19
+ continue;
20
+ }
21
+ if (arg === "--output") {
22
+ outputPath = rest[++i];
23
+ if (!outputPath)
24
+ throw new Error("knowledge curate --output requires a path");
25
+ continue;
26
+ }
27
+ if (arg.startsWith("--output=")) {
28
+ outputPath = arg.slice("--output=".length);
29
+ continue;
30
+ }
31
+ throw new Error(`unknown knowledge curate argument: ${arg}`);
32
+ }
33
+ if (!json && !markdown)
34
+ json = true;
35
+ return { command, outputPath, json, markdown };
36
+ }
37
+ export async function runKnowledge(repoRoot, args) {
38
+ const parsed = parseKnowledgeArgs(args);
39
+ const result = await curateKnowledgePatterns({
40
+ repoRoot,
41
+ outputPath: parsed.outputPath,
42
+ });
43
+ if (!result.ok) {
44
+ process.exitCode = 1;
45
+ }
46
+ if (result.outputPath) {
47
+ console.error(`[loop-agent] wrote knowledge proposal: ${path.relative(repoRoot, result.outputPath)}`);
48
+ }
49
+ if (parsed.json) {
50
+ console.log(JSON.stringify({
51
+ ok: result.ok,
52
+ patternsPath: path.relative(repoRoot, result.patternsPath),
53
+ outputPath: result.outputPath ? path.relative(repoRoot, result.outputPath) : undefined,
54
+ patternCount: result.patternCount,
55
+ safetyFindings: result.safetyFindings,
56
+ message: result.message,
57
+ }, null, 2));
58
+ }
59
+ if (parsed.markdown && result.proposalMarkdown && !result.outputPath) {
60
+ if (parsed.json)
61
+ console.log("");
62
+ process.stdout.write(result.proposalMarkdown);
63
+ }
64
+ }
@@ -0,0 +1,72 @@
1
+ import path from "node:path";
2
+ import { buildLoopBenchmarkResult, formatLoopBenchmarkMarkdown, writeLoopBenchmarkMarkdown, } from "../workflows/loop/benchmark.js";
3
+ export function parseLoopBenchmarkArgs(args) {
4
+ let json = false;
5
+ let markdown = false;
6
+ let outputPath;
7
+ for (let i = 0; i < args.length; i += 1) {
8
+ const arg = args[i];
9
+ if (arg === "--json") {
10
+ json = true;
11
+ continue;
12
+ }
13
+ if (arg === "--markdown") {
14
+ markdown = true;
15
+ continue;
16
+ }
17
+ if (arg === "--output") {
18
+ outputPath = args[++i];
19
+ if (!outputPath) {
20
+ throw new Error("loop-benchmark --output requires a path");
21
+ }
22
+ continue;
23
+ }
24
+ if (arg.startsWith("--output=")) {
25
+ outputPath = arg.slice("--output=".length);
26
+ continue;
27
+ }
28
+ if (arg === "--help" || arg === "-h") {
29
+ printLoopBenchmarkUsage();
30
+ process.exit(0);
31
+ }
32
+ throw new Error(`unknown loop-benchmark argument: ${arg}`);
33
+ }
34
+ if (!json && !markdown) {
35
+ json = true;
36
+ }
37
+ return { json, markdown, outputPath };
38
+ }
39
+ export function printLoopBenchmarkUsage() {
40
+ console.log(`usage: loop-benchmark [options]
41
+
42
+ Deterministic loop-agent loop benchmark baseline (no live Pi/Cursor calls).
43
+
44
+ Options:
45
+ --json Emit JSON (default when no format flag is set)
46
+ --markdown Emit Markdown report
47
+ --output <path> Write Markdown report to a repo-relative or absolute path
48
+ -h, --help Show this help
49
+
50
+ Control groups: single-repair, 3-pass-convergence, 3-pass-convergence+quota.
51
+ Recommendation never changes convergence.enabled default.`);
52
+ }
53
+ export async function runLoopBenchmark(repoRoot, rawArgs) {
54
+ const parsed = parseLoopBenchmarkArgs(rawArgs);
55
+ const result = buildLoopBenchmarkResult();
56
+ if (parsed.outputPath) {
57
+ if (!parsed.markdown) {
58
+ throw new Error("loop-benchmark --output requires --markdown");
59
+ }
60
+ const resolved = await writeLoopBenchmarkMarkdown(repoRoot, parsed.outputPath, result);
61
+ console.error(`[loop-agent] wrote loop benchmark report: ${path.relative(repoRoot, resolved)}`);
62
+ }
63
+ if (parsed.json) {
64
+ console.log(JSON.stringify(result, null, 2));
65
+ }
66
+ if (parsed.markdown && !parsed.outputPath) {
67
+ if (parsed.json) {
68
+ console.log("");
69
+ }
70
+ process.stdout.write(formatLoopBenchmarkMarkdown(result));
71
+ }
72
+ }