agentxchain 0.8.7 → 2.1.1
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.
- package/README.md +123 -154
- package/bin/agentxchain.js +240 -8
- package/dashboard/app.js +305 -0
- package/dashboard/components/blocked.js +145 -0
- package/dashboard/components/cross-repo.js +126 -0
- package/dashboard/components/gate.js +311 -0
- package/dashboard/components/hooks.js +177 -0
- package/dashboard/components/initiative.js +147 -0
- package/dashboard/components/ledger.js +165 -0
- package/dashboard/components/timeline.js +222 -0
- package/dashboard/index.html +352 -0
- package/package.json +16 -7
- package/scripts/agentxchain-autonudge.applescript +32 -5
- package/scripts/live-api-proxy-preflight-smoke.sh +531 -0
- package/scripts/publish-from-tag.sh +88 -0
- package/scripts/release-postflight.sh +231 -0
- package/scripts/release-preflight.sh +167 -0
- package/scripts/run-autonudge.sh +1 -1
- package/src/adapters/claude-code.js +7 -14
- package/src/adapters/cursor-local.js +17 -16
- package/src/commands/accept-turn.js +160 -0
- package/src/commands/approve-completion.js +80 -0
- package/src/commands/approve-transition.js +85 -0
- package/src/commands/branch.js +2 -2
- package/src/commands/claim.js +84 -9
- package/src/commands/config.js +16 -0
- package/src/commands/dashboard.js +70 -0
- package/src/commands/doctor.js +9 -1
- package/src/commands/init.js +540 -5
- package/src/commands/migrate.js +348 -0
- package/src/commands/multi.js +549 -0
- package/src/commands/plugin.js +157 -0
- package/src/commands/reject-turn.js +204 -0
- package/src/commands/resume.js +389 -0
- package/src/commands/status.js +196 -3
- package/src/commands/step.js +947 -0
- package/src/commands/stop.js +65 -33
- package/src/commands/template-list.js +33 -0
- package/src/commands/template-set.js +279 -0
- package/src/commands/update.js +24 -3
- package/src/commands/validate.js +20 -11
- package/src/commands/verify.js +71 -0
- package/src/commands/watch.js +112 -25
- package/src/lib/adapters/api-proxy-adapter.js +1076 -0
- package/src/lib/adapters/local-cli-adapter.js +337 -0
- package/src/lib/adapters/manual-adapter.js +169 -0
- package/src/lib/blocked-state.js +94 -0
- package/src/lib/config.js +143 -12
- package/src/lib/context-compressor.js +121 -0
- package/src/lib/context-section-parser.js +220 -0
- package/src/lib/coordinator-acceptance.js +428 -0
- package/src/lib/coordinator-config.js +461 -0
- package/src/lib/coordinator-dispatch.js +276 -0
- package/src/lib/coordinator-gates.js +487 -0
- package/src/lib/coordinator-hooks.js +239 -0
- package/src/lib/coordinator-recovery.js +523 -0
- package/src/lib/coordinator-state.js +365 -0
- package/src/lib/cross-repo-context.js +247 -0
- package/src/lib/dashboard/bridge-server.js +284 -0
- package/src/lib/dashboard/file-watcher.js +93 -0
- package/src/lib/dashboard/state-reader.js +96 -0
- package/src/lib/dispatch-bundle.js +568 -0
- package/src/lib/dispatch-manifest.js +252 -0
- package/src/lib/filter-agents.js +12 -0
- package/src/lib/gate-evaluator.js +285 -0
- package/src/lib/generate-vscode.js +158 -68
- package/src/lib/governed-state.js +2139 -0
- package/src/lib/governed-templates.js +145 -0
- package/src/lib/hook-runner.js +788 -0
- package/src/lib/next-owner.js +61 -6
- package/src/lib/normalized-config.js +539 -0
- package/src/lib/notify.js +14 -12
- package/src/lib/plugin-config-schema.js +192 -0
- package/src/lib/plugins.js +692 -0
- package/src/lib/prompt-core.js +108 -0
- package/src/lib/protocol-conformance.js +291 -0
- package/src/lib/reference-conformance-adapter.js +717 -0
- package/src/lib/repo-observer.js +597 -0
- package/src/lib/repo.js +0 -31
- package/src/lib/safe-write.js +44 -0
- package/src/lib/schema.js +189 -0
- package/src/lib/schemas/turn-result.schema.json +205 -0
- package/src/lib/seed-prompt-polling.js +15 -73
- package/src/lib/seed-prompt.js +17 -63
- package/src/lib/token-budget.js +206 -0
- package/src/lib/token-counter.js +27 -0
- package/src/lib/turn-paths.js +67 -0
- package/src/lib/turn-result-validator.js +496 -0
- package/src/lib/validation.js +167 -19
- package/src/lib/verify-command.js +72 -0
- package/src/templates/governed/api-service.json +31 -0
- package/src/templates/governed/cli-tool.json +30 -0
- package/src/templates/governed/generic.json +10 -0
- package/src/templates/governed/web-app.json +30 -0
package/src/commands/init.js
CHANGED
|
@@ -5,6 +5,7 @@ import chalk from 'chalk';
|
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import { CONFIG_FILE, LOCK_FILE, STATE_FILE } from '../lib/config.js';
|
|
7
7
|
import { generateVSCodeFiles } from '../lib/generate-vscode.js';
|
|
8
|
+
import { loadGovernedTemplate, VALID_GOVERNED_TEMPLATE_IDS } from '../lib/governed-templates.js';
|
|
8
9
|
|
|
9
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
11
|
const TEMPLATES_DIR = join(__dirname, '../templates');
|
|
@@ -45,14 +46,534 @@ function loadTemplates() {
|
|
|
45
46
|
return templates;
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
function interpolateTemplateContent(contentTemplate, projectName) {
|
|
50
|
+
return contentTemplate.replaceAll('{{project_name}}', projectName);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function appendPromptOverride(basePrompt, override) {
|
|
54
|
+
if (!override || !override.trim()) return basePrompt;
|
|
55
|
+
return `${basePrompt}\n\n---\n\n## Project-Type-Specific Guidance\n\n${override.trim()}\n`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function appendAcceptanceHints(baseMatrix, acceptanceHints) {
|
|
59
|
+
if (!Array.isArray(acceptanceHints) || acceptanceHints.length === 0) {
|
|
60
|
+
return baseMatrix;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const hintLines = acceptanceHints.map((hint) => `- [ ] ${hint}`).join('\n');
|
|
64
|
+
return `${baseMatrix}\n\n## Template Guidance\n${hintLines}\n`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ── Governed init ───────────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
const GOVERNED_ROLES = {
|
|
70
|
+
pm: {
|
|
71
|
+
title: 'Product Manager',
|
|
72
|
+
mandate: 'Protect user value, scope clarity, and acceptance criteria.',
|
|
73
|
+
write_authority: 'review_only',
|
|
74
|
+
runtime: 'manual-pm'
|
|
75
|
+
},
|
|
76
|
+
dev: {
|
|
77
|
+
title: 'Developer',
|
|
78
|
+
mandate: 'Implement approved work safely and verify behavior.',
|
|
79
|
+
write_authority: 'authoritative',
|
|
80
|
+
runtime: 'local-dev'
|
|
81
|
+
},
|
|
82
|
+
qa: {
|
|
83
|
+
title: 'QA',
|
|
84
|
+
mandate: 'Challenge correctness, acceptance coverage, and ship readiness.',
|
|
85
|
+
write_authority: 'review_only',
|
|
86
|
+
runtime: 'api-qa'
|
|
87
|
+
},
|
|
88
|
+
eng_director: {
|
|
89
|
+
title: 'Engineering Director',
|
|
90
|
+
mandate: 'Resolve tactical deadlocks and enforce technical coherence.',
|
|
91
|
+
write_authority: 'review_only',
|
|
92
|
+
runtime: 'manual-director'
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const GOVERNED_RUNTIMES = {
|
|
97
|
+
'manual-pm': { type: 'manual' },
|
|
98
|
+
'local-dev': { type: 'local_cli', command: ['claude', '--print', '-p', '{prompt}'], cwd: '.', prompt_transport: 'argv' },
|
|
99
|
+
'api-qa': { type: 'api_proxy', provider: 'anthropic', model: 'claude-sonnet-4-6', auth_env: 'ANTHROPIC_API_KEY' },
|
|
100
|
+
'manual-director': { type: 'manual' }
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const GOVERNED_ROUTING = {
|
|
104
|
+
planning: {
|
|
105
|
+
entry_role: 'pm',
|
|
106
|
+
allowed_next_roles: ['pm', 'eng_director', 'human'],
|
|
107
|
+
exit_gate: 'planning_signoff'
|
|
108
|
+
},
|
|
109
|
+
implementation: {
|
|
110
|
+
entry_role: 'dev',
|
|
111
|
+
allowed_next_roles: ['dev', 'qa', 'eng_director', 'human'],
|
|
112
|
+
exit_gate: 'implementation_complete'
|
|
113
|
+
},
|
|
114
|
+
qa: {
|
|
115
|
+
entry_role: 'qa',
|
|
116
|
+
allowed_next_roles: ['dev', 'qa', 'eng_director', 'human'],
|
|
117
|
+
exit_gate: 'qa_ship_verdict'
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const GOVERNED_GATES = {
|
|
122
|
+
planning_signoff: {
|
|
123
|
+
requires_files: ['.planning/PM_SIGNOFF.md', '.planning/ROADMAP.md'],
|
|
124
|
+
requires_human_approval: true
|
|
125
|
+
},
|
|
126
|
+
implementation_complete: {
|
|
127
|
+
requires_verification_pass: true
|
|
128
|
+
},
|
|
129
|
+
qa_ship_verdict: {
|
|
130
|
+
requires_files: ['.planning/acceptance-matrix.md', '.planning/ship-verdict.md'],
|
|
131
|
+
requires_human_approval: true
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
function buildGovernedPrompt(roleId, role) {
|
|
136
|
+
const rolePrompts = {
|
|
137
|
+
pm: buildPmPrompt,
|
|
138
|
+
dev: buildDevPrompt,
|
|
139
|
+
qa: buildQaPrompt,
|
|
140
|
+
eng_director: buildEngDirectorPrompt,
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const builder = rolePrompts[roleId];
|
|
144
|
+
if (builder) return builder(role);
|
|
145
|
+
|
|
146
|
+
// Fallback for custom roles
|
|
147
|
+
return buildGenericPrompt(roleId, role);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function buildPmPrompt(role) {
|
|
151
|
+
return `# Product Manager — Role Prompt
|
|
152
|
+
|
|
153
|
+
You are the Product Manager. Your mandate: **${role.mandate}**
|
|
154
|
+
|
|
155
|
+
## What You Do Each Turn
|
|
156
|
+
|
|
157
|
+
1. **Read the previous turn** (from CONTEXT.md). Understand what was done and what decisions were made.
|
|
158
|
+
2. **Challenge it.** Even if the work looks correct, identify at least one risk, scope gap, or assumption worth questioning. Rubber-stamping violates the protocol.
|
|
159
|
+
3. **Create or refine planning artifacts:**
|
|
160
|
+
- \`.planning/ROADMAP.md\` — what will be built, in what order, with acceptance criteria
|
|
161
|
+
- \`.planning/PM_SIGNOFF.md\` — your formal sign-off when planning is complete
|
|
162
|
+
- \`.planning/acceptance-matrix.md\` — the acceptance criteria checklist for QA
|
|
163
|
+
4. **Propose the next role.** Typically \`dev\` after planning is complete, or \`eng_director\` if there's a technical deadlock.
|
|
164
|
+
|
|
165
|
+
## Planning Phase Exit
|
|
166
|
+
|
|
167
|
+
To exit the planning phase, you must:
|
|
168
|
+
- Ensure \`.planning/PM_SIGNOFF.md\` exists with your explicit sign-off
|
|
169
|
+
- Ensure \`.planning/ROADMAP.md\` exists with clear acceptance criteria
|
|
170
|
+
- Set \`phase_transition_request: "implementation"\` in your turn result
|
|
171
|
+
|
|
172
|
+
The orchestrator will evaluate the gate and may require human approval.
|
|
173
|
+
|
|
174
|
+
## Scope Authority
|
|
175
|
+
|
|
176
|
+
You define **what** gets built and **why**. You do not define **how** — that's the developer's domain. If you disagree with a technical approach, raise an objection with rationale, but do not override implementation decisions.
|
|
177
|
+
|
|
178
|
+
## Acceptance Criteria Quality
|
|
179
|
+
|
|
180
|
+
Every roadmap item must have acceptance criteria that are:
|
|
181
|
+
- **Observable** — can be verified by running code or inspecting output
|
|
182
|
+
- **Specific** — not "works well" but "returns 200 for GET /api/users with valid token"
|
|
183
|
+
- **Complete** — covers happy path, error cases, and edge cases worth testing
|
|
184
|
+
`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function buildDevPrompt(role) {
|
|
188
|
+
return `# Developer — Role Prompt
|
|
189
|
+
|
|
190
|
+
You are the Developer. Your mandate: **${role.mandate}**
|
|
191
|
+
|
|
192
|
+
## What You Do Each Turn
|
|
193
|
+
|
|
194
|
+
1. **Read the previous turn and ROADMAP.md.** Understand what you're building and what the acceptance criteria are.
|
|
195
|
+
2. **Challenge the previous turn.** If the PM's acceptance criteria are ambiguous, flag it. If QA's objections are unfounded, explain why. Never skip this.
|
|
196
|
+
3. **Implement the work.**
|
|
197
|
+
- Write clean, correct code that meets the acceptance criteria
|
|
198
|
+
- Run tests and include the results as verification evidence
|
|
199
|
+
- Accurately list every file you changed in \`files_changed\`
|
|
200
|
+
4. **Verify your work.** Run the test suite, linter, or build command. Record the commands and exit codes in \`verification.machine_evidence\`.
|
|
201
|
+
|
|
202
|
+
## Implementation Rules
|
|
203
|
+
|
|
204
|
+
- Only implement what the roadmap and acceptance criteria require. Do not add unrequested features.
|
|
205
|
+
- If acceptance criteria are unclear, set \`status: "needs_human"\` and explain what needs clarification in \`needs_human_reason\`.
|
|
206
|
+
- If you encounter a technical blocker, set \`status: "blocked"\` and describe it.
|
|
207
|
+
|
|
208
|
+
## Verification Is Mandatory
|
|
209
|
+
|
|
210
|
+
You must run verification commands and report them honestly:
|
|
211
|
+
- \`verification.status\` must be \`"pass"\` only if all commands exited with code 0
|
|
212
|
+
- \`verification.machine_evidence\` must list every command you ran with its actual exit code
|
|
213
|
+
- Do NOT claim \`"pass"\` if you did not run the tests
|
|
214
|
+
|
|
215
|
+
## Phase Transition
|
|
216
|
+
|
|
217
|
+
When your implementation is complete and verified:
|
|
218
|
+
- If the implementation phase gate requires verification pass: ensure tests pass
|
|
219
|
+
- Set \`phase_transition_request: "qa"\` to advance to QA
|
|
220
|
+
- The gate may auto-advance or require human approval depending on config
|
|
221
|
+
|
|
222
|
+
## Artifact Type
|
|
223
|
+
|
|
224
|
+
Your artifact type is \`workspace\` (direct file modifications). The orchestrator will diff your changes against the pre-turn snapshot to verify \`files_changed\` accuracy.
|
|
225
|
+
`;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function buildQaPrompt(role) {
|
|
229
|
+
return `# QA — Role Prompt
|
|
230
|
+
|
|
231
|
+
You are QA. Your mandate: **${role.mandate}**
|
|
232
|
+
|
|
233
|
+
## What You Do Each Turn
|
|
234
|
+
|
|
235
|
+
1. **Read the previous turn, the ROADMAP, and the acceptance matrix.** Understand what was built and what the acceptance criteria are.
|
|
236
|
+
2. **Challenge the implementation.** You MUST raise at least one objection — this is a protocol requirement for review_only roles. If the code is perfect, challenge the test coverage, the edge cases, or the documentation.
|
|
237
|
+
3. **Evaluate against acceptance criteria.** Go through each criterion and determine pass/fail.
|
|
238
|
+
4. **Create review artifacts:**
|
|
239
|
+
- \`.planning/acceptance-matrix.md\` — updated with pass/fail verdicts per criterion
|
|
240
|
+
- \`.planning/ship-verdict.md\` — your overall ship/no-ship recommendation
|
|
241
|
+
|
|
242
|
+
## You Cannot Modify Code
|
|
243
|
+
|
|
244
|
+
You have \`review_only\` write authority. You may NOT modify product files. You may only create/modify files under \`.planning/\` and \`.agentxchain/reviews/\`. Your artifact type must be \`review\`.
|
|
245
|
+
|
|
246
|
+
## Objection Requirement
|
|
247
|
+
|
|
248
|
+
You MUST raise at least one objection in your turn result. An empty \`objections\` array is a protocol violation and will be rejected by the validator. If the work is genuinely excellent, raise a low-severity observation about test coverage, documentation, or future risk.
|
|
249
|
+
|
|
250
|
+
Each objection must have:
|
|
251
|
+
- \`id\`: pattern \`OBJ-NNN\`
|
|
252
|
+
- \`severity\`: \`low\`, \`medium\`, \`high\`, or \`blocking\`
|
|
253
|
+
- \`against_turn_id\`: the turn you're challenging
|
|
254
|
+
- \`statement\`: clear description of the issue
|
|
255
|
+
- \`status\`: \`"raised"\`
|
|
256
|
+
|
|
257
|
+
## Blocking vs. Non-Blocking
|
|
258
|
+
|
|
259
|
+
- \`blocking\` severity means the work cannot ship. Use sparingly and only for real defects.
|
|
260
|
+
- \`high\` severity means significant risk but potentially shippable with mitigation.
|
|
261
|
+
- \`medium\` and \`low\` are observations that improve quality but don't block.
|
|
262
|
+
|
|
263
|
+
## Ship Verdict & Run Completion
|
|
264
|
+
|
|
265
|
+
When you are satisfied the work meets acceptance criteria:
|
|
266
|
+
1. Create \`.planning/ship-verdict.md\` with your verdict
|
|
267
|
+
2. Create/update \`.planning/acceptance-matrix.md\` with all criteria checked
|
|
268
|
+
3. Set \`run_completion_request: true\` in your turn result
|
|
269
|
+
|
|
270
|
+
**Only set \`run_completion_request: true\` when:**
|
|
271
|
+
- All blocking objections from prior turns are resolved
|
|
272
|
+
- The acceptance matrix shows all critical criteria passing
|
|
273
|
+
- \`.planning/ship-verdict.md\` exists with an affirmative verdict
|
|
274
|
+
|
|
275
|
+
**Do NOT set \`run_completion_request: true\` if:**
|
|
276
|
+
- You have unresolved blocking objections
|
|
277
|
+
- Critical acceptance criteria are failing
|
|
278
|
+
- You need the developer to fix issues first (propose \`dev\` as next role instead)
|
|
279
|
+
|
|
280
|
+
## Routing After QA
|
|
281
|
+
|
|
282
|
+
- If issues found → propose \`dev\` as next role (they fix, then you re-review)
|
|
283
|
+
- If ship-ready → set \`run_completion_request: true\`
|
|
284
|
+
- If deadlocked → propose \`eng_director\` or \`human\`
|
|
285
|
+
`;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function buildEngDirectorPrompt(role) {
|
|
289
|
+
return `# Engineering Director — Role Prompt
|
|
290
|
+
|
|
291
|
+
You are the Engineering Director. Your mandate: **${role.mandate}**
|
|
292
|
+
|
|
293
|
+
## When You Are Called
|
|
294
|
+
|
|
295
|
+
You are invoked when the normal PM → Dev → QA loop is stuck:
|
|
296
|
+
- Repeated QA/Dev cycles without convergence
|
|
297
|
+
- Technical disagreement between roles
|
|
298
|
+
- Scope dispute that the PM cannot resolve
|
|
299
|
+
- Budget or timeline pressure requiring trade-offs
|
|
300
|
+
|
|
301
|
+
## What You Do Each Turn
|
|
302
|
+
|
|
303
|
+
1. **Read the full context.** Review the escalation reason, unresolved objections, and the decision history.
|
|
304
|
+
2. **Make a binding decision.** Your role is to break deadlocks, not to add more opinions. State your decision clearly with rationale.
|
|
305
|
+
3. **Challenge what led to the deadlock.** Identify the root cause — unclear acceptance criteria? Wrong technical approach? Scope creep?
|
|
306
|
+
4. **Route back to the appropriate role.** After your decision, the normal loop should resume.
|
|
307
|
+
|
|
308
|
+
## Decision Authority
|
|
309
|
+
|
|
310
|
+
- You may override QA objections if they are unreasonable or out of scope
|
|
311
|
+
- You may override PM scope decisions if they create technical impossibility
|
|
312
|
+
- You may NOT override human decisions — escalate to \`human\` if needed
|
|
313
|
+
- Every override must be recorded as a decision with clear rationale
|
|
314
|
+
|
|
315
|
+
## You Cannot Modify Code
|
|
316
|
+
|
|
317
|
+
You have \`review_only\` write authority. Like QA, you must raise at least one objection (protocol requirement). Your artifact type is \`review\`.
|
|
318
|
+
|
|
319
|
+
## Objection Requirement
|
|
320
|
+
|
|
321
|
+
You MUST raise at least one objection. Typically this will be about the process failure that led to your involvement — why did the loop deadlock? What should be done differently next time?
|
|
322
|
+
|
|
323
|
+
## Escalation to Human
|
|
324
|
+
|
|
325
|
+
If you cannot resolve the deadlock:
|
|
326
|
+
- Set \`status: "needs_human"\`
|
|
327
|
+
- Explain the situation in \`needs_human_reason\`
|
|
328
|
+
- The orchestrator will pause the run for human input
|
|
329
|
+
`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function buildGenericPrompt(roleId, role) {
|
|
333
|
+
return `# ${role.title} — Role Prompt
|
|
334
|
+
|
|
335
|
+
You are the **${role.title}** on this project.
|
|
336
|
+
|
|
337
|
+
**Mandate:** ${role.mandate}
|
|
338
|
+
**Write authority:** ${role.write_authority}
|
|
339
|
+
|
|
340
|
+
## What You Do Each Turn
|
|
341
|
+
|
|
342
|
+
1. Read the previous turn and challenge it explicitly.
|
|
343
|
+
2. Do your work according to your mandate.
|
|
344
|
+
3. Write your structured turn result to the turn-scoped staging path printed by the orchestrator (\`.agentxchain/staging/<turn_id>/turn-result.json\`).
|
|
345
|
+
|
|
346
|
+
## File Access
|
|
347
|
+
|
|
348
|
+
${role.write_authority === 'authoritative'
|
|
349
|
+
? 'You may modify product files directly.'
|
|
350
|
+
: role.write_authority === 'proposed'
|
|
351
|
+
? 'You may propose changes via patches.'
|
|
352
|
+
: 'You may NOT modify product files. Only create review artifacts under `.planning/` and `.agentxchain/reviews/`.'}
|
|
353
|
+
`;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function scaffoldGoverned(dir, projectName, projectId, templateId = 'generic') {
|
|
357
|
+
const template = loadGovernedTemplate(templateId);
|
|
358
|
+
const config = {
|
|
359
|
+
schema_version: '1.0',
|
|
360
|
+
template: template.id,
|
|
361
|
+
project: {
|
|
362
|
+
id: projectId,
|
|
363
|
+
name: projectName,
|
|
364
|
+
default_branch: 'main'
|
|
365
|
+
},
|
|
366
|
+
roles: GOVERNED_ROLES,
|
|
367
|
+
runtimes: GOVERNED_RUNTIMES,
|
|
368
|
+
routing: GOVERNED_ROUTING,
|
|
369
|
+
gates: GOVERNED_GATES,
|
|
370
|
+
budget: {
|
|
371
|
+
per_turn_max_usd: 2.0,
|
|
372
|
+
per_run_max_usd: 50.0,
|
|
373
|
+
on_exceed: 'pause_and_escalate'
|
|
374
|
+
},
|
|
375
|
+
retention: {
|
|
376
|
+
talk_strategy: 'append_only',
|
|
377
|
+
history_strategy: 'jsonl_append_only'
|
|
378
|
+
},
|
|
379
|
+
prompts: {
|
|
380
|
+
pm: '.agentxchain/prompts/pm.md',
|
|
381
|
+
dev: '.agentxchain/prompts/dev.md',
|
|
382
|
+
qa: '.agentxchain/prompts/qa.md',
|
|
383
|
+
eng_director: '.agentxchain/prompts/eng_director.md'
|
|
384
|
+
},
|
|
385
|
+
rules: {
|
|
386
|
+
challenge_required: true,
|
|
387
|
+
max_turn_retries: 2,
|
|
388
|
+
max_deadlock_cycles: 2
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
const state = {
|
|
393
|
+
schema_version: '1.1',
|
|
394
|
+
run_id: null,
|
|
395
|
+
project_id: projectId,
|
|
396
|
+
status: 'idle',
|
|
397
|
+
phase: 'planning',
|
|
398
|
+
accepted_integration_ref: null,
|
|
399
|
+
active_turns: {},
|
|
400
|
+
turn_sequence: 0,
|
|
401
|
+
last_completed_turn_id: null,
|
|
402
|
+
blocked_on: null,
|
|
403
|
+
blocked_reason: null,
|
|
404
|
+
escalation: null,
|
|
405
|
+
queued_phase_transition: null,
|
|
406
|
+
queued_run_completion: null,
|
|
407
|
+
phase_gate_status: {
|
|
408
|
+
planning_signoff: 'pending',
|
|
409
|
+
implementation_complete: 'pending',
|
|
410
|
+
qa_ship_verdict: 'pending'
|
|
411
|
+
},
|
|
412
|
+
budget_reservations: {},
|
|
413
|
+
budget_status: {
|
|
414
|
+
spent_usd: 0,
|
|
415
|
+
remaining_usd: config.budget.per_run_max_usd
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// Create directories
|
|
420
|
+
mkdirSync(join(dir, '.agentxchain', 'staging'), { recursive: true });
|
|
421
|
+
mkdirSync(join(dir, '.agentxchain', 'prompts'), { recursive: true });
|
|
422
|
+
mkdirSync(join(dir, '.agentxchain', 'reviews'), { recursive: true });
|
|
423
|
+
mkdirSync(join(dir, '.agentxchain', 'dispatch'), { recursive: true });
|
|
424
|
+
mkdirSync(join(dir, '.planning'), { recursive: true });
|
|
425
|
+
|
|
426
|
+
// Core governed files
|
|
427
|
+
writeFileSync(join(dir, CONFIG_FILE), JSON.stringify(config, null, 2) + '\n');
|
|
428
|
+
writeFileSync(join(dir, '.agentxchain', 'state.json'), JSON.stringify(state, null, 2) + '\n');
|
|
429
|
+
writeFileSync(join(dir, '.agentxchain', 'history.jsonl'), '');
|
|
430
|
+
writeFileSync(join(dir, '.agentxchain', 'decision-ledger.jsonl'), '');
|
|
431
|
+
|
|
432
|
+
// Prompt templates
|
|
433
|
+
for (const [roleId, role] of Object.entries(GOVERNED_ROLES)) {
|
|
434
|
+
const basePrompt = buildGovernedPrompt(roleId, role);
|
|
435
|
+
const prompt = appendPromptOverride(basePrompt, template.prompt_overrides?.[roleId]);
|
|
436
|
+
writeFileSync(join(dir, '.agentxchain', 'prompts', `${roleId}.md`), prompt);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Planning artifacts
|
|
440
|
+
writeFileSync(join(dir, '.planning', 'PM_SIGNOFF.md'), `# PM Signoff — ${projectName}\n\nApproved: NO\n\n## Discovery Checklist\n- [ ] Target user defined\n- [ ] Core pain point defined\n- [ ] Core workflow defined\n- [ ] MVP scope defined\n- [ ] Out-of-scope list defined\n- [ ] Success metric defined\n\n## Notes for team\n(PM and human add final kickoff notes here.)\n`);
|
|
441
|
+
writeFileSync(join(dir, '.planning', 'ROADMAP.md'), `# Roadmap — ${projectName}\n\n## Phases\n\n| Phase | Goal | Status |\n|-------|------|--------|\n| Planning | Align scope, requirements, acceptance criteria | In progress |\n| Implementation | Build and verify | Pending |\n| QA | Challenge correctness and ship readiness | Pending |\n`);
|
|
442
|
+
const baseAcceptanceMatrix = `# Acceptance Matrix — ${projectName}\n\n| Req # | Requirement | Acceptance criteria | Test status | Last tested | Status |\n|-------|-------------|-------------------|-------------|-------------|--------|\n| (QA fills this from ROADMAP.md) | | | | | |\n`;
|
|
443
|
+
writeFileSync(
|
|
444
|
+
join(dir, '.planning', 'acceptance-matrix.md'),
|
|
445
|
+
appendAcceptanceHints(baseAcceptanceMatrix, template.acceptance_hints)
|
|
446
|
+
);
|
|
447
|
+
writeFileSync(join(dir, '.planning', 'ship-verdict.md'), `# Ship Verdict — ${projectName}\n\n## Verdict: PENDING\n\n## QA Summary\n\n(QA writes the final ship/no-ship assessment here.)\n\n## Open Blockers\n\n(List any blocking issues.)\n\n## Conditions\n\n(List any conditions for shipping.)\n`);
|
|
448
|
+
for (const artifact of template.planning_artifacts) {
|
|
449
|
+
writeFileSync(
|
|
450
|
+
join(dir, '.planning', artifact.filename),
|
|
451
|
+
interpolateTemplateContent(artifact.content_template, projectName)
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// TALK.md
|
|
456
|
+
writeFileSync(join(dir, 'TALK.md'), `# ${projectName} — Team Talk File\n\nCanonical human-readable handoff log for all agents.\n\n---\n\n`);
|
|
457
|
+
|
|
458
|
+
// .gitignore additions
|
|
459
|
+
const gitignorePath = join(dir, '.gitignore');
|
|
460
|
+
const requiredIgnores = ['.env', '.agentxchain/staging/', '.agentxchain/dispatch/'];
|
|
461
|
+
if (!existsSync(gitignorePath)) {
|
|
462
|
+
writeFileSync(gitignorePath, requiredIgnores.join('\n') + '\n');
|
|
463
|
+
} else {
|
|
464
|
+
const existingIgnore = readFileSync(gitignorePath, 'utf8');
|
|
465
|
+
const missing = requiredIgnores.filter(entry => !existingIgnore.split(/\r?\n/).includes(entry));
|
|
466
|
+
if (missing.length > 0) {
|
|
467
|
+
const prefix = existingIgnore.endsWith('\n') ? '' : '\n';
|
|
468
|
+
writeFileSync(gitignorePath, existingIgnore + prefix + missing.join('\n') + '\n');
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return { config, state };
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async function initGoverned(opts) {
|
|
476
|
+
let projectName, folderName;
|
|
477
|
+
const templateId = opts.template || 'generic';
|
|
478
|
+
|
|
479
|
+
if (!VALID_GOVERNED_TEMPLATE_IDS.includes(templateId)) {
|
|
480
|
+
console.error(chalk.red(` Error: Unknown template "${templateId}".`));
|
|
481
|
+
console.error('');
|
|
482
|
+
console.error(' Available templates:');
|
|
483
|
+
console.error(' generic Default governed scaffold');
|
|
484
|
+
console.error(' api-service Governed scaffold for a backend service');
|
|
485
|
+
console.error(' cli-tool Governed scaffold for a CLI tool');
|
|
486
|
+
console.error(' web-app Governed scaffold for a web application');
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
if (opts.yes) {
|
|
491
|
+
projectName = 'My AgentXchain Project';
|
|
492
|
+
folderName = slugify(projectName);
|
|
493
|
+
} else {
|
|
494
|
+
const { name } = await inquirer.prompt([{
|
|
495
|
+
type: 'input',
|
|
496
|
+
name: 'name',
|
|
497
|
+
message: 'Project name:',
|
|
498
|
+
default: 'My AgentXchain Project'
|
|
499
|
+
}]);
|
|
500
|
+
projectName = name;
|
|
501
|
+
folderName = slugify(projectName);
|
|
502
|
+
|
|
503
|
+
const { folder } = await inquirer.prompt([{
|
|
504
|
+
type: 'input',
|
|
505
|
+
name: 'folder',
|
|
506
|
+
message: 'Folder name:',
|
|
507
|
+
default: folderName
|
|
508
|
+
}]);
|
|
509
|
+
folderName = folder;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const dir = resolve(process.cwd(), folderName);
|
|
513
|
+
const projectId = slugify(projectName);
|
|
514
|
+
|
|
515
|
+
if (existsSync(dir) && existsSync(join(dir, CONFIG_FILE))) {
|
|
516
|
+
if (!opts.yes) {
|
|
517
|
+
const { overwrite } = await inquirer.prompt([{
|
|
518
|
+
type: 'confirm',
|
|
519
|
+
name: 'overwrite',
|
|
520
|
+
message: `${folderName}/ already has agentxchain.json. Overwrite?`,
|
|
521
|
+
default: false
|
|
522
|
+
}]);
|
|
523
|
+
if (!overwrite) {
|
|
524
|
+
console.log(chalk.yellow(' Aborted.'));
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
531
|
+
|
|
532
|
+
scaffoldGoverned(dir, projectName, projectId, templateId);
|
|
533
|
+
|
|
534
|
+
console.log('');
|
|
535
|
+
console.log(chalk.green(` ✓ Created governed project ${chalk.bold(folderName)}/`));
|
|
536
|
+
console.log('');
|
|
537
|
+
console.log(` ${chalk.dim('├──')} agentxchain.json ${chalk.dim('(governed)')}`);
|
|
538
|
+
console.log(` ${chalk.dim('├──')} .agentxchain/`);
|
|
539
|
+
console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} state.json / history.jsonl / decision-ledger.jsonl`);
|
|
540
|
+
console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} staging/`);
|
|
541
|
+
console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} prompts/ ${chalk.dim('(pm, dev, qa, eng_director)')}`);
|
|
542
|
+
console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} reviews/`);
|
|
543
|
+
console.log(` ${chalk.dim('│')} ${chalk.dim('└──')} dispatch/`);
|
|
544
|
+
console.log(` ${chalk.dim('├──')} .planning/`);
|
|
545
|
+
console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} PM_SIGNOFF.md / ROADMAP.md`);
|
|
546
|
+
console.log(` ${chalk.dim('│')} ${chalk.dim('└──')} acceptance-matrix.md / ship-verdict.md`);
|
|
547
|
+
console.log(` ${chalk.dim('└──')} TALK.md`);
|
|
548
|
+
console.log('');
|
|
549
|
+
console.log(` ${chalk.dim('Roles:')} pm, dev, qa, eng_director`);
|
|
550
|
+
console.log(` ${chalk.dim('Template:')} ${templateId}`);
|
|
551
|
+
console.log(` ${chalk.dim('Protocol:')} governed convergence`);
|
|
552
|
+
console.log('');
|
|
553
|
+
console.log(` ${chalk.cyan('Next:')}`);
|
|
554
|
+
console.log(` ${chalk.bold(`cd ${folderName}`)}`);
|
|
555
|
+
console.log(` ${chalk.bold('agentxchain step')} ${chalk.dim('# run the first governed turn')}`);
|
|
556
|
+
console.log(` ${chalk.bold('agentxchain status')} ${chalk.dim('# inspect phase, gate, and turn state')}`);
|
|
557
|
+
console.log('');
|
|
558
|
+
}
|
|
559
|
+
|
|
48
560
|
export async function initCommand(opts) {
|
|
561
|
+
if (opts.governed || opts.schemaVersion === '4') {
|
|
562
|
+
return initGoverned(opts);
|
|
563
|
+
}
|
|
564
|
+
|
|
49
565
|
let project, agents, folderName, rules;
|
|
50
566
|
|
|
51
567
|
if (opts.yes) {
|
|
52
568
|
project = 'My AgentXchain project';
|
|
53
569
|
agents = DEFAULT_AGENTS;
|
|
54
570
|
folderName = slugify(project);
|
|
55
|
-
rules = {
|
|
571
|
+
rules = {
|
|
572
|
+
max_consecutive_claims: 2,
|
|
573
|
+
require_message: true,
|
|
574
|
+
compress_after_words: 5000,
|
|
575
|
+
strict_next_owner: false
|
|
576
|
+
};
|
|
56
577
|
} else {
|
|
57
578
|
const templates = loadTemplates();
|
|
58
579
|
|
|
@@ -86,7 +607,12 @@ export async function initCommand(opts) {
|
|
|
86
607
|
project = projectName;
|
|
87
608
|
} else if (template === 'default') {
|
|
88
609
|
agents = DEFAULT_AGENTS;
|
|
89
|
-
rules = {
|
|
610
|
+
rules = {
|
|
611
|
+
max_consecutive_claims: 2,
|
|
612
|
+
require_message: true,
|
|
613
|
+
compress_after_words: 5000,
|
|
614
|
+
strict_next_owner: false
|
|
615
|
+
};
|
|
90
616
|
const { projectName } = await inquirer.prompt([{
|
|
91
617
|
type: 'input',
|
|
92
618
|
name: 'projectName',
|
|
@@ -103,7 +629,12 @@ export async function initCommand(opts) {
|
|
|
103
629
|
}]);
|
|
104
630
|
project = projectName;
|
|
105
631
|
agents = {};
|
|
106
|
-
rules = {
|
|
632
|
+
rules = {
|
|
633
|
+
max_consecutive_claims: 2,
|
|
634
|
+
require_message: true,
|
|
635
|
+
compress_after_words: 5000,
|
|
636
|
+
strict_next_owner: false
|
|
637
|
+
};
|
|
107
638
|
|
|
108
639
|
const { count } = await inquirer.prompt([{
|
|
109
640
|
type: 'number',
|
|
@@ -192,6 +723,7 @@ export async function initCommand(opts) {
|
|
|
192
723
|
compress_after_words: rules.compress_after_words || 5000,
|
|
193
724
|
ttl_minutes: rules.ttl_minutes || 10,
|
|
194
725
|
watch_interval_ms: rules.watch_interval_ms || 5000,
|
|
726
|
+
strict_next_owner: rules.strict_next_owner === true,
|
|
195
727
|
...(rules.verify_command ? { verify_command: rules.verify_command } : {})
|
|
196
728
|
}
|
|
197
729
|
};
|
|
@@ -209,7 +741,7 @@ export async function initCommand(opts) {
|
|
|
209
741
|
writeFileSync(join(dir, 'TALK.md'), `# ${project} — Team Talk File\n\nCanonical human-readable handoff log for all agents.\n\n## How to write entries\n\nUse this exact structure:\n\n## Turn N — <agent_id> (<role>)\n- Status:\n- Decision:\n- Action:\n- Risks/Questions:\n- Next owner:\n\n---\n\n`);
|
|
210
742
|
writeFileSync(join(dir, 'HUMAN_TASKS.md'), '# Human Tasks\n\n(Agents append tasks here when they need human action.)\n');
|
|
211
743
|
const gitignorePath = join(dir, '.gitignore');
|
|
212
|
-
const requiredIgnores = ['.env', '.agentxchain-trigger.json', '.agentxchain-prompts/', '.agentxchain-workspaces/'];
|
|
744
|
+
const requiredIgnores = ['.env', '.agentxchain-trigger.json', '.agentxchain-prompts/', '.agentxchain-workspaces/', '.agentxchain-watch.pid', '.agentxchain-autonudge.state'];
|
|
213
745
|
if (!existsSync(gitignorePath)) {
|
|
214
746
|
writeFileSync(gitignorePath, requiredIgnores.join('\n') + '\n');
|
|
215
747
|
} else {
|
|
@@ -230,10 +762,13 @@ export async function initCommand(opts) {
|
|
|
230
762
|
|
|
231
763
|
writeFileSync(join(dir, '.planning', 'REQUIREMENTS.md'), `# Requirements — ${project}\n\n## v1 (MVP)\n\n(PM fills this: numbered list of requirements. Each requirement has one-sentence acceptance criteria.)\n\n| # | Requirement | Acceptance criteria | Phase | Status |\n|---|-------------|-------------------|-------|--------|\n| 1 | | | | Pending |\n\n## v2 (Future)\n\n(Out of scope for MVP. Captured here so they don't creep in.)\n\n## Out of scope\n\n(Explicitly not building.)\n`);
|
|
232
764
|
|
|
233
|
-
writeFileSync(join(dir, '.planning', 'ROADMAP.md'), `# Roadmap — ${project}\n\n## Phases\n\n| Phase | Description | Status | Requirements |\n|-------|-------------|--------|-------------|\n| 1 | Discovery + setup | In progress | — |\n\n(PM updates this as phases are planned and completed.)\n`);
|
|
765
|
+
writeFileSync(join(dir, '.planning', 'ROADMAP.md'), `# Roadmap — ${project}\n\n## Waves\n\n| Wave | Goal | Status |\n|------|------|--------|\n| Wave 1 | Discovery, planning, and phase setup | In progress |\n\n## Phases\n\n| Phase | Description | Status | Requirements |\n|-------|-------------|--------|-------------|\n| 1 | Discovery + setup | In progress | — |\n\n(PM updates this as phases are planned and completed.)\n`);
|
|
234
766
|
writeFileSync(join(dir, '.planning', 'PM_SIGNOFF.md'), `# PM Signoff — ${project}\n\nApproved: NO\n\n## Discovery Checklist\n- [ ] Target user defined\n- [ ] Core pain point defined\n- [ ] Core workflow defined\n- [ ] MVP scope defined\n- [ ] Out-of-scope list defined\n- [ ] Success metric defined\n\n## Notes for team\n(PM and human add final kickoff notes here.)\n`);
|
|
235
767
|
|
|
236
768
|
// QA structure
|
|
769
|
+
mkdirSync(join(dir, '.planning', 'phases', 'phase-1'), { recursive: true });
|
|
770
|
+
writeFileSync(join(dir, '.planning', 'phases', 'phase-1', 'PLAN.md'), `# Phase 1 Plan — ${project}\n\n## Goal\n\nAlign scope, requirements, and initial implementation plan.\n\n## Deliverables\n\n- PM signoff\n- Initial requirements and roadmap\n- First implementation slice\n`);
|
|
771
|
+
writeFileSync(join(dir, '.planning', 'phases', 'phase-1', 'TESTS.md'), `# Phase 1 Tests — ${project}\n\n## Planned checks\n\n- Kickoff validation passes\n- Requirements have acceptance criteria\n- First implementation slice has an executable verification path\n`);
|
|
237
772
|
writeFileSync(join(dir, '.planning', 'qa', 'TEST-COVERAGE.md'), `# Test Coverage — ${project}\n\n## Coverage Map\n\n| Feature / Area | Unit tests | Integration tests | E2E tests | Manual QA | UX audit | Status |\n|---------------|-----------|------------------|----------|----------|---------|--------|\n| (QA fills this as testing progresses) | | | | | | |\n\n## Coverage gaps\n\n(Areas with no tests or insufficient coverage.)\n`);
|
|
238
773
|
|
|
239
774
|
writeFileSync(join(dir, '.planning', 'qa', 'REGRESSION-LOG.md'), `# Regression Log — ${project}\n\nBugs that were found and fixed. Each entry has a regression test to prevent recurrence.\n\n| Bug ID | Description | Found turn | Fixed turn | Regression test | Status |\n|--------|-------------|-----------|-----------|----------------|--------|\n| (QA adds entries as bugs are found and fixed) | | | | | |\n`);
|