agentxchain 2.157.0 → 2.159.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.
@@ -124,6 +124,8 @@ import { benchmarkCommand } from '../src/commands/benchmark.js';
124
124
  import { benchmarkWorkloadsCommand } from '../src/commands/benchmark-workloads.js';
125
125
  import { historyCommand } from '../src/commands/history.js';
126
126
  import { decisionsCommand } from '../src/commands/decisions.js';
127
+ import { shipStatusCommand } from '../src/commands/ship-status.js';
128
+ import { attentionCommand } from '../src/commands/attention.js';
127
129
  import { diffCommand } from '../src/commands/diff.js';
128
130
  import { eventsCommand } from '../src/commands/events.js';
129
131
  import { connectorCapabilitiesCommand, connectorCheckCommand, connectorValidateCommand } from '../src/commands/connector.js';
@@ -420,6 +422,22 @@ program
420
422
  .option('-d, --dir <path>', 'Project directory')
421
423
  .action(decisionsCommand);
422
424
 
425
+ program
426
+ .command('ship-status')
427
+ .description('Evaluate whether the current project is ready to ship')
428
+ .option('-j, --json', 'Machine-readable JSON output')
429
+ .option('-v, --verbose', 'Per-dimension detail breakdown')
430
+ .option('-d, --dir <path>', 'Project directory')
431
+ .action(shipStatusCommand);
432
+
433
+ program
434
+ .command('attention')
435
+ .description('Show the cross-run human-decision queue — what needs you, and nothing else (govern by exception)')
436
+ .option('-j, --json', 'Machine-readable JSON output (full HumanAttentionReport)')
437
+ .option('-a, --all', 'Include informational/non-blocking items (e.g. pending approved intents)')
438
+ .option('-d, --dir <path>', 'Project directory')
439
+ .action(attentionCommand);
440
+
423
441
  program
424
442
  .command('diff <left_ref> <right_ref>')
425
443
  .description('Compare two recorded governed runs or two export artifacts')
@@ -953,6 +971,12 @@ roleCmd
953
971
  .option('-j, --json', 'Output as JSON')
954
972
  .action((roleId, opts) => roleCommand('show', roleId, opts));
955
973
 
974
+ roleCmd
975
+ .command('validate [role_id]')
976
+ .description('Validate role charter well-formedness against the four VISION:123–128 invariants (exit 1 if any role incomplete)')
977
+ .option('-j, --json', 'Output as JSON')
978
+ .action((roleId, opts) => roleCommand('validate', roleId, opts));
979
+
956
980
  const turnCmd = program
957
981
  .command('turn')
958
982
  .description('Inspect active governed turn dispatch bundles');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.157.0",
3
+ "version": "2.159.0",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,66 @@
1
+ /**
2
+ * CLI command: agentxchain attention
3
+ *
4
+ * Answers "what needs MY attention right now — and nothing else?" by composing the
5
+ * cross-category human-decision queue into a single HumanAttentionReport. All evaluation
6
+ * logic lives in lib/human-attention.js — this command is presentation only
7
+ * (Architecture Invariant #6).
8
+ *
9
+ * It is a status surface, not a gate: it exits 0 in BOTH the clear and attention states.
10
+ * When the queue is empty it prints "Nothing needs your attention." — the operational
11
+ * proof that the human can step back and let governed autonomy run (VISION.md:51).
12
+ */
13
+
14
+ import { resolve } from 'node:path';
15
+ import chalk from 'chalk';
16
+ import { findProjectRoot } from '../lib/config.js';
17
+ import { evaluateHumanAttention } from '../lib/human-attention.js';
18
+
19
+ export async function attentionCommand(opts) {
20
+ const dir = opts.dir ? resolve(opts.dir) : process.cwd();
21
+ const root = findProjectRoot(dir);
22
+ if (!root) {
23
+ console.error(chalk.red('No agentxchain project found. Run "agentxchain init" first.'));
24
+ process.exitCode = 1;
25
+ return;
26
+ }
27
+
28
+ const report = evaluateHumanAttention(root);
29
+
30
+ if (opts.json) {
31
+ // --json always emits the full, schema-valid HumanAttentionReport; exit 0 in both states.
32
+ console.log(JSON.stringify(report, null, 2));
33
+ return;
34
+ }
35
+
36
+ if (report.overall === 'clear') {
37
+ console.log(chalk.green('Nothing needs your attention.'));
38
+ return;
39
+ }
40
+
41
+ // Default view surfaces blocking + escalation items first; informational pending-intent
42
+ // items are summarized as a count unless --all is passed.
43
+ const visible = opts.all ? report.items : report.items.filter((item) => item.blocking);
44
+ const hidden = report.items.length - visible.length;
45
+
46
+ console.log(
47
+ `\n${chalk.bold('Attention needed')}: ${report.items_count} `
48
+ + `item${report.items_count === 1 ? '' : 's'} (${report.blocking_count} blocking)`,
49
+ );
50
+
51
+ visible.forEach((item, idx) => {
52
+ const tag = chalk.cyan(String(item.category).padEnd(16));
53
+ const run = item.run_id ? `${chalk.dim(item.run_id)} ` : '';
54
+ console.log(` [${idx + 1}] ${tag} ${run}${item.summary}`);
55
+ console.log(` ${chalk.dim('→')} ${item.action_hint}`);
56
+ });
57
+
58
+ if (hidden > 0) {
59
+ console.log(
60
+ chalk.dim(` (+${hidden} informational item${hidden === 1 ? '' : 's'} — run with --all to show)`),
61
+ );
62
+ }
63
+
64
+ console.log('');
65
+ // Status surface, not a gate — no non-zero exit even in the attention state.
66
+ }
@@ -4,6 +4,10 @@ import {
4
4
  getRoleRuntimeCapabilityContract,
5
5
  summarizeRuntimeCapabilityContract,
6
6
  } from '../lib/runtime-capabilities.js';
7
+ import {
8
+ evaluateRoleCharter,
9
+ evaluateAllRoleCharters,
10
+ } from '../lib/role-charter.js';
7
11
 
8
12
  export function roleCommand(subcommand, roleId, opts) {
9
13
  const context = loadProjectContext();
@@ -12,7 +16,7 @@ export function roleCommand(subcommand, roleId, opts) {
12
16
  process.exit(1);
13
17
  }
14
18
 
15
- const { config, version } = context;
19
+ const { config, rawConfig, version } = context;
16
20
 
17
21
  if (version !== 4) {
18
22
  console.log(chalk.red(' Not a governed AgentXchain project (requires v4 config).'));
@@ -27,6 +31,10 @@ export function roleCommand(subcommand, roleId, opts) {
27
31
  return showRole(roleId, roles, runtimes, roleIds, opts);
28
32
  }
29
33
 
34
+ if (subcommand === 'validate') {
35
+ return validateRoles(roleId, config, rawConfig, roleIds, opts);
36
+ }
37
+
30
38
  // Default: list
31
39
  return listRoles(roles, runtimes, roleIds, opts);
32
40
  }
@@ -155,3 +163,63 @@ function showRole(roleId, roles, runtimes, roleIds, opts) {
155
163
  }
156
164
  console.log('');
157
165
  }
166
+
167
+ function renderCharterReport(report) {
168
+ const mark = report.overall === 'well_formed' ? chalk.green('✓') : chalk.red('✗');
169
+ if (report.overall === 'well_formed') {
170
+ console.log(` ${mark} ${chalk.cyan(report.role_id)} well-formed (4/4 invariants)`);
171
+ return;
172
+ }
173
+ console.log(` ${mark} ${chalk.cyan(report.role_id)} incomplete — missing: ${report.missing.join(', ')}`);
174
+ for (const inv of report.invariants) {
175
+ if (!inv.satisfied && inv.fix_hint) {
176
+ console.log(chalk.dim(` → ${inv.fix_hint}`));
177
+ }
178
+ }
179
+ }
180
+
181
+ function validateRoles(roleId, config, rawConfig, roleIds, opts) {
182
+ // Single-role validation
183
+ if (roleId) {
184
+ if (!config.roles?.[roleId]) {
185
+ console.log(chalk.red(` Unknown role: ${roleId}`));
186
+ console.log(chalk.dim(` Available: ${roleIds.join(', ')}`));
187
+ process.exit(1);
188
+ }
189
+ const report = evaluateRoleCharter(config, rawConfig, roleId);
190
+ if (opts.json) {
191
+ console.log(JSON.stringify(report, null, 2));
192
+ } else {
193
+ console.log('');
194
+ renderCharterReport(report);
195
+ console.log('');
196
+ }
197
+ process.exit(report.overall === 'well_formed' ? 0 : 1);
198
+ }
199
+
200
+ // All-roles validation
201
+ const all = evaluateAllRoleCharters(config, rawConfig);
202
+ if (opts.json) {
203
+ console.log(JSON.stringify(all, null, 2));
204
+ process.exit(all.incomplete === 0 ? 0 : 1);
205
+ }
206
+
207
+ if (all.total === 0) {
208
+ console.log(' No roles defined.');
209
+ process.exit(0);
210
+ }
211
+
212
+ if (all.incomplete === 0) {
213
+ console.log(chalk.bold(`\n Role charter validation (${all.total} roles): all well-formed (4/4 invariants each).\n`));
214
+ process.exit(0);
215
+ }
216
+
217
+ console.log(chalk.bold(`\n Role charter validation (${all.total} roles):\n`));
218
+ for (const report of all.roles) {
219
+ renderCharterReport(report);
220
+ }
221
+ console.log('');
222
+ console.log(` ${all.incomplete} of ${all.total} roles incomplete.`);
223
+ console.log('');
224
+ process.exit(1);
225
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * CLI command: agentxchain ship-status
3
+ *
4
+ * Answers "is this ready to ship?" by composing five evidence dimensions into a
5
+ * single ShipStatusReport. All evaluation logic lives in lib/ship-status.js — this
6
+ * command is presentation only (Architecture Invariant #5).
7
+ */
8
+
9
+ import { existsSync } from 'node:fs';
10
+ import { resolve, join } from 'node:path';
11
+ import chalk from 'chalk';
12
+ import { findProjectRoot } from '../lib/config.js';
13
+ import { COORDINATOR_CONFIG_FILE } from '../lib/coordinator-config.js';
14
+ import { evaluateShipStatus, evaluateCoordinatorShipStatus } from '../lib/ship-status.js';
15
+
16
+ function statusIcon(status) {
17
+ if (status === 'pass') return chalk.green('✓');
18
+ if (status === 'fail') return chalk.red('✗');
19
+ return chalk.yellow('?');
20
+ }
21
+
22
+ function overallLabel(overall) {
23
+ if (overall === 'pass') return chalk.green.bold('YES');
24
+ if (overall === 'fail') return chalk.red.bold('NO');
25
+ return chalk.yellow.bold('PENDING');
26
+ }
27
+
28
+ function countPassed(report) {
29
+ return (report.dimensions || []).filter((d) => d.status === 'pass').length;
30
+ }
31
+
32
+ function printDimensions(dimensions, indent) {
33
+ for (const dim of dimensions || []) {
34
+ console.log(`${indent}${statusIcon(dim.status)} ${dim.name}: ${dim.detail}`);
35
+ }
36
+ }
37
+
38
+ function printSingle(report, opts) {
39
+ const passed = countPassed(report);
40
+ const total = (report.dimensions || []).length;
41
+ const blocking = report.blocking_reasons || [];
42
+ const suffix = blocking.length ? `, ${blocking.length} blocking` : '';
43
+ console.log(`\nShip Status: ${overallLabel(report.overall)} (${passed}/${total} dimensions pass${suffix})`);
44
+
45
+ if (opts.verbose) {
46
+ console.log('');
47
+ printDimensions(report.dimensions, ' ');
48
+ } else {
49
+ for (const reason of blocking) {
50
+ console.log(` ${chalk.dim('-')} ${reason}`);
51
+ }
52
+ }
53
+ console.log('');
54
+ }
55
+
56
+ export async function shipStatusCommand(opts) {
57
+ const dir = opts.dir ? resolve(opts.dir) : process.cwd();
58
+ const coordinatorConfigPath = join(dir, COORDINATOR_CONFIG_FILE);
59
+
60
+ let report;
61
+ let isCoordinator = false;
62
+
63
+ if (existsSync(coordinatorConfigPath)) {
64
+ isCoordinator = true;
65
+ report = evaluateCoordinatorShipStatus(dir);
66
+ } else {
67
+ const root = findProjectRoot(dir);
68
+ if (!root) {
69
+ console.error(chalk.red('No agentxchain project found. Run "agentxchain init" first.'));
70
+ process.exitCode = 1;
71
+ return;
72
+ }
73
+ report = evaluateShipStatus(root);
74
+ }
75
+
76
+ if (opts.json) {
77
+ console.log(JSON.stringify(report, null, 2));
78
+ } else if (isCoordinator) {
79
+ const blocking = report.blocking_repos || [];
80
+ const total = (report.repos || []).length;
81
+ const shippable = total - blocking.length;
82
+ console.log(`\nCoordinator Ship Status: ${overallLabel(report.overall)} (${shippable}/${total} repos shippable)`);
83
+ for (const entry of report.repos || []) {
84
+ const passed = countPassed(entry.ship_status);
85
+ const dimTotal = (entry.ship_status.dimensions || []).length;
86
+ console.log(`\n ${chalk.bold(entry.repo_id)}: ${overallLabel(entry.ship_status.overall)} (${passed}/${dimTotal})`);
87
+ if (opts.verbose) {
88
+ printDimensions(entry.ship_status.dimensions, ' ');
89
+ } else {
90
+ for (const reason of entry.ship_status.blocking_reasons || []) {
91
+ console.log(` ${chalk.dim('-')} ${reason}`);
92
+ }
93
+ }
94
+ }
95
+ console.log('');
96
+ } else {
97
+ printSingle(report, opts);
98
+ }
99
+
100
+ // Non-zero exit when not shippable (useful for CI gating).
101
+ if (report.overall === 'fail') {
102
+ process.exitCode = 1;
103
+ }
104
+ }
@@ -471,6 +471,18 @@ function renderPrompt(role, roleId, turn, state, config, root) {
471
471
  lines.push('');
472
472
  }
473
473
 
474
+ // Single-shot execution model — prevents ghost turns from async / background-wait patterns.
475
+ lines.push('## Single-Shot Execution (read carefully)');
476
+ lines.push('');
477
+ lines.push('You run as a **single, non-interactive `--print` subprocess**. You get exactly ONE invocation with no "later": there is no async wake-up, no second message, and nothing will notify you after you stop responding. The moment you end your turn the subprocess exits — if you have not written your turn result by then, the entire turn is **discarded as a ghost** (no result, wasted run).');
478
+ lines.push('');
479
+ lines.push('Therefore:');
480
+ lines.push('- Do ALL work **synchronously** within this one turn. Run every command to completion in the foreground and read its output inline before continuing.');
481
+ lines.push('- **NEVER** start a background job and then wait for it. Do not use `ScheduleWakeup`; do not start a `Monitor` or `tail -f` and wait for events; do not background a process with `&` and poll for a notification; do not defer any conclusion to "after the suite/build finishes". You will not be re-invoked to finish.');
482
+ lines.push('- If you need a long command or test suite result, run it **blocking in the foreground** (e.g. `npm test`) and wait for it to return in-line — the per-turn timeout is generous, so budget your time for it.');
483
+ lines.push('- Writing the structured turn result below is your **final action**: gather every piece of evidence first, then write the result before you finish.');
484
+ lines.push('');
485
+
474
486
  // Output format with complete JSON template
475
487
  lines.push('## Required Output');
476
488
  lines.push('');