@tea-agent/loop-agent 0.1.0 → 0.2.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 (100) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +127 -92
  3. package/dist/adapters/index.js +3 -2
  4. package/dist/adapters/loop-agent.js +44 -2
  5. package/dist/application/dag/args.js +420 -0
  6. package/dist/application/dag/generate-task-dag.js +280 -0
  7. package/dist/application/dag/report-dag.js +14 -0
  8. package/dist/application/dag/run-dag.js +93 -0
  9. package/dist/application/dag/validate-dag.js +101 -0
  10. package/dist/application/loop/run-action.js +23 -0
  11. package/dist/cli/catalog.js +2 -237
  12. package/dist/cli/command-definitions.js +571 -0
  13. package/dist/cli/index.js +2 -0
  14. package/dist/cli/program.js +65 -1
  15. package/dist/cli/router.js +13 -0
  16. package/dist/cli-governance/active-residue-check.js +38 -0
  17. package/dist/commands/dag-report.js +6 -107
  18. package/dist/commands/dag-run-task.js +8 -466
  19. package/dist/commands/dag-validate.js +7 -179
  20. package/dist/commands/examples.js +90 -0
  21. package/dist/commands/init.js +1495 -0
  22. package/dist/commands/loop.js +57 -31
  23. package/dist/commands/pi-prompt.js +2 -9
  24. package/dist/commands/run-dag.js +7 -180
  25. package/dist/executors/cursor-executor-artifacts.js +3 -4
  26. package/dist/executors/cursor-worker-client.js +13 -3
  27. package/dist/executors/dag-cursor-executor.js +2 -3
  28. package/dist/executors/dag-pi-executor.js +3 -4
  29. package/dist/executors/dag-static-executor.js +2 -5
  30. package/dist/executors/pi-defaults.js +9 -0
  31. package/dist/executors/shell-executor.js +12 -20
  32. package/dist/governance/manifest-types.js +1 -0
  33. package/dist/infrastructure/harness/active-residue-policy.js +73 -0
  34. package/dist/infrastructure/harness/artifact-store.js +72 -0
  35. package/dist/infrastructure/harness/atomic-write.js +49 -0
  36. package/dist/infrastructure/harness/completed-facts-guard.js +40 -0
  37. package/dist/infrastructure/harness/loop-action-store.js +23 -0
  38. package/dist/infrastructure/harness/loop-store.js +41 -0
  39. package/dist/infrastructure/harness/one-shot-run-store.js +94 -0
  40. package/dist/infrastructure/harness/task-store.js +77 -0
  41. package/dist/records/one-shot-runs.js +26 -61
  42. package/dist/records/promotion.js +3 -4
  43. package/dist/shared/artifacts-core.js +5 -5
  44. package/dist/shared/logger.js +9 -15
  45. package/dist/task/delegate.js +4 -4
  46. package/dist/task/runtime.js +5 -7
  47. package/dist/task/state.js +6 -20
  48. package/dist/workflows/dag/convergence/controller.js +277 -0
  49. package/dist/workflows/dag/dynamic-runtime/condition.js +48 -0
  50. package/dist/workflows/dag/dynamic-runtime/loop-until.js +156 -0
  51. package/dist/workflows/dag/dynamic-runtime/map.js +185 -0
  52. package/dist/workflows/dag/dynamic-runtime/reduction.js +72 -0
  53. package/dist/workflows/dag/dynamic-runtime/shared.js +133 -0
  54. package/dist/workflows/dag/lifecycle.js +6 -5
  55. package/dist/workflows/dag/node-execution.js +262 -0
  56. package/dist/workflows/dag/run-store.js +36 -0
  57. package/dist/workflows/dag/runner.js +82 -1341
  58. package/dist/workflows/dag/scheduler.js +84 -0
  59. package/dist/workflows/dag/upstream-artifacts.js +20 -18
  60. package/dist/workflows/loop/actions/cursor-fix.js +191 -0
  61. package/dist/workflows/loop/actions/dag-action.js +130 -0
  62. package/dist/workflows/loop/actions/pi-review.js +267 -0
  63. package/dist/workflows/loop/actions/shared.js +157 -0
  64. package/dist/workflows/loop/actions/shell-verify.js +82 -0
  65. package/dist/workflows/loop/actions/types.js +1 -0
  66. package/dist/workflows/loop/actions/workflow-action.js +255 -0
  67. package/dist/workflows/loop/actions.js +55 -1212
  68. package/dist/workflows/loop/closeout.js +5 -4
  69. package/dist/workflows/loop/context.js +2 -3
  70. package/dist/workflows/loop/events.js +3 -2
  71. package/dist/workflows/loop/policy/auto-policy.js +104 -0
  72. package/dist/workflows/loop/policy/cursor-fix-policy.js +31 -0
  73. package/dist/workflows/loop/rounds.js +3 -3
  74. package/dist/workflows/loop/signals.js +4 -7
  75. package/dist/workflows/loop/state.js +11 -11
  76. package/docs/README.md +3 -2
  77. package/docs/architecture/runtime-boundaries.md +147 -0
  78. package/docs/exec-plans/active/README.md +4 -0
  79. package/docs/exec-plans/completed/README.md +6 -2
  80. package/package.json +2 -1
  81. package/skills/ai-engineering-context/SKILL.md +21 -21
  82. package/skills/loop-agent/SKILL.md +73 -188
  83. package/skills/loop-agent/references/README.md +6 -2
  84. package/skills/loop-agent/references/harness-policy.md +113 -113
  85. package/skills/loop-agent/references/learned/README.md +13 -13
  86. package/skills/loop-agent/references/long-running-loop.md +59 -0
  87. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +0 -2
  88. package/skills/loop-agent/references/verification-and-failure-handling.md +18 -0
  89. package/skills/requesting-code-review/SKILL.md +40 -40
  90. package/skills/requesting-code-review/code-reviewer.md +4 -4
  91. package/skills/systematic-debugging/CREATION-LOG.md +43 -43
  92. package/skills/systematic-debugging/SKILL.md +113 -113
  93. package/skills/systematic-debugging/condition-based-waiting.md +20 -20
  94. package/skills/systematic-debugging/defense-in-depth.md +27 -27
  95. package/skills/systematic-debugging/root-cause-tracing.md +38 -38
  96. package/skills/systematic-debugging/test-academic.md +6 -6
  97. package/skills/systematic-debugging/test-pressure-1.md +6 -6
  98. package/skills/systematic-debugging/test-pressure-2.md +2 -2
  99. package/skills/systematic-debugging/test-pressure-3.md +6 -6
  100. package/skills/verification-before-completion/SKILL.md +37 -37
@@ -0,0 +1,571 @@
1
+ import { runDoctor } from "../commands/doctor.js";
2
+ import { runDocsArchive } from "../commands/docs-archive.js";
3
+ import { runDocsAudit } from "../commands/docs-audit.js";
4
+ import { runCoverageAudit } from "../commands/coverage-audit.js";
5
+ import { runExamples } from "../commands/examples.js";
6
+ import { runCloseout } from "../commands/closeout.js";
7
+ import { runHandoffCheck } from "../commands/handoff-check.js";
8
+ import { runInspect } from "../commands/inspect.js";
9
+ import { runInstructions } from "../commands/instructions.js";
10
+ import { runNewTask } from "../commands/new-task.js";
11
+ import { runPlanList } from "../commands/plan-list.js";
12
+ import { runPromoteRun } from "../commands/promote-run.js";
13
+ import { runSpine } from "../commands/spine.js";
14
+ import { runStats } from "../commands/stats.js";
15
+ import { runStatus } from "../commands/status.js";
16
+ import { runWorktreeCreate } from "../commands/worktree-create.js";
17
+ import { runWorktreeList } from "../commands/worktree-list.js";
18
+ import { runWorktreeRemove } from "../commands/worktree-remove.js";
19
+ import { runDelegate } from "../commands/delegate.js";
20
+ import { runHarvest } from "../commands/harvest.js";
21
+ import { runInit } from "../commands/init.js";
22
+ import { runRunDag } from "../commands/run-dag.js";
23
+ import { runDagInitHybrid } from "../commands/dag-init-hybrid.js";
24
+ import { runDagValidate } from "../commands/dag-validate.js";
25
+ import { runDagWorkflowValidate } from "../commands/dag-workflow-validate.js";
26
+ import { runDagWorkflowCompile } from "../commands/dag-workflow-compile.js";
27
+ import { runDagWorkflowPlan } from "../commands/dag-workflow-plan.js";
28
+ import { runDagApprove } from "../commands/dag-approve.js";
29
+ import { runDagReject } from "../commands/dag-reject.js";
30
+ import { runDagReconcileTasks } from "../commands/dag-reconcile-tasks.js";
31
+ import { runDagResume } from "../commands/dag-resume.js";
32
+ import { runDagDecisionInspect, runDagDecisionValidate, } from "../workflows/dag/decision-envelope.js";
33
+ import { runDagDoctor, runDagStatus, } from "../workflows/dag/lifecycle.js";
34
+ import { runDagRunTask } from "../commands/dag-run-task.js";
35
+ import { runDagCloseoutDraft, runDagReport } from "../commands/dag-report.js";
36
+ import { runDagFinalVerification } from "../commands/dag-final-verification.js";
37
+ import { runCursorWorker } from "../commands/cursor-worker.js";
38
+ import { runKnowledge } from "../commands/knowledge.js";
39
+ import { runLoopBenchmark } from "../commands/loop-benchmark.js";
40
+ import { runPiReuseBenchmark } from "../commands/pi-reuse-benchmark.js";
41
+ import { runGoal } from "../commands/goal.js";
42
+ import { runLoop } from "../commands/loop.js";
43
+ import { runWorkflowCommand } from "../commands/workflow.js";
44
+ import { runReferenceIndex } from "../commands/reference-index.js";
45
+ import { runStudyInit } from "../commands/study-init.js";
46
+ export function formatSubcommandUsage(name, subcommands) {
47
+ return `${name} <${subcommands.join("|")}> ...`;
48
+ }
49
+ export function formatSubcommandUsageError(name, subcommands) {
50
+ return `usage: ${formatSubcommandUsage(name, subcommands)}`;
51
+ }
52
+ const DOCS_SUBCOMMANDS = ["audit", "archive"];
53
+ const INIT_SUBCOMMANDS = ["instructions", "doctor"];
54
+ const EXAMPLES_SUBCOMMANDS = ["list", "show", "copy"];
55
+ const CLOSEOUT_SUBCOMMANDS = ["task"];
56
+ const PLAN_SUBCOMMANDS = ["list"];
57
+ const SPINE_SUBCOMMANDS = ["audit"];
58
+ const HANDOFF_SUBCOMMANDS = ["check", "coverage"];
59
+ const HANDOFF_USAGE = "handoff <check|coverage> [taskId]";
60
+ const REFERENCE_SUBCOMMANDS = ["index"];
61
+ const STUDY_SUBCOMMANDS = ["init"];
62
+ const WORKTREE_SUBCOMMANDS = ["create", "list", "remove"];
63
+ const KNOWLEDGE_SUBCOMMANDS = ["curate"];
64
+ const DAG_SUBCOMMANDS = [
65
+ "init-hybrid",
66
+ "run-task",
67
+ "validate",
68
+ "workflow-plan",
69
+ "workflow-validate",
70
+ "workflow-compile",
71
+ "approve",
72
+ "reject",
73
+ "resume",
74
+ "status",
75
+ "doctor",
76
+ "report",
77
+ "closeout-draft",
78
+ "reconcile-tasks",
79
+ "final-verification",
80
+ "decision",
81
+ ];
82
+ const WORKFLOW_SUBCOMMANDS = [
83
+ "list",
84
+ "inspect",
85
+ "save",
86
+ "run",
87
+ "diff",
88
+ "replay",
89
+ ];
90
+ const WORKFLOW_USAGE = "workflow <list|inspect|save|run|diff|replay> [name] [options]";
91
+ const LOOP_SUBCOMMANDS = [
92
+ "init",
93
+ "status",
94
+ "run",
95
+ "record-round",
96
+ "add-signal",
97
+ "closeout",
98
+ ];
99
+ /** Order remains stable for compatibility; tiers provide the selection signal. */
100
+ export const COMMAND_DEFINITIONS = [
101
+ {
102
+ name: "inspect",
103
+ adapter: "required",
104
+ tier: "operator",
105
+ intent: "Inspect the target repo harness manifest.",
106
+ usage: "inspect",
107
+ handler: async ({ adapter, repoRoot }) => {
108
+ await runInspect(adapter, repoRoot);
109
+ },
110
+ },
111
+ {
112
+ name: "doctor",
113
+ adapter: "required",
114
+ tier: "operator",
115
+ intent: "Check local loop-agent runtime health.",
116
+ usage: "doctor",
117
+ handler: async ({ adapter, repoRoot }) => {
118
+ await runDoctor(adapter, repoRoot);
119
+ },
120
+ },
121
+ {
122
+ name: "docs",
123
+ adapter: "required",
124
+ tier: "operator",
125
+ intent: "Audit or archive repo governance documents.",
126
+ usage: formatSubcommandUsage("docs", DOCS_SUBCOMMANDS),
127
+ subcommands: [...DOCS_SUBCOMMANDS],
128
+ handler: async ({ repoRoot, subcommand, rest }) => {
129
+ if (subcommand === "audit") {
130
+ await runDocsAudit(repoRoot);
131
+ return;
132
+ }
133
+ if (subcommand === "archive") {
134
+ const [planPath] = rest;
135
+ if (!planPath)
136
+ throw new Error("usage: docs archive <active-plan-path>");
137
+ await runDocsArchive(repoRoot, planPath);
138
+ return;
139
+ }
140
+ throw new Error(formatSubcommandUsageError("docs", DOCS_SUBCOMMANDS));
141
+ },
142
+ },
143
+ {
144
+ name: "init",
145
+ adapter: "none",
146
+ tier: "primary",
147
+ intent: "Initialize a target repository with loop-agent harness capabilities.",
148
+ usage: "init [instructions|doctor] [--profile full|minimal] [--merge]",
149
+ subcommands: [...INIT_SUBCOMMANDS],
150
+ handler: async ({ repoRoot, subcommand, rest }) => {
151
+ await runInit(repoRoot, [subcommand, ...rest].filter(Boolean));
152
+ },
153
+ },
154
+ {
155
+ name: "examples",
156
+ adapter: "none",
157
+ tier: "operator",
158
+ intent: "List, show, or copy bundled loop-agent examples.",
159
+ usage: "examples <list|show|copy> [name] [--output <path>]",
160
+ subcommands: [...EXAMPLES_SUBCOMMANDS],
161
+ handler: async ({ repoRoot, subcommand, rest }) => {
162
+ await runExamples(repoRoot, [subcommand, ...rest].filter(Boolean));
163
+ },
164
+ },
165
+ {
166
+ name: "new-task",
167
+ adapter: "required",
168
+ tier: "primary",
169
+ intent: "Create a harness task before DAG autonomous work.",
170
+ usage: "new-task <task-id> [title]",
171
+ handler: async ({ repoRoot, subcommand, rest }) => {
172
+ const [taskId, ...titleParts] = [subcommand, ...rest];
173
+ if (!taskId)
174
+ throw new Error("taskId required");
175
+ await runNewTask(repoRoot, taskId, titleParts.join(" ") || undefined);
176
+ },
177
+ },
178
+ {
179
+ name: "status",
180
+ adapter: "required",
181
+ tier: "operator",
182
+ intent: "Inspect a task state and machine-readable action context.",
183
+ usage: "status <task-id> [--json]",
184
+ handler: async ({ repoRoot, subcommand }) => {
185
+ const taskId = subcommand;
186
+ if (!taskId)
187
+ throw new Error("taskId required");
188
+ await runStatus(repoRoot, taskId);
189
+ },
190
+ },
191
+ {
192
+ name: "instructions",
193
+ adapter: "required",
194
+ tier: "operator",
195
+ intent: "Resolve task artifact targets, dependencies, templates, and write policy.",
196
+ usage: "instructions <source|dag-draft|task-artifacts|promotion|closeout> --task <task-id> [--json]",
197
+ handler: async ({ repoRoot, subcommand, rest }) => {
198
+ await runInstructions(repoRoot, [subcommand, ...rest].filter(Boolean));
199
+ },
200
+ },
201
+ {
202
+ name: "promote-run",
203
+ adapter: "required",
204
+ tier: "operator",
205
+ intent: "Promote completed DAG or one-shot run evidence into task artifacts without mutating run facts.",
206
+ usage: "promote-run <task-id> --run-id <run-id>",
207
+ handler: async ({ repoRoot, subcommand, rest }) => {
208
+ await runPromoteRun(repoRoot, [subcommand, ...rest].filter(Boolean));
209
+ },
210
+ },
211
+ {
212
+ name: "closeout",
213
+ adapter: "required",
214
+ tier: "operator",
215
+ intent: "Create deterministic task closeout/progress records from promoted task artifacts.",
216
+ usage: "closeout task <task-id>",
217
+ subcommands: [...CLOSEOUT_SUBCOMMANDS],
218
+ handler: async ({ repoRoot, subcommand, rest }) => {
219
+ await runCloseout(repoRoot, [subcommand, ...rest].filter(Boolean));
220
+ },
221
+ },
222
+ {
223
+ name: "stats",
224
+ adapter: "required",
225
+ tier: "operator",
226
+ intent: "Inspect historical step duration statistics.",
227
+ usage: "stats",
228
+ handler: async ({ repoRoot }) => {
229
+ await runStats(repoRoot);
230
+ },
231
+ },
232
+ {
233
+ name: "plan",
234
+ adapter: "required",
235
+ tier: "operator",
236
+ intent: "List repo execution plans.",
237
+ usage: formatSubcommandUsage("plan", PLAN_SUBCOMMANDS),
238
+ subcommands: [...PLAN_SUBCOMMANDS],
239
+ handler: async ({ repoRoot, subcommand }) => {
240
+ if (subcommand === "list") {
241
+ await runPlanList(repoRoot);
242
+ return;
243
+ }
244
+ throw new Error(formatSubcommandUsageError("plan", PLAN_SUBCOMMANDS));
245
+ },
246
+ },
247
+ {
248
+ name: "spine",
249
+ adapter: "required",
250
+ tier: "operator",
251
+ intent: "Audit a task minimal spec spine: source, path ownership, requirements, and final verification coverage.",
252
+ usage: "spine audit <task-id> [--json|--markdown]",
253
+ subcommands: [...SPINE_SUBCOMMANDS],
254
+ handler: async ({ repoRoot, subcommand, rest }) => {
255
+ await runSpine(repoRoot, [subcommand, ...rest].filter((arg) => Boolean(arg)));
256
+ },
257
+ },
258
+ {
259
+ name: "handoff",
260
+ adapter: "required",
261
+ tier: "operator",
262
+ intent: "Check task handoff completeness and requirement coverage.",
263
+ usage: HANDOFF_USAGE,
264
+ subcommands: [...HANDOFF_SUBCOMMANDS],
265
+ handler: async ({ repoRoot, subcommand, rest }) => {
266
+ if (subcommand === "check") {
267
+ const [taskId] = rest;
268
+ await runHandoffCheck(repoRoot, taskId);
269
+ return;
270
+ }
271
+ if (subcommand === "coverage") {
272
+ const [taskId, ...flags] = rest;
273
+ if (!taskId)
274
+ throw new Error("usage: handoff coverage <taskId> [--json|--markdown]");
275
+ const json = flags.includes("--json");
276
+ const markdown = flags.includes("--markdown");
277
+ const exitCode = await runCoverageAudit(repoRoot, taskId, {
278
+ json,
279
+ markdown,
280
+ });
281
+ process.exitCode = exitCode;
282
+ return;
283
+ }
284
+ throw new Error(`usage: ${HANDOFF_USAGE}`);
285
+ },
286
+ },
287
+ {
288
+ name: "goal",
289
+ adapter: "required",
290
+ tier: "compatibility",
291
+ compatibilityStatus: "deprecated-candidate",
292
+ intent: "Manage legacy task continuation goals (compatibility-only; prefer Agent DAG closeout).",
293
+ usage: "goal <set|get|pause|resume|clear> ...",
294
+ handler: async ({ repoRoot, subcommand, rest }) => {
295
+ await runGoal(repoRoot, subcommand, rest);
296
+ },
297
+ },
298
+ {
299
+ name: "reference",
300
+ adapter: "required",
301
+ tier: "compatibility",
302
+ compatibilityStatus: "compatibility-only",
303
+ intent: "Index external references for feature-study tasks (compatibility-only).",
304
+ usage: "reference <index> <task-id>",
305
+ subcommands: [...REFERENCE_SUBCOMMANDS],
306
+ handler: async ({ repoRoot, subcommand, rest }) => {
307
+ if (subcommand === "index") {
308
+ const [taskId] = rest;
309
+ if (!taskId)
310
+ throw new Error("usage: reference index <task-id>");
311
+ await runReferenceIndex(repoRoot, taskId);
312
+ return;
313
+ }
314
+ throw new Error("usage: reference <index> <task-id>");
315
+ },
316
+ },
317
+ {
318
+ name: "study",
319
+ adapter: "required",
320
+ tier: "compatibility",
321
+ compatibilityStatus: "compatibility-only",
322
+ intent: "Initialize feature-study tasks (compatibility-only; prefer DAG work).",
323
+ usage: "study init <task-id> [title] ...",
324
+ subcommands: [...STUDY_SUBCOMMANDS],
325
+ handler: async ({ repoRoot, subcommand, rest }) => {
326
+ if (subcommand === "init") {
327
+ await runStudyInit(repoRoot, rest);
328
+ return;
329
+ }
330
+ throw new Error("usage: study init <task-id> [title] ...");
331
+ },
332
+ },
333
+ {
334
+ name: "worktree",
335
+ adapter: "required",
336
+ tier: "escape-hatch",
337
+ intent: "Manage delegated worktrees directly.",
338
+ usage: formatSubcommandUsage("worktree", WORKTREE_SUBCOMMANDS),
339
+ subcommands: [...WORKTREE_SUBCOMMANDS],
340
+ handler: async ({ repoRoot, subcommand, rest }) => {
341
+ if (subcommand === "create") {
342
+ await runWorktreeCreate(repoRoot, rest);
343
+ return;
344
+ }
345
+ if (subcommand === "list") {
346
+ await runWorktreeList(repoRoot);
347
+ return;
348
+ }
349
+ if (subcommand === "remove") {
350
+ await runWorktreeRemove(repoRoot, rest);
351
+ return;
352
+ }
353
+ throw new Error(formatSubcommandUsageError("worktree", WORKTREE_SUBCOMMANDS));
354
+ },
355
+ },
356
+ {
357
+ name: "delegate",
358
+ adapter: "required",
359
+ tier: "escape-hatch",
360
+ intent: "Prepare or run isolated executor delegation outside the normal DAG path.",
361
+ usage: "delegate <task-id> [options]",
362
+ handler: async ({ repoRoot, subcommand, rest }) => {
363
+ const delegateArgs = [subcommand, ...rest].filter((arg) => Boolean(arg));
364
+ await runDelegate(repoRoot, delegateArgs);
365
+ },
366
+ },
367
+ {
368
+ name: "harvest",
369
+ adapter: "required",
370
+ tier: "escape-hatch",
371
+ intent: "Merge and close isolated delegated work after completion.",
372
+ usage: "harvest <task-id> [options]",
373
+ handler: async ({ repoRoot, subcommand, rest }) => {
374
+ const harvestArgs = [subcommand, ...rest].filter((arg) => Boolean(arg));
375
+ await runHarvest(repoRoot, harvestArgs);
376
+ },
377
+ },
378
+ {
379
+ name: "cursor-worker",
380
+ adapter: "required",
381
+ tier: "escape-hatch",
382
+ intent: "Inspect or control the optional Cursor SDK worker process.",
383
+ usage: "cursor-worker <status|stop|ping>",
384
+ handler: async ({ repoRoot, subcommand, rest }) => {
385
+ await runCursorWorker(repoRoot, [subcommand, ...rest].filter((arg) => Boolean(arg)));
386
+ },
387
+ },
388
+ {
389
+ name: "knowledge",
390
+ adapter: "required",
391
+ tier: "operator",
392
+ intent: "Curate completed convergence patterns into human-gated learned guidance proposals.",
393
+ usage: "knowledge curate [--json|--markdown] [--output <path>]",
394
+ subcommands: [...KNOWLEDGE_SUBCOMMANDS],
395
+ handler: async ({ repoRoot, subcommand, rest }) => {
396
+ await runKnowledge(repoRoot, [subcommand, ...rest].filter((arg) => Boolean(arg)));
397
+ },
398
+ },
399
+ {
400
+ name: "dag",
401
+ adapter: "required",
402
+ tier: "primary",
403
+ intent: "Generate, validate, inspect, recover, close out Agent DAG runs, and use experimental dynamic workflow compile seams.",
404
+ usage: formatSubcommandUsage("dag", DAG_SUBCOMMANDS),
405
+ subcommands: [...DAG_SUBCOMMANDS],
406
+ handler: async ({ repoRoot, subcommand, rest }) => {
407
+ if (subcommand === "init-hybrid") {
408
+ const initArgs = rest.filter((arg) => Boolean(arg));
409
+ await runDagInitHybrid(repoRoot, initArgs);
410
+ return;
411
+ }
412
+ if (subcommand === "validate") {
413
+ const validateArgs = rest.filter((arg) => Boolean(arg));
414
+ await runDagValidate(repoRoot, validateArgs);
415
+ return;
416
+ }
417
+ if (subcommand === "workflow-validate") {
418
+ const validateArgs = rest.filter((arg) => Boolean(arg));
419
+ await runDagWorkflowValidate(repoRoot, validateArgs);
420
+ return;
421
+ }
422
+ if (subcommand === "workflow-plan") {
423
+ const planArgs = rest.filter((arg) => Boolean(arg));
424
+ await runDagWorkflowPlan(repoRoot, planArgs);
425
+ return;
426
+ }
427
+ if (subcommand === "workflow-compile") {
428
+ const compileArgs = rest.filter((arg) => Boolean(arg));
429
+ await runDagWorkflowCompile(repoRoot, compileArgs);
430
+ return;
431
+ }
432
+ if (subcommand === "approve") {
433
+ const approveArgs = rest.filter((arg) => Boolean(arg));
434
+ await runDagApprove(repoRoot, approveArgs);
435
+ return;
436
+ }
437
+ if (subcommand === "reject") {
438
+ const rejectArgs = rest.filter((arg) => Boolean(arg));
439
+ await runDagReject(repoRoot, rejectArgs);
440
+ return;
441
+ }
442
+ if (subcommand === "resume") {
443
+ const resumeArgs = rest.filter((arg) => Boolean(arg));
444
+ await runDagResume(repoRoot, resumeArgs);
445
+ return;
446
+ }
447
+ if (subcommand === "status") {
448
+ const statusArgs = rest.filter((arg) => Boolean(arg));
449
+ await runDagStatus(repoRoot, statusArgs);
450
+ return;
451
+ }
452
+ if (subcommand === "doctor") {
453
+ const doctorArgs = rest.filter((arg) => Boolean(arg));
454
+ await runDagDoctor(repoRoot, doctorArgs);
455
+ return;
456
+ }
457
+ if (subcommand === "run-task") {
458
+ const runTaskArgs = rest.filter((arg) => Boolean(arg));
459
+ await runDagRunTask(repoRoot, runTaskArgs);
460
+ return;
461
+ }
462
+ if (subcommand === "report") {
463
+ const reportArgs = rest.filter((arg) => Boolean(arg));
464
+ await runDagReport(repoRoot, reportArgs);
465
+ return;
466
+ }
467
+ if (subcommand === "closeout-draft") {
468
+ const closeoutArgs = rest.filter((arg) => Boolean(arg));
469
+ await runDagCloseoutDraft(repoRoot, closeoutArgs);
470
+ return;
471
+ }
472
+ if (subcommand === "reconcile-tasks") {
473
+ const reconcileArgs = rest.filter((arg) => Boolean(arg));
474
+ await runDagReconcileTasks(repoRoot, reconcileArgs);
475
+ return;
476
+ }
477
+ if (subcommand === "final-verification") {
478
+ const finalVerificationArgs = rest.filter((arg) => Boolean(arg));
479
+ await runDagFinalVerification(repoRoot, finalVerificationArgs);
480
+ return;
481
+ }
482
+ if (subcommand === "decision") {
483
+ const decisionSubcommand = rest[0];
484
+ const decisionArgs = rest.slice(1).filter((arg) => Boolean(arg));
485
+ if (decisionSubcommand === "inspect") {
486
+ await runDagDecisionInspect(repoRoot, decisionArgs);
487
+ return;
488
+ }
489
+ if (decisionSubcommand === "validate") {
490
+ await runDagDecisionValidate(repoRoot, decisionArgs);
491
+ return;
492
+ }
493
+ throw new Error("usage: dag decision <inspect|validate> --run-id <id> [--node-id <node-id>]");
494
+ }
495
+ throw new Error(formatSubcommandUsageError("dag", DAG_SUBCOMMANDS));
496
+ },
497
+ },
498
+ {
499
+ name: "run-dag",
500
+ adapter: "required",
501
+ tier: "primary",
502
+ intent: "Execute a reviewed Agent DAG spec.",
503
+ usage: "run-dag --dag <path> [options]",
504
+ handler: async ({ repoRoot, subcommand, rest }) => {
505
+ const runDagArgs = [subcommand, ...rest].filter((arg) => Boolean(arg));
506
+ await runRunDag(repoRoot, runDagArgs);
507
+ },
508
+ },
509
+ {
510
+ name: "workflow",
511
+ adapter: "required",
512
+ tier: "experimental",
513
+ intent: "List, save, inspect, run, diff, and replay saved dynamic workflows.",
514
+ usage: WORKFLOW_USAGE,
515
+ subcommands: [...WORKFLOW_SUBCOMMANDS],
516
+ handler: async ({ repoRoot, subcommand, rest }) => {
517
+ await runWorkflowCommand(repoRoot, [subcommand, ...rest].filter((arg) => Boolean(arg)));
518
+ },
519
+ },
520
+ {
521
+ name: "loop",
522
+ adapter: "required",
523
+ tier: "experimental",
524
+ intent: "Manage durable long-running task loop state above Agent DAG rounds.",
525
+ usage: "loop <init|status|run|record-round|add-signal|closeout> <task-id> [options]",
526
+ subcommands: [...LOOP_SUBCOMMANDS],
527
+ handler: async ({ repoRoot, subcommand, rest }) => {
528
+ await runLoop(repoRoot, [subcommand, ...rest].filter((arg) => Boolean(arg)));
529
+ },
530
+ },
531
+ {
532
+ name: "cursor-prompt",
533
+ adapter: "none",
534
+ tier: "escape-hatch",
535
+ intent: "Run one-shot Cursor diagnostics without harness state.",
536
+ usage: "cursor-prompt [options] [task]",
537
+ },
538
+ {
539
+ name: "pi-prompt",
540
+ adapter: "none",
541
+ tier: "escape-hatch",
542
+ intent: "Run one-shot Pi diagnostics without harness state.",
543
+ usage: "pi-prompt [options] [task]",
544
+ },
545
+ {
546
+ name: "pi-reuse-benchmark",
547
+ adapter: "required",
548
+ tier: "operator",
549
+ intent: "Read existing Pi runtime reuse benchmark evidence.",
550
+ usage: "pi-reuse-benchmark [--report <path>] [--approval <path>] [--off-executor|--off-task ...] [--on-executor|--on-task ...] [--json] [--markdown]",
551
+ handler: async ({ repoRoot, subcommand, rest }) => {
552
+ const benchmarkArgs = [subcommand, ...rest].filter((arg) => Boolean(arg));
553
+ await runPiReuseBenchmark(repoRoot, benchmarkArgs);
554
+ },
555
+ },
556
+ {
557
+ name: "loop-benchmark",
558
+ adapter: "required",
559
+ tier: "operator",
560
+ intent: "Generate deterministic loop convergence benchmark baseline evidence.",
561
+ usage: "loop-benchmark [--json|--markdown] [--output <path>]",
562
+ handler: async ({ repoRoot, subcommand, rest }) => {
563
+ const benchmarkArgs = [subcommand, ...rest].filter((arg) => Boolean(arg));
564
+ await runLoopBenchmark(repoRoot, benchmarkArgs);
565
+ },
566
+ },
567
+ ];
568
+ export function toCommandManifestEntry(definition) {
569
+ const { handler: _handler, ...manifest } = definition;
570
+ return manifest;
571
+ }
package/dist/cli/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export * from "./catalog.js";
2
+ export * from "./command-definitions.js";
2
3
  export * from "./help.js";
3
4
  export * from "./program.js";
5
+ export * from "./router.js";
@@ -21,9 +21,11 @@ import { runDelegate } from "../commands/delegate.js";
21
21
  import { runDocsArchive } from "../commands/docs-archive.js";
22
22
  import { runDocsAudit } from "../commands/docs-audit.js";
23
23
  import { runDoctor } from "../commands/doctor.js";
24
+ import { runExamples } from "../commands/examples.js";
24
25
  import { runGoal } from "../commands/goal.js";
25
26
  import { runHandoffCheck } from "../commands/handoff-check.js";
26
27
  import { runHarvest } from "../commands/harvest.js";
28
+ import { runInit } from "../commands/init.js";
27
29
  import { runInspect } from "../commands/inspect.js";
28
30
  import { runInstructions } from "../commands/instructions.js";
29
31
  import { runKnowledge } from "../commands/knowledge.js";
@@ -48,6 +50,13 @@ import { handleStandaloneOneShotCommandError } from "../shared/one-shot-prompt-a
48
50
  import { runDagDecisionInspect, runDagDecisionValidate, } from "../workflows/dag/decision-envelope.js";
49
51
  import { runDagDoctor, runDagStatus } from "../workflows/dag/lifecycle.js";
50
52
  import { CODE_AGENT_COMMAND_MANIFEST } from "./catalog.js";
53
+ function resolveRepoRootOnly(command, defaultRepoRoot) {
54
+ const options = collectGlobalOptions(command);
55
+ const repoRoot = path.resolve(options.repoRoot ?? defaultRepoRoot);
56
+ if (options.repoRoot)
57
+ console.log(`[loop-agent] repo root: ${repoRoot}`);
58
+ return repoRoot;
59
+ }
51
60
  function collectGlobalOptions(command) {
52
61
  let current = command;
53
62
  const merged = {};
@@ -314,6 +323,18 @@ function addRawSubcommands(parent, subcommands, defaultRepoRoot) {
314
323
  parent.addCommand(createRawArgsCommand(subcommand, `${parent.name()} ${subcommand}`, defaultRepoRoot));
315
324
  }
316
325
  }
326
+ function addStandaloneSubcommands(parent, subcommands, handler) {
327
+ for (const subcommand of subcommands) {
328
+ parent
329
+ .command(subcommand)
330
+ .allowUnknownOption(true)
331
+ .allowExcessArguments(true)
332
+ .argument("[args...]", "command-specific arguments")
333
+ .action(async (args, _options, command) => {
334
+ await handler([subcommand, ...args], command);
335
+ });
336
+ }
337
+ }
317
338
  function configureDagValidate(command) {
318
339
  command
319
340
  .description("Validate an Agent DAG spec")
@@ -386,7 +407,13 @@ function optionTokens(command) {
386
407
  return tokens;
387
408
  }
388
409
  function commandArgsWithOptions(args, command) {
389
- return [...args, ...optionTokens(command)];
410
+ const tokens = [];
411
+ let current = command;
412
+ while (current?.parent) {
413
+ tokens.push(...optionTokens(current));
414
+ current = current.parent;
415
+ }
416
+ return [...args, ...tokens];
390
417
  }
391
418
  function replaceActionWithOptionAwareDispatch(command, defaultRepoRoot) {
392
419
  command.action(async (args, _options, actionCommand) => {
@@ -446,6 +473,12 @@ async function runPiPromptCommand(args) {
446
473
  handleStandaloneOneShotCommandError(err, printPiPromptUsage);
447
474
  }
448
475
  }
476
+ async function runInitCommand(args, command, defaultRepoRoot) {
477
+ await runInit(resolveRepoRootOnly(command, defaultRepoRoot), commandArgsWithOptions(args, command));
478
+ }
479
+ async function runExamplesCommand(args, command, defaultRepoRoot) {
480
+ await runExamples(resolveRepoRootOnly(command, defaultRepoRoot), commandArgsWithOptions(args, command));
481
+ }
449
482
  export function buildLoopAgentProgram(options) {
450
483
  const program = new Command();
451
484
  program
@@ -480,6 +513,37 @@ export function buildLoopAgentProgram(options) {
480
513
  .action(async (args) => runPiPromptCommand(args));
481
514
  continue;
482
515
  }
516
+ if (entry.name === "init") {
517
+ const command = new Command("init")
518
+ .description(entry.intent)
519
+ .allowUnknownOption(true)
520
+ .allowExcessArguments(true)
521
+ .argument("[args...]", "init arguments")
522
+ .option("--project-name <name>", "target project name")
523
+ .option("--governance-root <path>", "governance root", "docs")
524
+ .option("--profile <profile>", "full|minimal", "full")
525
+ .option("--merge", "merge existing files")
526
+ .option("--no-merge", "skip existing files")
527
+ .option("--provider <provider>", "model provider")
528
+ .option("--model <model>", "model name")
529
+ .option("--json", "print JSON");
530
+ command.action(async (args, _options, actionCommand) => runInitCommand(args, actionCommand, options.defaultRepoRoot));
531
+ addStandaloneSubcommands(command, entry.subcommands ?? [], (args, actionCommand) => runInitCommand(args, actionCommand, options.defaultRepoRoot));
532
+ program.addCommand(command);
533
+ continue;
534
+ }
535
+ if (entry.name === "examples") {
536
+ const command = new Command("examples")
537
+ .description(entry.intent)
538
+ .allowUnknownOption(true)
539
+ .allowExcessArguments(true)
540
+ .argument("[args...]", "examples arguments")
541
+ .option("-o, --output <path>", "copy output path");
542
+ command.action(async (args, _options, actionCommand) => runExamplesCommand(args, actionCommand, options.defaultRepoRoot));
543
+ addStandaloneSubcommands(command, entry.subcommands ?? [], (args, actionCommand) => runExamplesCommand(args, actionCommand, options.defaultRepoRoot));
544
+ program.addCommand(command);
545
+ continue;
546
+ }
483
547
  const command = createRawArgsCommand(entry.name, entry.intent, options.defaultRepoRoot);
484
548
  if (entry.name === "run-dag") {
485
549
  configureRunDag(command);