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.
Files changed (94) hide show
  1. package/README.md +123 -154
  2. package/bin/agentxchain.js +240 -8
  3. package/dashboard/app.js +305 -0
  4. package/dashboard/components/blocked.js +145 -0
  5. package/dashboard/components/cross-repo.js +126 -0
  6. package/dashboard/components/gate.js +311 -0
  7. package/dashboard/components/hooks.js +177 -0
  8. package/dashboard/components/initiative.js +147 -0
  9. package/dashboard/components/ledger.js +165 -0
  10. package/dashboard/components/timeline.js +222 -0
  11. package/dashboard/index.html +352 -0
  12. package/package.json +16 -7
  13. package/scripts/agentxchain-autonudge.applescript +32 -5
  14. package/scripts/live-api-proxy-preflight-smoke.sh +531 -0
  15. package/scripts/publish-from-tag.sh +88 -0
  16. package/scripts/release-postflight.sh +231 -0
  17. package/scripts/release-preflight.sh +167 -0
  18. package/scripts/run-autonudge.sh +1 -1
  19. package/src/adapters/claude-code.js +7 -14
  20. package/src/adapters/cursor-local.js +17 -16
  21. package/src/commands/accept-turn.js +160 -0
  22. package/src/commands/approve-completion.js +80 -0
  23. package/src/commands/approve-transition.js +85 -0
  24. package/src/commands/branch.js +2 -2
  25. package/src/commands/claim.js +84 -9
  26. package/src/commands/config.js +16 -0
  27. package/src/commands/dashboard.js +70 -0
  28. package/src/commands/doctor.js +9 -1
  29. package/src/commands/init.js +540 -5
  30. package/src/commands/migrate.js +348 -0
  31. package/src/commands/multi.js +549 -0
  32. package/src/commands/plugin.js +157 -0
  33. package/src/commands/reject-turn.js +204 -0
  34. package/src/commands/resume.js +389 -0
  35. package/src/commands/status.js +196 -3
  36. package/src/commands/step.js +947 -0
  37. package/src/commands/stop.js +65 -33
  38. package/src/commands/template-list.js +33 -0
  39. package/src/commands/template-set.js +279 -0
  40. package/src/commands/update.js +24 -3
  41. package/src/commands/validate.js +20 -11
  42. package/src/commands/verify.js +71 -0
  43. package/src/commands/watch.js +112 -25
  44. package/src/lib/adapters/api-proxy-adapter.js +1076 -0
  45. package/src/lib/adapters/local-cli-adapter.js +337 -0
  46. package/src/lib/adapters/manual-adapter.js +169 -0
  47. package/src/lib/blocked-state.js +94 -0
  48. package/src/lib/config.js +143 -12
  49. package/src/lib/context-compressor.js +121 -0
  50. package/src/lib/context-section-parser.js +220 -0
  51. package/src/lib/coordinator-acceptance.js +428 -0
  52. package/src/lib/coordinator-config.js +461 -0
  53. package/src/lib/coordinator-dispatch.js +276 -0
  54. package/src/lib/coordinator-gates.js +487 -0
  55. package/src/lib/coordinator-hooks.js +239 -0
  56. package/src/lib/coordinator-recovery.js +523 -0
  57. package/src/lib/coordinator-state.js +365 -0
  58. package/src/lib/cross-repo-context.js +247 -0
  59. package/src/lib/dashboard/bridge-server.js +284 -0
  60. package/src/lib/dashboard/file-watcher.js +93 -0
  61. package/src/lib/dashboard/state-reader.js +96 -0
  62. package/src/lib/dispatch-bundle.js +568 -0
  63. package/src/lib/dispatch-manifest.js +252 -0
  64. package/src/lib/filter-agents.js +12 -0
  65. package/src/lib/gate-evaluator.js +285 -0
  66. package/src/lib/generate-vscode.js +158 -68
  67. package/src/lib/governed-state.js +2139 -0
  68. package/src/lib/governed-templates.js +145 -0
  69. package/src/lib/hook-runner.js +788 -0
  70. package/src/lib/next-owner.js +61 -6
  71. package/src/lib/normalized-config.js +539 -0
  72. package/src/lib/notify.js +14 -12
  73. package/src/lib/plugin-config-schema.js +192 -0
  74. package/src/lib/plugins.js +692 -0
  75. package/src/lib/prompt-core.js +108 -0
  76. package/src/lib/protocol-conformance.js +291 -0
  77. package/src/lib/reference-conformance-adapter.js +717 -0
  78. package/src/lib/repo-observer.js +597 -0
  79. package/src/lib/repo.js +0 -31
  80. package/src/lib/safe-write.js +44 -0
  81. package/src/lib/schema.js +189 -0
  82. package/src/lib/schemas/turn-result.schema.json +205 -0
  83. package/src/lib/seed-prompt-polling.js +15 -73
  84. package/src/lib/seed-prompt.js +17 -63
  85. package/src/lib/token-budget.js +206 -0
  86. package/src/lib/token-counter.js +27 -0
  87. package/src/lib/turn-paths.js +67 -0
  88. package/src/lib/turn-result-validator.js +496 -0
  89. package/src/lib/validation.js +167 -19
  90. package/src/lib/verify-command.js +72 -0
  91. package/src/templates/governed/api-service.json +31 -0
  92. package/src/templates/governed/cli-tool.json +30 -0
  93. package/src/templates/governed/generic.json +10 -0
  94. package/src/templates/governed/web-app.json +30 -0
@@ -20,11 +20,13 @@ export function generateVSCodeFiles(dir, config) {
20
20
  }
21
21
 
22
22
  writeFileSync(join(hooksDir, 'agentxchain.json'), buildHooksJson());
23
+ writeFileSync(join(scriptsDir, 'agentxchain-hook-runtime.cjs'), buildHookRuntimeScript());
23
24
  writeFileSync(join(scriptsDir, 'agentxchain-session-start.sh'), SESSION_START_SCRIPT);
24
- writeFileSync(join(scriptsDir, 'agentxchain-stop.sh'), buildStopScript(config));
25
+ writeFileSync(join(scriptsDir, 'agentxchain-stop.sh'), buildStopScript());
25
26
  writeFileSync(join(scriptsDir, 'agentxchain-pre-tool.sh'), PRE_TOOL_SCRIPT);
26
27
 
27
28
  try {
29
+ chmodSync(join(scriptsDir, 'agentxchain-hook-runtime.cjs'), 0o755);
28
30
  chmodSync(join(scriptsDir, 'agentxchain-session-start.sh'), 0o755);
29
31
  chmodSync(join(scriptsDir, 'agentxchain-stop.sh'), 0o755);
30
32
  chmodSync(join(scriptsDir, 'agentxchain-pre-tool.sh'), 0o755);
@@ -188,23 +190,150 @@ function buildHooksJson() {
188
190
  return JSON.stringify(hooks, null, 2) + '\n';
189
191
  }
190
192
 
191
- function buildStopScript(config) {
192
- const verifyCmd = config.rules?.verify_command || '';
193
- const verifyBlock = verifyCmd
194
- ? `
195
- # Run verify command before allowing release
196
- if [ -z "$HOLDER" ] || [ "$HOLDER" = "null" ]; then
197
- VERIFY_CMD="${verifyCmd}"
198
- if [ -n "$VERIFY_CMD" ]; then
199
- if ! eval "$VERIFY_CMD" > /dev/null 2>&1; then
200
- echo '{"hookSpecificOutput":{"hookEventName":"Stop","decision":"block","reason":"Verification failed: '"$VERIFY_CMD"'. Fix the issue and release the lock."}}'
201
- exit 0
202
- fi
203
- fi
204
- fi
205
- `
206
- : '';
193
+ function buildHookRuntimeScript() {
194
+ return `#!/usr/bin/env node
195
+ const fs = require('fs');
196
+ const path = require('path');
197
+ const { spawnSync } = require('child_process');
198
+
199
+ function readJson(file) {
200
+ try {
201
+ return JSON.parse(fs.readFileSync(path.join(process.cwd(), file), 'utf8'));
202
+ } catch {
203
+ return null;
204
+ }
205
+ }
206
+
207
+ function splitCommand(input) {
208
+ const out = [];
209
+ let current = '';
210
+ let quote = null;
211
+ let escape = false;
212
+ for (const char of String(input || '')) {
213
+ if (escape) {
214
+ current += char;
215
+ escape = false;
216
+ continue;
217
+ }
218
+ if (char === '\\\\') {
219
+ escape = true;
220
+ continue;
221
+ }
222
+ if (quote) {
223
+ if (char === quote) {
224
+ quote = null;
225
+ } else {
226
+ current += char;
227
+ }
228
+ continue;
229
+ }
230
+ if (char === '"' || char === "'") {
231
+ quote = char;
232
+ continue;
233
+ }
234
+ if (/\\s/.test(char)) {
235
+ if (current) {
236
+ out.push(current);
237
+ current = '';
238
+ }
239
+ continue;
240
+ }
241
+ current += char;
242
+ }
243
+ if (current) out.push(current);
244
+ return out;
245
+ }
246
+
247
+ function normalizeVerifyCommand(config) {
248
+ const raw = config?.rules?.verify_command;
249
+ if (Array.isArray(raw) && raw.every(part => typeof part === 'string' && part.length > 0)) {
250
+ return raw;
251
+ }
252
+ if (typeof raw !== 'string' || !raw.trim()) return null;
253
+ return splitCommand(raw.trim());
254
+ }
255
+
256
+ function normalizeAgentId(raw) {
257
+ if (!raw) return null;
258
+ let value = String(raw).trim();
259
+ value = value.replace(/[\`*_]/g, '').trim();
260
+ value = value.replace(/\\(.*?\\)/g, '').trim();
261
+ value = value.split(/[\\s,]+/)[0];
262
+ value = value.toLowerCase();
263
+ return /^[a-z0-9_-]+$/.test(value) ? value : null;
264
+ }
265
+
266
+ function parseNextOwnerFromTalk(talkPath, validAgentIds) {
267
+ try {
268
+ const text = fs.readFileSync(path.join(process.cwd(), talkPath), 'utf8');
269
+ const lines = text.split(/\\r?\\n/);
270
+ for (let i = lines.length - 1; i >= 0; i -= 1) {
271
+ const line = lines[i].trim();
272
+ const match = line.match(/^(?:-|\\*)?\\s*\\**next\\s*owner\\**\\s*:\\s*(.+)$/i);
273
+ if (!match) continue;
274
+ const candidate = normalizeAgentId(match[1]);
275
+ if (candidate && validAgentIds.includes(candidate)) return candidate;
276
+ }
277
+ } catch {}
278
+ return null;
279
+ }
280
+
281
+ function resolveNextAgent(config, lock) {
282
+ const agentIds = Object.keys(config?.agents || {});
283
+ if (agentIds.length === 0) return null;
284
+ const talkFile = config?.talk_file || 'TALK.md';
285
+ const fromTalk = parseNextOwnerFromTalk(talkFile, agentIds);
286
+ if (fromTalk) return fromTalk;
287
+ const last = lock?.last_released_by;
288
+ if (last && agentIds.includes(last)) {
289
+ const idx = agentIds.indexOf(last);
290
+ return agentIds[(idx + 1) % agentIds.length];
291
+ }
292
+ return agentIds[0];
293
+ }
294
+
295
+ function outputJson(value) {
296
+ process.stdout.write(JSON.stringify(value));
297
+ }
298
+
299
+ const command = process.argv[2];
300
+ const config = readJson('agentxchain.json');
301
+ const lock = readJson('lock.json');
302
+ const state = readJson('state.json');
303
+
304
+ if (command === 'verify') {
305
+ const verifyArgs = normalizeVerifyCommand(config);
306
+ if (!verifyArgs || verifyArgs.length === 0) process.exit(0);
307
+ const result = spawnSync(verifyArgs[0], verifyArgs.slice(1), { stdio: 'ignore', cwd: process.cwd() });
308
+ process.exit(result.status === 0 ? 0 : 2);
309
+ }
310
+
311
+ if (command === 'next') {
312
+ process.stdout.write(resolveNextAgent(config, lock) || '');
313
+ process.exit(0);
314
+ }
315
+
316
+ if (command === 'next-name') {
317
+ const next = resolveNextAgent(config, lock);
318
+ process.stdout.write(next ? (config?.agents?.[next]?.name || next) : '');
319
+ process.exit(0);
320
+ }
321
+
322
+ if (command === 'session-start') {
323
+ if (!lock || !state) {
324
+ outputJson({ continue: true });
325
+ process.exit(0);
326
+ }
327
+ const context = \`AgentXchain context: Project=\${state.project || 'unknown'} | Phase=\${state.phase || 'unknown'} | Turn=\${lock.turn_number ?? 0} | Lock=\${lock.holder ?? 'none'} | Last released by=\${lock.last_released_by ?? 'none'} | Blocked=\${state.blocked ?? false}\`;
328
+ outputJson({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: context } });
329
+ process.exit(0);
330
+ }
331
+
332
+ outputJson({ continue: true });
333
+ `;
334
+ }
207
335
 
336
+ function buildStopScript() {
208
337
  return `#!/bin/bash
209
338
  INPUT=$(cat)
210
339
  STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
@@ -222,51 +351,28 @@ fi
222
351
  LOCK=$(cat lock.json 2>/dev/null)
223
352
  HOLDER=$(echo "$LOCK" | jq -r '.holder // empty')
224
353
  TURN=$(echo "$LOCK" | jq -r '.turn_number // 0')
225
- ${verifyBlock}
226
354
  if [ -z "$HOLDER" ] || [ "$HOLDER" = "null" ]; then
227
- LAST=$(echo "$LOCK" | jq -r '.last_released_by // empty')
355
+ node "./scripts/agentxchain-hook-runtime.cjs" verify >/dev/null 2>&1
356
+ VERIFY_STATUS=$?
357
+ if [ "$VERIFY_STATUS" -eq 2 ]; then
358
+ echo '{"hookSpecificOutput":{"hookEventName":"Stop","decision":"block","reason":"Verification failed. Fix the issue and release the lock."}}'
359
+ exit 0
360
+ fi
361
+ fi
228
362
 
363
+ if [ -z "$HOLDER" ] || [ "$HOLDER" = "null" ]; then
229
364
  if [ ! -f "agentxchain.json" ]; then
230
365
  echo '{"continue":true}'
231
366
  exit 0
232
367
  fi
233
368
 
234
- NEXT=$(node -e "
235
- const fs = require('fs');
236
- const cfg = JSON.parse(fs.readFileSync('agentxchain.json','utf8'));
237
- const ids = Object.keys(cfg.agents || {});
238
- const lock = JSON.parse(fs.readFileSync('lock.json','utf8'));
239
- const talkFile = cfg.talk_file || 'TALK.md';
240
- let fromTalk = '';
241
- try {
242
- const talk = fs.readFileSync(talkFile, 'utf8').split(/\\r?\\n/);
243
- for (let i = talk.length - 1; i >= 0; i -= 1) {
244
- const m = talk[i].trim().match(/^(?:-|\\*)?\\s*\\**next\\s*owner\\**\\s*:\\s*(.+)$/i);
245
- if (!m) continue;
246
- let candidate = String(m[1] || '').replace(/[\\*_]/g, '').replace(/\\(.*?\\)/g, '').trim().split(/[\\s,]+/)[0].toLowerCase();
247
- if (ids.includes(candidate)) { fromTalk = candidate; break; }
248
- }
249
- } catch {}
250
- if (fromTalk) {
251
- process.stdout.write(fromTalk);
252
- process.exit(0);
253
- }
254
- const last = lock.last_released_by || '';
255
- const idx = ids.indexOf(last);
256
- const next = idx >= 0 ? ids[(idx + 1) % ids.length] : ids[0];
257
- process.stdout.write(next || '');
258
- " -- "$LAST" 2>/dev/null)
259
-
369
+ NEXT=$(node "./scripts/agentxchain-hook-runtime.cjs" next 2>/dev/null)
260
370
  if [ -z "$NEXT" ]; then
261
371
  echo '{"continue":true}'
262
372
  exit 0
263
373
  fi
264
374
 
265
- NEXT_NAME=$(node -e "
266
- const cfg = JSON.parse(require('fs').readFileSync('agentxchain.json','utf8'));
267
- const a = cfg.agents[process.argv[1]];
268
- process.stdout.write(a ? a.name : process.argv[1]);
269
- " -- "$NEXT" 2>/dev/null)
375
+ NEXT_NAME=$(node "./scripts/agentxchain-hook-runtime.cjs" next-name 2>/dev/null)
270
376
 
271
377
  echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"Stop\\",\\"decision\\":\\"block\\",\\"reason\\":\\"Turn $TURN complete. Next agent: $NEXT ($NEXT_NAME). Read lock.json, claim it, and do your work.\\"}}"
272
378
  elif [ "$HOLDER" = "human" ]; then
@@ -278,24 +384,8 @@ fi
278
384
  }
279
385
 
280
386
  const SESSION_START_SCRIPT = `#!/bin/bash
281
- if [ ! -f "lock.json" ] || [ ! -f "state.json" ]; then
282
- echo '{"continue":true}'
283
- exit 0
284
- fi
285
-
286
- LOCK=$(cat lock.json 2>/dev/null)
287
- STATE=$(cat state.json 2>/dev/null)
288
-
289
- HOLDER=$(echo "$LOCK" | jq -r '.holder // "none"')
290
- TURN=$(echo "$LOCK" | jq -r '.turn_number // 0')
291
- LAST=$(echo "$LOCK" | jq -r '.last_released_by // "none"')
292
- PHASE=$(echo "$STATE" | jq -r '.phase // "unknown"')
293
- BLOCKED=$(echo "$STATE" | jq -r '.blocked // false')
294
- PROJECT=$(echo "$STATE" | jq -r '.project // "unknown"')
295
-
296
- CONTEXT="AgentXchain context: Project=$PROJECT | Phase=$PHASE | Turn=$TURN | Lock=$HOLDER | Last released by=$LAST | Blocked=$BLOCKED"
297
-
298
- echo "{\\"hookSpecificOutput\\":{\\"hookEventName\\":\\"SessionStart\\",\\"additionalContext\\":\\"$CONTEXT\\"}}"
387
+ CONTEXT_JSON=$(node "./scripts/agentxchain-hook-runtime.cjs" session-start 2>/dev/null || echo '{"continue":true}')
388
+ printf '%s\n' "$CONTEXT_JSON"
299
389
  `;
300
390
 
301
391
  const PRE_TOOL_SCRIPT = `#!/bin/bash