@ludecker/aaac 1.0.0 → 1.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 (82) hide show
  1. package/README.md +4 -3
  2. package/package.json +13 -1
  3. package/src/cli.mjs +39 -5
  4. package/src/generators/generate-commands.mjs +120 -3
  5. package/src/generators/generate-graph.mjs +17 -0
  6. package/src/lib/install.mjs +1 -0
  7. package/src/lib/run-engine-paths.mjs +33 -0
  8. package/src/run-engine/advance-phase.mjs +192 -0
  9. package/src/run-engine/debug-run.mjs +38 -0
  10. package/src/run-engine/gate-write.mjs +95 -0
  11. package/src/run-engine/init-run.mjs +165 -0
  12. package/src/run-engine/lib.mjs +136 -0
  13. package/src/run-engine/log-dump.mjs +76 -0
  14. package/src/run-engine/log-trace.mjs +18 -0
  15. package/src/run-engine/log.mjs +343 -0
  16. package/src/run-engine/record-task.mjs +56 -0
  17. package/src/run-engine/stop-check.mjs +55 -0
  18. package/templates/cursor/aaac/complexity.yaml +98 -0
  19. package/templates/cursor/aaac/contracts/commands/fix-bug.yaml +10 -3
  20. package/templates/cursor/aaac/contracts/commands/fix-module.yaml +41 -0
  21. package/templates/cursor/aaac/contracts/skills/investigation.yaml +22 -1
  22. package/templates/cursor/aaac/contracts/skills/planning.yaml +17 -0
  23. package/templates/cursor/aaac/contracts/skills/validation.yaml +9 -1
  24. package/templates/cursor/aaac/dispatch.md +30 -5
  25. package/templates/cursor/aaac/enforcement.json +22 -0
  26. package/templates/cursor/aaac/fitness-functions.yaml +8 -0
  27. package/templates/cursor/aaac/governance/gates.json +3 -1
  28. package/templates/cursor/aaac/graph.project.yaml +237 -5
  29. package/templates/cursor/aaac/layers.md +3 -1
  30. package/templates/cursor/aaac/lifecycle/lifecycle.json +41 -1
  31. package/templates/cursor/aaac/lifecycle/phases.json +1 -0
  32. package/templates/cursor/aaac/observability/telemetry.yaml +60 -0
  33. package/templates/cursor/aaac/observability/verb-debug.yaml +170 -0
  34. package/templates/cursor/aaac/ontology.json +10 -1
  35. package/templates/cursor/aaac/run/RUN.md +2 -0
  36. package/templates/cursor/aaac/run/schema.json +9 -0
  37. package/templates/cursor/aaac/scripts/generate-runtime-registry.mjs +115 -0
  38. package/templates/cursor/aaac/scripts/run-engine/advance-phase.mjs +192 -0
  39. package/templates/cursor/aaac/scripts/run-engine/debug-run.mjs +38 -0
  40. package/templates/cursor/aaac/scripts/run-engine/gate-write.mjs +95 -0
  41. package/templates/cursor/aaac/scripts/run-engine/init-run.mjs +165 -0
  42. package/templates/cursor/aaac/scripts/run-engine/lib.mjs +136 -0
  43. package/templates/cursor/aaac/scripts/run-engine/log-dump.mjs +76 -0
  44. package/templates/cursor/aaac/scripts/run-engine/log-trace.mjs +18 -0
  45. package/templates/cursor/aaac/scripts/run-engine/log.mjs +343 -0
  46. package/templates/cursor/aaac/scripts/run-engine/record-task.mjs +56 -0
  47. package/templates/cursor/aaac/scripts/run-engine/stop-check.mjs +55 -0
  48. package/templates/cursor/agents/aaac-log-debug.md +72 -0
  49. package/templates/cursor/agents/fix-code-path.md +27 -0
  50. package/templates/cursor/agents/fix-hypothesis-validate.md +26 -0
  51. package/templates/cursor/agents/fix-inventory-confirm.md +22 -0
  52. package/templates/cursor/agents/fix-recent-changes.md +22 -0
  53. package/templates/cursor/agents/fix-regression-scope.md +27 -0
  54. package/templates/cursor/agents/fix-repro-verify.md +21 -0
  55. package/templates/cursor/agents/fix-repro.md +29 -0
  56. package/templates/cursor/agents/fix-runtime-evidence.md +22 -0
  57. package/templates/cursor/agents/fix-test-failures.md +23 -0
  58. package/templates/cursor/agents/playwright-check-run.md +44 -0
  59. package/templates/cursor/hooks/aaac-before-submit.sh +3 -0
  60. package/templates/cursor/hooks/aaac-pre-tool.sh +4 -0
  61. package/templates/cursor/hooks/aaac-stop.sh +3 -0
  62. package/templates/cursor/hooks/aaac-subagent-start.sh +3 -0
  63. package/templates/cursor/hooks.json +30 -0
  64. package/templates/cursor/policies/minimal-complexity.md +101 -0
  65. package/templates/cursor/rules/aaac-enforcement.mdc +42 -0
  66. package/templates/cursor/skills/shared/execution/SKILL.md +1 -1
  67. package/templates/cursor/skills/shared/fitness-functions/SKILL.md +23 -7
  68. package/templates/cursor/skills/shared/investigation/SKILL.md +91 -18
  69. package/templates/cursor/skills/shared/investigation/orchestrator/SKILL.md +12 -4
  70. package/templates/cursor/skills/shared/planning/SKILL.md +74 -8
  71. package/templates/cursor/skills/shared/reporting/SKILL.md +2 -1
  72. package/templates/cursor/skills/shared/root-cause/SKILL.md +14 -3
  73. package/templates/cursor/skills/shared/testing/SKILL.md +26 -5
  74. package/templates/cursor/skills/shared/validation/SKILL.md +48 -13
  75. package/templates/cursor/skills/shared/verbs/_dispatch-utils.md +20 -1
  76. package/templates/cursor/skills/shared/verbs/_lifecycle.md +3 -2
  77. package/templates/cursor/skills/shared/verbs/check/orchestrator/SKILL.md +4 -1
  78. package/templates/cursor/skills/shared/verbs/create/orchestrator/SKILL.md +2 -2
  79. package/templates/cursor/skills/shared/verbs/fix/orchestrator/SKILL.md +21 -11
  80. package/templates/cursor/skills/shared/verbs/fix/orchestrator/contract.yaml +19 -4
  81. package/templates/cursor/skills/shared/verbs/update/orchestrator/SKILL.md +2 -2
  82. package/templates/cursor/skills/shared/verification/SKILL.md +2 -0
@@ -0,0 +1,170 @@
1
+ # AAAC verb-specific debug profiles — expected phases and log events
2
+
3
+ version: 1
4
+
5
+ create:
6
+ description: Create verbs — discover → investigate_lite → plan → gates → execute → verify → report.
7
+ phases:
8
+ - discover
9
+ - investigate_lite
10
+ - plan
11
+ - validate
12
+ - impact_analysis
13
+ - dependency_graph
14
+ - fitness_functions
15
+ - rollback
16
+ - execute
17
+ - verify
18
+ - report
19
+ swarm_minimums:
20
+ discover: 4
21
+ expected_events:
22
+ discover:
23
+ - command_parsed
24
+ - run_created
25
+ - graph_resolved
26
+ - phase_start
27
+ - agent_spawned
28
+ - phase_complete
29
+ investigate_lite:
30
+ - phase_start
31
+ - skill_loaded
32
+ - phase_complete
33
+ plan:
34
+ - phase_start
35
+ - skill_loaded
36
+ - phase_complete
37
+ validate:
38
+ - phase_start
39
+ - gate_pass
40
+ - phase_complete
41
+ execute:
42
+ - phase_start
43
+ - execute_start
44
+ - edit_allowed
45
+ - execute_complete
46
+ - phase_complete
47
+
48
+ update:
49
+ description: Update verbs mirror create — discover → investigate_lite → plan → gates → execute.
50
+ phases:
51
+ - discover
52
+ - investigate_lite
53
+ - plan
54
+ - validate
55
+ - impact_analysis
56
+ - dependency_graph
57
+ - fitness_functions
58
+ - rollback
59
+ - execute
60
+ - verify
61
+ - report
62
+ swarm_minimums:
63
+ discover: 4
64
+ expected_events:
65
+ discover:
66
+ - command_parsed
67
+ - run_created
68
+ - graph_resolved
69
+ - phase_start
70
+ - agent_spawned
71
+ - phase_complete
72
+ investigate_lite:
73
+ - phase_start
74
+ - skill_loaded
75
+ - phase_complete
76
+ plan:
77
+ - phase_start
78
+ - skill_loaded
79
+ - phase_complete
80
+ validate:
81
+ - phase_start
82
+ - gate_pass
83
+ - phase_complete
84
+ execute:
85
+ - phase_start
86
+ - execute_start
87
+ - edit_allowed
88
+ - execute_complete
89
+ - phase_complete
90
+
91
+ fix:
92
+ description: Fix verbs — investigate_swarm → root_cause → plan → gates → execute → verify_fix swarm.
93
+ phases:
94
+ - discover
95
+ - investigate_swarm
96
+ - root_cause
97
+ - plan
98
+ - validate
99
+ - impact_analysis
100
+ - dependency_graph
101
+ - fitness_functions
102
+ - rollback
103
+ - execute
104
+ - verify
105
+ - report
106
+ swarm_minimums:
107
+ discover: 4
108
+ investigate_swarm: 7
109
+ verify: 3
110
+ expected_events:
111
+ discover:
112
+ - command_parsed
113
+ - run_created
114
+ - graph_resolved
115
+ - phase_start
116
+ - agent_spawned
117
+ - phase_complete
118
+ investigate_swarm:
119
+ - phase_start
120
+ - agent_spawned
121
+ - skill_loaded
122
+ - phase_complete
123
+ root_cause:
124
+ - phase_start
125
+ - skill_loaded
126
+ - phase_complete
127
+ verify:
128
+ - phase_start
129
+ - agent_spawned
130
+ - phase_complete
131
+ execute:
132
+ - phase_start
133
+ - execute_start
134
+ - edit_allowed
135
+ - edit_denied
136
+ - execute_complete
137
+ - phase_complete
138
+
139
+ check:
140
+ description: Check verbs — readonly discover → gates → report (no execute).
141
+ phases:
142
+ - discover
143
+ - validate
144
+ - impact_analysis
145
+ - dependency_graph
146
+ - fitness_functions
147
+ - report
148
+ swarm_minimums:
149
+ discover: 4
150
+ check_swarm: 3
151
+ expected_events:
152
+ discover:
153
+ - command_parsed
154
+ - run_created
155
+ - graph_resolved
156
+ - phase_start
157
+ - agent_spawned
158
+ - phase_complete
159
+ validate:
160
+ - phase_start
161
+ - gate_pass
162
+ - phase_complete
163
+ report:
164
+ - phase_start
165
+ - skill_loaded
166
+ - phase_complete
167
+ readonly: true
168
+ notes: >-
169
+ Orchestrator runs check_swarm (3 explore agents) during discover.
170
+ Edits always denied — no execute phase.
@@ -122,6 +122,13 @@
122
122
  "requirements": 0.8,
123
123
  "scope": 0.8
124
124
  },
125
+ "complexity_ref": "aaac/complexity.yaml",
126
+ "complexity_strategy": {
127
+ "optimization": "capability_per_complexity",
128
+ "default_verb_when_ambiguous": "update",
129
+ "mutating_verbs": ["create", "update", "fix"],
130
+ "priority": ["reuse", "extend", "modify", "create"]
131
+ },
125
132
  "object_maturity": {
126
133
  "function": "evolving",
127
134
  "component": "evolving",
@@ -214,6 +221,8 @@
214
221
  "create-form": "create-component",
215
222
  "update-page": "update-component",
216
223
  "review-flow": "review-workflow",
217
- "update-flow": "update-workflow"
224
+ "update-flow": "update-workflow",
225
+ "module-fix": "fix-module",
226
+ "bug-fix": "fix-bug"
218
227
  }
219
228
  }
@@ -2,6 +2,8 @@
2
2
 
3
3
  **Every AAAC command executes within a Run.** There is no standalone lifecycle execution or standalone logging.
4
4
 
5
+ **Hook enforcement:** [.cursor/hooks.json](../../hooks.json) + [enforcement.json](../enforcement.json) block code edits until `execute`. Registry: [runtime-registry.json](../runtime-registry.json).
6
+
5
7
  Schema: [schema.json](schema.json)
6
8
 
7
9
  ## Create Run (dispatch step 2.5 — after graph resolve, before orchestrator)
@@ -71,6 +71,15 @@
71
71
  "rollback",
72
72
  "report"
73
73
  ],
74
+ "plan_complexity_fields": [
75
+ "requirement_map",
76
+ "complexity_score",
77
+ "complexity_breakdown",
78
+ "reuse",
79
+ "modify",
80
+ "create",
81
+ "rejected_alternatives"
82
+ ],
74
83
  "resume": {
75
84
  "read_manifest": "state/runs/{run_id}/run.json",
76
85
  "continue_from": "phase field when status running or blocked with user approval"
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ /** Emit .cursor/aaac/runtime-registry.json from ontology + lifecycle — SSOT for hooks */
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import { fileURLToPath } from "url";
6
+
7
+ const aaac = path.join(
8
+ path.dirname(fileURLToPath(import.meta.url)),
9
+ "..",
10
+ );
11
+
12
+ const ontology = JSON.parse(
13
+ fs.readFileSync(path.join(aaac, "ontology.json"), "utf8"),
14
+ );
15
+ const lifecycle = JSON.parse(
16
+ fs.readFileSync(path.join(aaac, "lifecycle/lifecycle.json"), "utf8"),
17
+ );
18
+ const phases = JSON.parse(
19
+ fs.readFileSync(path.join(aaac, "lifecycle/phases.json"), "utf8"),
20
+ );
21
+ const gates = JSON.parse(
22
+ fs.readFileSync(path.join(aaac, "governance/gates.json"), "utf8"),
23
+ );
24
+
25
+ function composeRuntimePhases(verbDef) {
26
+ const work = verbDef.work_phases ?? [];
27
+ if (!verbDef.gate_stack) return work;
28
+ const gatePhases = gates.stacks[verbDef.gate_stack] ?? [];
29
+ const executeIdx = work.indexOf("execute");
30
+ if (executeIdx === -1) {
31
+ const reportIdx = work.indexOf("report");
32
+ if (reportIdx <= 0) {
33
+ return work.length <= 1
34
+ ? [...work, ...gatePhases]
35
+ : [...work.slice(0, -1), ...gatePhases, work[work.length - 1]];
36
+ }
37
+ return [...work.slice(0, reportIdx), ...gatePhases, ...work.slice(reportIdx)];
38
+ }
39
+ return [...work.slice(0, executeIdx), ...gatePhases, ...work.slice(executeIdx)];
40
+ }
41
+
42
+ const gatePhases = {};
43
+ for (const [id, def] of Object.entries(phases.phases ?? {})) {
44
+ if (def.gate) gatePhases[id] = true;
45
+ }
46
+
47
+ const commands = {};
48
+ const aliases = { ...(ontology.command_aliases ?? {}) };
49
+
50
+ for (const [cmd, entry] of Object.entries(ontology.command_overrides ?? {})) {
51
+ if (entry.alias) continue;
52
+ let pending;
53
+ let gate_stack = null;
54
+ if (lifecycle.workflows?.[cmd]) {
55
+ pending = composeRuntimePhases(lifecycle.workflows[cmd]);
56
+ gate_stack = lifecycle.workflows[cmd].gate_stack ?? null;
57
+ } else {
58
+ const verb = cmd.split("-")[0];
59
+ const verbDef = lifecycle.verbs?.[verb];
60
+ if (verbDef) {
61
+ pending = composeRuntimePhases(verbDef);
62
+ gate_stack = verbDef.gate_stack ?? null;
63
+ }
64
+ }
65
+ if (!pending) continue;
66
+ commands[cmd] = {
67
+ verb: cmd.split("-")[0],
68
+ object: cmd.includes("-") ? cmd.split("-").slice(1).join("-") : null,
69
+ orchestrator: entry.orchestrator ?? entry.resolver ?? null,
70
+ resolver: entry.resolver ?? null,
71
+ pending,
72
+ gate_stack,
73
+ };
74
+ }
75
+
76
+ for (const verb of Object.keys(ontology.verbs ?? {})) {
77
+ for (const object of Object.keys(ontology.objects ?? {})) {
78
+ const cmd = `${verb}-${object}`;
79
+ if (commands[cmd]) continue;
80
+ if (
81
+ (ontology.invalid_pairs ?? []).some(([v, o]) => v === verb && o === object)
82
+ ) {
83
+ continue;
84
+ }
85
+ const verbDef = lifecycle.verbs?.[verb];
86
+ if (!verbDef) continue;
87
+ commands[cmd] = {
88
+ verb,
89
+ object,
90
+ orchestrator: verb === "check" ? "verb-check" : `verb-${verb}`,
91
+ pending: composeRuntimePhases(verbDef),
92
+ gate_stack: verbDef.gate_stack ?? null,
93
+ };
94
+ }
95
+ }
96
+
97
+ for (const [alias, canonical] of Object.entries(aliases)) {
98
+ if (commands[canonical] && !commands[alias]) {
99
+ commands[alias] = { ...commands[canonical], alias_of: canonical };
100
+ }
101
+ }
102
+
103
+ const out = {
104
+ version: 1,
105
+ generated_at: new Date().toISOString(),
106
+ aliases,
107
+ gate_phases: gatePhases,
108
+ commands,
109
+ };
110
+
111
+ fs.writeFileSync(
112
+ path.join(aaac, "runtime-registry.json"),
113
+ `${JSON.stringify(out, null, 2)}\n`,
114
+ );
115
+ console.log(`Wrote runtime-registry.json (${Object.keys(commands).length} commands)`);
@@ -0,0 +1,192 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Advance Run to next phase. Validates swarm counts + required artifacts.
4
+ * Usage: node advance-phase.mjs <run_id> <completed_phase> [--force]
5
+ */
6
+ import fs from "fs";
7
+ import path from "path";
8
+ import {
9
+ loadRegistry,
10
+ loadEnforcement,
11
+ loadRunManifest,
12
+ runDir,
13
+ isoNow,
14
+ phaseKind,
15
+ isEditPhase,
16
+ isGatePhase,
17
+ writeJson,
18
+ saveActiveRun,
19
+ } from "./lib.mjs";
20
+ import { recordLog } from "./log.mjs";
21
+
22
+ const runId = process.argv[2];
23
+ const completedPhase = process.argv[3];
24
+ const force = process.argv.includes("--force");
25
+
26
+ if (!runId || !completedPhase) {
27
+ console.error("Usage: advance-phase.mjs <run_id> <completed_phase> [--force]");
28
+ process.exit(1);
29
+ }
30
+
31
+ const registry = loadRegistry();
32
+ const enforcement = loadEnforcement();
33
+ const manifestPath = path.join(runDir(runId), "run.json");
34
+ const manifest = loadRunManifest(runId);
35
+
36
+ if (!manifest) {
37
+ console.error(`Run not found: ${runId}`);
38
+ process.exit(1);
39
+ }
40
+
41
+ if (manifest.phase !== completedPhase) {
42
+ console.error(
43
+ `Phase mismatch: current=${manifest.phase} completed=${completedPhase}`,
44
+ );
45
+ process.exit(1);
46
+ }
47
+
48
+ const minAgents =
49
+ completedPhase === "verify" &&
50
+ (enforcement.fix_commands?.includes(manifest.command) || manifest.verb === "fix")
51
+ ? enforcement.swarm_min_agents?.verify_fix
52
+ : enforcement.swarm_min_agents?.[completedPhase];
53
+ const launches = manifest.swarm?.task_launches_this_phase ?? 0;
54
+ if (minAgents && launches < minAgents && !force) {
55
+ recordLog(manifest, {
56
+ event: "gate_fail",
57
+ phase: completedPhase,
58
+ phase_kind: manifest.phase_kind,
59
+ detail: `swarm incomplete: ${launches}/${minAgents} agents`,
60
+ level: "warn",
61
+ });
62
+ manifest.updated_at = isoNow();
63
+ writeJson(manifestPath, manifest);
64
+ console.error(
65
+ `Swarm incomplete: phase ${completedPhase} requires ${minAgents} Task agents, got ${launches}. Launch parallel Task subagents first.`,
66
+ );
67
+ process.exit(2);
68
+ }
69
+
70
+ const requiredArtifacts = enforcement.phase_artifacts?.[completedPhase] ?? [];
71
+ for (const rel of requiredArtifacts) {
72
+ const artifactPath = path.join(runDir(runId), rel);
73
+ if (!fs.existsSync(artifactPath)) {
74
+ recordLog(manifest, {
75
+ event: "gate_fail",
76
+ phase: completedPhase,
77
+ phase_kind: manifest.phase_kind,
78
+ detail: `missing artifact: ${rel}`,
79
+ level: "warn",
80
+ });
81
+ manifest.updated_at = isoNow();
82
+ writeJson(manifestPath, manifest);
83
+ console.error(`Missing artifact: ${rel} (required before leaving ${completedPhase})`);
84
+ process.exit(2);
85
+ }
86
+ }
87
+
88
+ const now = isoNow();
89
+ const completedIsGate = isGatePhase(completedPhase, registry);
90
+
91
+ if (completedIsGate) {
92
+ recordLog(manifest, {
93
+ event: "gate_pass",
94
+ phase: completedPhase,
95
+ phase_kind: "gate",
96
+ detail: "gate phase completed",
97
+ level: "info",
98
+ });
99
+ manifest.gates = manifest.gates ?? { stack: null, results: {} };
100
+ manifest.gates.results[completedPhase] = "pass";
101
+ }
102
+
103
+ if (completedPhase === "execute") {
104
+ recordLog(manifest, {
105
+ event: "execute_complete",
106
+ phase: completedPhase,
107
+ phase_kind: "work",
108
+ detail: "execute phase completed",
109
+ level: "info",
110
+ });
111
+ }
112
+
113
+ manifest.completed.push(completedPhase);
114
+ recordLog(manifest, {
115
+ event: "phase_complete",
116
+ phase: completedPhase,
117
+ phase_kind: manifest.phase_kind,
118
+ detail: minAgents ? `swarm_count=${launches}` : "ok",
119
+ level: "info",
120
+ });
121
+
122
+ const nextPhase = manifest.pending.shift() ?? null;
123
+ if (!nextPhase) {
124
+ manifest.status = "completed";
125
+ manifest.phase = "report";
126
+ manifest.enforcement.edit_allowed = false;
127
+ recordLog(manifest, {
128
+ event: "run_completed",
129
+ phase: "report",
130
+ phase_kind: "work",
131
+ detail: "all phases completed",
132
+ level: "info",
133
+ });
134
+ } else {
135
+ manifest.phase = nextPhase;
136
+ manifest.phase_kind = phaseKind(nextPhase, registry);
137
+ manifest.swarm = { task_launches_this_phase: 0, phase: nextPhase };
138
+ manifest.enforcement.edit_allowed = isEditPhase(nextPhase, enforcement);
139
+
140
+ recordLog(manifest, {
141
+ event: "phase_start",
142
+ phase: nextPhase,
143
+ phase_kind: manifest.phase_kind,
144
+ detail: "advanced",
145
+ level: "info",
146
+ });
147
+
148
+ if (nextPhase === "execute") {
149
+ recordLog(manifest, {
150
+ event: "execute_start",
151
+ phase: nextPhase,
152
+ phase_kind: "work",
153
+ detail: "edit phase unlocked",
154
+ level: "info",
155
+ });
156
+ }
157
+
158
+ if (isGatePhase(nextPhase, registry)) {
159
+ recordLog(manifest, {
160
+ event: "gate_blocked",
161
+ phase: nextPhase,
162
+ phase_kind: "gate",
163
+ detail: "awaiting gate evaluation",
164
+ level: "debug",
165
+ });
166
+ }
167
+ }
168
+
169
+ manifest.updated_at = now;
170
+ writeJson(manifestPath, manifest);
171
+
172
+ saveActiveRun(manifest.conversation_id ?? null, {
173
+ run_id: runId,
174
+ conversation_id: manifest.conversation_id ?? null,
175
+ command: manifest.command,
176
+ phase: manifest.phase,
177
+ status: manifest.status,
178
+ task_launches_this_phase: 0,
179
+ edit_allowed: manifest.enforcement.edit_allowed,
180
+ started_at: manifest.created_at,
181
+ });
182
+
183
+ console.log(
184
+ JSON.stringify({
185
+ ok: true,
186
+ run_id: runId,
187
+ completed: completedPhase,
188
+ phase: manifest.phase,
189
+ status: manifest.status,
190
+ edit_allowed: manifest.enforcement.edit_allowed,
191
+ }),
192
+ );
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ /** One-shot Run status. Usage: debug-run.mjs <run_id> [--json] */
3
+ import { loadRunManifest } from "./lib.mjs";
4
+ import { debugRunSummary } from "./log.mjs";
5
+
6
+ const runId = process.argv[2];
7
+ const asJson = process.argv.includes("--json");
8
+
9
+ if (!runId) {
10
+ console.error("Usage: debug-run.mjs <run_id> [--json]");
11
+ process.exit(1);
12
+ }
13
+
14
+ const manifest = loadRunManifest(runId);
15
+ if (!manifest) {
16
+ console.error(`Run not found: ${runId}`);
17
+ process.exit(1);
18
+ }
19
+
20
+ const summary = debugRunSummary(manifest);
21
+
22
+ if (asJson) {
23
+ console.log(JSON.stringify(summary, null, 2));
24
+ process.exit(0);
25
+ }
26
+
27
+ console.log(`Run: ${summary.run_id}`);
28
+ console.log(`Command: /${summary.command} verb=${summary.verb} status=${summary.status}`);
29
+ console.log(`Phase: ${summary.phase} (${summary.phase_kind}) edit_allowed=${summary.edit_allowed}`);
30
+ if (summary.blocked_reason) console.log(`Blocked: ${summary.blocked_reason}`);
31
+ console.log(`Completed: ${summary.completed.join(" → ") || "(none)"}`);
32
+ console.log(`Pending: ${summary.pending.join(" → ") || "(none)"}`);
33
+ console.log(`Swarm: phase=${summary.swarm.phase} count=${summary.swarm.task_launches_this_phase}`);
34
+ console.log(`Log: ${summary.log_count} entries Decisions: ${summary.decisions_count}`);
35
+ console.log("--- last 10 log entries ---");
36
+ for (const e of summary.last_log_entries) {
37
+ console.log(`${e.at} ${e.phase} :: ${e.event} — ${e.detail}`);
38
+ }
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ /** preToolUse — deny code edits outside execute phase for THIS chat only. */
3
+ import path from "path";
4
+ import {
5
+ loadActiveRun,
6
+ loadRunManifest,
7
+ loadEnforcement,
8
+ isEditPhase,
9
+ isArtifactPath,
10
+ conversationIdFromHook,
11
+ runDir,
12
+ writeJson,
13
+ isoNow,
14
+ } from "./lib.mjs";
15
+ import { recordLog } from "./log.mjs";
16
+
17
+ let input = "";
18
+ process.stdin.setEncoding("utf8");
19
+ process.stdin.on("data", (c) => (input += c));
20
+ process.stdin.on("end", () => {
21
+ const deny = (userMessage, agentMessage, detail) => {
22
+ console.log(JSON.stringify({ permission: "deny", user_message: userMessage, agent_message: agentMessage }));
23
+ process.exit(0);
24
+ };
25
+ const allow = () => {
26
+ console.log(JSON.stringify({ permission: "allow" }));
27
+ process.exit(0);
28
+ };
29
+
30
+ const persistEditEvent = (manifest, runId, event, detail) => {
31
+ recordLog(manifest, {
32
+ event,
33
+ phase: manifest.phase,
34
+ phase_kind: manifest.phase_kind,
35
+ detail,
36
+ level: "debug",
37
+ });
38
+ manifest.updated_at = isoNow();
39
+ writeJson(path.join(runDir(runId), "run.json"), manifest);
40
+ };
41
+
42
+ let hook;
43
+ try {
44
+ hook = JSON.parse(input || "{}");
45
+ } catch {
46
+ allow();
47
+ }
48
+
49
+ const toolName = hook.tool_name ?? hook.toolName ?? "";
50
+ if (!/^(Write|StrReplace|Delete|EditNotebook|ApplyPatch)$/i.test(toolName)) allow();
51
+
52
+ const conversationId = conversationIdFromHook(hook);
53
+ if (!conversationId) allow();
54
+
55
+ const active = loadActiveRun(conversationId);
56
+ if (!active?.run_id || active.status === "completed") allow();
57
+
58
+ const manifest = loadRunManifest(active.run_id);
59
+ if (!manifest || manifest.status === "completed") allow();
60
+ if (manifest.conversation_id && manifest.conversation_id !== conversationId) allow();
61
+
62
+ const enforcement = loadEnforcement();
63
+ const filePath =
64
+ hook.tool_input?.path ?? hook.toolInput?.path ?? hook.tool_input?.file_path ?? hook.arguments?.path ?? "";
65
+
66
+ if (filePath && isArtifactPath(filePath, enforcement)) {
67
+ persistEditEvent(manifest, active.run_id, "edit_allowed", `artifact path: ${filePath}`);
68
+ allow();
69
+ }
70
+
71
+ if (manifest.awaiting_approval || manifest.status === "blocked") {
72
+ persistEditEvent(manifest, active.run_id, "edit_denied", `blocked at gate: ${manifest.blocked_reason ?? "approval"}`);
73
+ deny(`AAAC Run ${active.run_id} blocked at gate.`, `Run blocked. Run: ${active.run_id}`);
74
+ }
75
+
76
+ if (isEditPhase(manifest.phase, enforcement)) {
77
+ persistEditEvent(manifest, active.run_id, "edit_allowed", `${toolName} in phase ${manifest.phase}`);
78
+ allow();
79
+ }
80
+ if (enforcement.artifact_write_phases?.includes(manifest.phase) && filePath) {
81
+ persistEditEvent(manifest, active.run_id, "edit_allowed", `artifact_write phase ${manifest.phase}`);
82
+ allow();
83
+ }
84
+
85
+ persistEditEvent(
86
+ manifest,
87
+ active.run_id,
88
+ "edit_denied",
89
+ `${toolName} blocked in phase ${manifest.phase}${filePath ? `: ${filePath}` : ""}`,
90
+ );
91
+ deny(
92
+ `AAAC: edits blocked in phase "${manifest.phase}" (this chat). Run: ${active.run_id}`,
93
+ `Cannot ${toolName} during "${manifest.phase}". Chat ${conversationId}. Advance phase first.`,
94
+ );
95
+ });