agentxchain 0.8.8 → 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 +126 -142
- package/bin/agentxchain.js +186 -5
- 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 +14 -6
- 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/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/dashboard.js +70 -0
- package/src/commands/init.js +516 -0
- 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/template-list.js +33 -0
- package/src/commands/template-set.js +279 -0
- package/src/commands/validate.js +20 -11
- package/src/commands/verify.js +71 -0
- 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 +97 -1
- 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/gate-evaluator.js +285 -0
- 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/normalized-config.js +539 -0
- package/src/lib/plugin-config-schema.js +192 -0
- package/src/lib/plugins.js +692 -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/schema.js +121 -0
- package/src/lib/schemas/turn-result.schema.json +205 -0
- 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 +137 -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
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { loadAllGovernedTemplates, VALID_GOVERNED_TEMPLATE_IDS } from '../lib/governed-templates.js';
|
|
3
|
+
|
|
4
|
+
export function templateListCommand(opts) {
|
|
5
|
+
if (opts.json) {
|
|
6
|
+
const templates = loadAllGovernedTemplates();
|
|
7
|
+
const output = templates.map((t) => ({
|
|
8
|
+
id: t.id,
|
|
9
|
+
display_name: t.display_name,
|
|
10
|
+
description: t.description,
|
|
11
|
+
planning_artifacts: (t.planning_artifacts || []).map((a) => a.filename),
|
|
12
|
+
prompt_overrides: Object.keys(t.prompt_overrides || {}),
|
|
13
|
+
acceptance_hints: t.acceptance_hints || [],
|
|
14
|
+
}));
|
|
15
|
+
console.log(JSON.stringify(output, null, 2));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
console.log(chalk.bold('\n Available governed templates:\n'));
|
|
20
|
+
const templates = loadAllGovernedTemplates();
|
|
21
|
+
for (const t of templates) {
|
|
22
|
+
const artifacts = (t.planning_artifacts || []).map((a) => a.filename);
|
|
23
|
+
console.log(` ${chalk.cyan(t.id)} — ${t.description}`);
|
|
24
|
+
if (artifacts.length > 0) {
|
|
25
|
+
console.log(` Planning artifacts: ${artifacts.join(', ')}`);
|
|
26
|
+
}
|
|
27
|
+
if (t.prompt_overrides && Object.keys(t.prompt_overrides).length > 0) {
|
|
28
|
+
console.log(` Prompt overrides: ${Object.keys(t.prompt_overrides).join(', ')}`);
|
|
29
|
+
}
|
|
30
|
+
console.log('');
|
|
31
|
+
}
|
|
32
|
+
console.log(chalk.dim(` Usage: agentxchain template set <id>\n`));
|
|
33
|
+
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { CONFIG_FILE } from '../lib/config.js';
|
|
5
|
+
import { loadGovernedTemplate, VALID_GOVERNED_TEMPLATE_IDS } from '../lib/governed-templates.js';
|
|
6
|
+
|
|
7
|
+
const LEDGER_PATH = '.agentxchain/decision-ledger.jsonl';
|
|
8
|
+
const PROMPT_OVERRIDE_SEPARATOR = '## Project-Type-Specific Guidance';
|
|
9
|
+
const ACCEPTANCE_HINTS_SEPARATOR = '## Template Guidance';
|
|
10
|
+
|
|
11
|
+
function interpolateTemplateContent(contentTemplate, projectName) {
|
|
12
|
+
return contentTemplate.replaceAll('{{project_name}}', projectName);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function appendJsonl(root, relPath, entry) {
|
|
16
|
+
const filePath = join(root, relPath);
|
|
17
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
18
|
+
const line = JSON.stringify(entry) + '\n';
|
|
19
|
+
writeFileSync(filePath, line, { flag: 'a' });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function templateSetCommand(templateId, opts) {
|
|
23
|
+
const root = process.cwd();
|
|
24
|
+
const governedWorkspacePath = join(root, '.agentxchain');
|
|
25
|
+
|
|
26
|
+
// ── Validate governed project ──────────────────────────────────────────
|
|
27
|
+
const configPath = join(root, CONFIG_FILE);
|
|
28
|
+
if (!existsSync(configPath)) {
|
|
29
|
+
console.error(chalk.red(' Error: No agentxchain.json found.'));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let config;
|
|
34
|
+
try {
|
|
35
|
+
config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.error(chalk.red(` Error: Failed to read agentxchain.json: ${err.message}`));
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (config.schema_version !== '1.0' && config.schema_version !== '1.1') {
|
|
42
|
+
console.error(chalk.red(' Error: This is not a governed project. template set requires a governed project (schema_version 1.0 or 1.1).'));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!existsSync(governedWorkspacePath)) {
|
|
47
|
+
console.error(chalk.red(' Error: Governed workspace missing. template set requires an existing .agentxchain/ directory.'));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Validate template ID ──────────────────────────────────────────────
|
|
52
|
+
if (!VALID_GOVERNED_TEMPLATE_IDS.includes(templateId)) {
|
|
53
|
+
console.error(chalk.red(` Error: Unknown template "${templateId}".`));
|
|
54
|
+
console.error(` Available templates: ${VALID_GOVERNED_TEMPLATE_IDS.join(', ')}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Same template = no-op ─────────────────────────────────────────────
|
|
59
|
+
const previousTemplate = config.template || 'generic';
|
|
60
|
+
if (previousTemplate === templateId) {
|
|
61
|
+
console.log(chalk.green(` Already set to "${templateId}". No changes.`));
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Load manifest ─────────────────────────────────────────────────────
|
|
66
|
+
const manifest = loadGovernedTemplate(templateId);
|
|
67
|
+
const projectName = config.project?.name || 'Untitled';
|
|
68
|
+
|
|
69
|
+
// ── Build mutation plan ───────────────────────────────────────────────
|
|
70
|
+
const plan = {
|
|
71
|
+
config_update: true,
|
|
72
|
+
files_created: [],
|
|
73
|
+
files_skipped: [],
|
|
74
|
+
prompts_appended: [],
|
|
75
|
+
prompts_existing_guidance: [],
|
|
76
|
+
prompts_missing_paths: [],
|
|
77
|
+
prompts_missing_files: [],
|
|
78
|
+
acceptance_hints_status: 'none',
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Planning artifacts
|
|
82
|
+
for (const artifact of manifest.planning_artifacts || []) {
|
|
83
|
+
const artifactPath = join(root, '.planning', artifact.filename);
|
|
84
|
+
if (existsSync(artifactPath)) {
|
|
85
|
+
plan.files_skipped.push(artifact.filename);
|
|
86
|
+
} else {
|
|
87
|
+
plan.files_created.push(artifact.filename);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Prompt overrides
|
|
92
|
+
const promptOverrides = manifest.prompt_overrides || {};
|
|
93
|
+
for (const roleId of Object.keys(promptOverrides)) {
|
|
94
|
+
const promptRelPath = config.prompts?.[roleId];
|
|
95
|
+
if (!promptRelPath) {
|
|
96
|
+
plan.prompts_missing_paths.push(roleId);
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const promptPath = join(root, promptRelPath);
|
|
100
|
+
if (!existsSync(promptPath)) {
|
|
101
|
+
plan.prompts_missing_files.push({
|
|
102
|
+
role_id: roleId,
|
|
103
|
+
path: promptRelPath,
|
|
104
|
+
});
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const content = readFileSync(promptPath, 'utf8');
|
|
108
|
+
if (content.includes(PROMPT_OVERRIDE_SEPARATOR)) {
|
|
109
|
+
plan.prompts_existing_guidance.push(roleId);
|
|
110
|
+
} else {
|
|
111
|
+
plan.prompts_appended.push(roleId);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Acceptance hints
|
|
116
|
+
const acceptanceMatrixPath = join(root, '.planning', 'acceptance-matrix.md');
|
|
117
|
+
if (Array.isArray(manifest.acceptance_hints) && manifest.acceptance_hints.length > 0) {
|
|
118
|
+
if (existsSync(acceptanceMatrixPath)) {
|
|
119
|
+
const matrixContent = readFileSync(acceptanceMatrixPath, 'utf8');
|
|
120
|
+
if (matrixContent.includes(ACCEPTANCE_HINTS_SEPARATOR)) {
|
|
121
|
+
plan.acceptance_hints_status = 'existing_guidance';
|
|
122
|
+
} else {
|
|
123
|
+
plan.acceptance_hints_status = 'append';
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
plan.acceptance_hints_status = 'missing_file';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ── Dry run: print plan and exit ──────────────────────────────────────
|
|
131
|
+
if (opts.dryRun) {
|
|
132
|
+
console.log(chalk.bold(`\n Template: ${previousTemplate} → ${templateId}\n`));
|
|
133
|
+
console.log(' Config:');
|
|
134
|
+
console.log(` agentxchain.json: template field will be updated`);
|
|
135
|
+
console.log('\n Planning artifacts:');
|
|
136
|
+
for (const f of plan.files_created) {
|
|
137
|
+
console.log(` .planning/${f}: ${chalk.green('WILL CREATE')}`);
|
|
138
|
+
}
|
|
139
|
+
for (const f of plan.files_skipped) {
|
|
140
|
+
console.log(` .planning/${f}: ${chalk.dim('EXISTS (skip)')}`);
|
|
141
|
+
}
|
|
142
|
+
if (plan.files_created.length === 0 && plan.files_skipped.length === 0) {
|
|
143
|
+
console.log(` ${chalk.dim('(none)')}`);
|
|
144
|
+
}
|
|
145
|
+
console.log('\n Prompts:');
|
|
146
|
+
for (const r of plan.prompts_appended) {
|
|
147
|
+
const p = config.prompts?.[r] || `<unknown path for ${r}>`;
|
|
148
|
+
console.log(` ${p}: ${chalk.green('WILL APPEND override')}`);
|
|
149
|
+
}
|
|
150
|
+
for (const r of plan.prompts_existing_guidance) {
|
|
151
|
+
const p = config.prompts?.[r] || `<unknown path for ${r}>`;
|
|
152
|
+
console.log(` ${p}: ${chalk.dim('ALREADY HAS guidance (skip)')}`);
|
|
153
|
+
}
|
|
154
|
+
for (const r of plan.prompts_missing_paths) {
|
|
155
|
+
console.log(` <no configured path for ${r}>: ${chalk.yellow('NO PROMPT PATH (skip)')}`);
|
|
156
|
+
}
|
|
157
|
+
for (const prompt of plan.prompts_missing_files) {
|
|
158
|
+
console.log(` ${prompt.path}: ${chalk.yellow('MISSING FILE (skip)')}`);
|
|
159
|
+
}
|
|
160
|
+
if (
|
|
161
|
+
plan.prompts_appended.length === 0
|
|
162
|
+
&& plan.prompts_existing_guidance.length === 0
|
|
163
|
+
&& plan.prompts_missing_paths.length === 0
|
|
164
|
+
&& plan.prompts_missing_files.length === 0
|
|
165
|
+
) {
|
|
166
|
+
console.log(` ${chalk.dim('(none)')}`);
|
|
167
|
+
}
|
|
168
|
+
console.log('\n Acceptance hints:');
|
|
169
|
+
if (plan.acceptance_hints_status === 'append') {
|
|
170
|
+
console.log(` .planning/acceptance-matrix.md: ${chalk.green('WILL APPEND template guidance')}`);
|
|
171
|
+
} else if (plan.acceptance_hints_status === 'existing_guidance') {
|
|
172
|
+
console.log(` .planning/acceptance-matrix.md: ${chalk.dim('ALREADY HAS guidance (skip)')}`);
|
|
173
|
+
} else if (plan.acceptance_hints_status === 'missing_file') {
|
|
174
|
+
console.log(` .planning/acceptance-matrix.md: ${chalk.yellow('MISSING FILE (skip)')}`);
|
|
175
|
+
} else {
|
|
176
|
+
console.log(` ${chalk.dim('(none)')}`);
|
|
177
|
+
}
|
|
178
|
+
console.log(chalk.dim('\n No changes written. Use without --dry-run to apply.\n'));
|
|
179
|
+
process.exit(0);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── Confirmation prompt ───────────────────────────────────────────────
|
|
183
|
+
if (!opts.yes) {
|
|
184
|
+
console.log(chalk.bold(`\n Template: ${previousTemplate} → ${templateId}`));
|
|
185
|
+
console.log(` ${plan.files_created.length} files to create, ${plan.prompts_appended.length} prompts to append.\n`);
|
|
186
|
+
const readline = await import('node:readline');
|
|
187
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
188
|
+
const answer = await new Promise((resolve) => {
|
|
189
|
+
rl.question(' Apply changes? [y/N] ', resolve);
|
|
190
|
+
});
|
|
191
|
+
rl.close();
|
|
192
|
+
if (answer.trim().toLowerCase() !== 'y') {
|
|
193
|
+
console.log(chalk.dim(' Aborted.'));
|
|
194
|
+
process.exit(0);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ── Execute mutations ─────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
// 1. Update config
|
|
201
|
+
config.template = templateId;
|
|
202
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
|
|
203
|
+
|
|
204
|
+
// 2. Create planning artifacts
|
|
205
|
+
mkdirSync(join(root, '.planning'), { recursive: true });
|
|
206
|
+
for (const artifact of manifest.planning_artifacts || []) {
|
|
207
|
+
const artifactPath = join(root, '.planning', artifact.filename);
|
|
208
|
+
if (!existsSync(artifactPath)) {
|
|
209
|
+
writeFileSync(
|
|
210
|
+
artifactPath,
|
|
211
|
+
interpolateTemplateContent(artifact.content_template, projectName)
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 3. Append prompt overrides
|
|
217
|
+
for (const roleId of plan.prompts_appended) {
|
|
218
|
+
const promptPath = join(root, config.prompts[roleId]);
|
|
219
|
+
const content = readFileSync(promptPath, 'utf8');
|
|
220
|
+
const override = promptOverrides[roleId];
|
|
221
|
+
const appended = `${content}\n\n---\n\n${PROMPT_OVERRIDE_SEPARATOR}\n\n${override.trim()}\n`;
|
|
222
|
+
writeFileSync(promptPath, appended);
|
|
223
|
+
}
|
|
224
|
+
// Warn about skipped prompts when switching templates
|
|
225
|
+
for (const roleId of plan.prompts_existing_guidance) {
|
|
226
|
+
console.log(chalk.yellow(` Warning: Prompt for ${roleId} already has project-type guidance. Skipping. Edit manually if you want the new template's guidance.`));
|
|
227
|
+
}
|
|
228
|
+
for (const roleId of plan.prompts_missing_paths) {
|
|
229
|
+
console.log(chalk.yellow(` Warning: Prompt for ${roleId} has no configured path in agentxchain.json. Skipping template guidance.`));
|
|
230
|
+
}
|
|
231
|
+
for (const prompt of plan.prompts_missing_files) {
|
|
232
|
+
console.log(chalk.yellow(` Warning: Prompt file for ${prompt.role_id} not found at ${prompt.path}. Skipping template guidance.`));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// 4. Append acceptance hints
|
|
236
|
+
if (plan.acceptance_hints_status === 'append' && existsSync(acceptanceMatrixPath)) {
|
|
237
|
+
const matrixContent = readFileSync(acceptanceMatrixPath, 'utf8');
|
|
238
|
+
const hintLines = manifest.acceptance_hints.map((hint) => `- [ ] ${hint}`).join('\n');
|
|
239
|
+
const appended = `${matrixContent}\n\n${ACCEPTANCE_HINTS_SEPARATOR}\n\n${hintLines}\n`;
|
|
240
|
+
writeFileSync(acceptanceMatrixPath, appended);
|
|
241
|
+
} else if (plan.acceptance_hints_status === 'missing_file') {
|
|
242
|
+
console.log(chalk.yellow(' Warning: .planning/acceptance-matrix.md not found. Skipping template guidance hints.'));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 5. Decision ledger
|
|
246
|
+
const ledgerEntry = {
|
|
247
|
+
type: 'template_set',
|
|
248
|
+
timestamp: new Date().toISOString(),
|
|
249
|
+
previous_template: previousTemplate,
|
|
250
|
+
new_template: templateId,
|
|
251
|
+
files_created: plan.files_created,
|
|
252
|
+
files_skipped: plan.files_skipped,
|
|
253
|
+
prompts_appended: plan.prompts_appended,
|
|
254
|
+
prompts_skipped: [
|
|
255
|
+
...plan.prompts_existing_guidance,
|
|
256
|
+
...plan.prompts_missing_paths,
|
|
257
|
+
...plan.prompts_missing_files.map((prompt) => prompt.role_id),
|
|
258
|
+
],
|
|
259
|
+
prompt_missing_paths: plan.prompts_missing_paths,
|
|
260
|
+
prompt_missing_files: plan.prompts_missing_files,
|
|
261
|
+
acceptance_hints_appended: plan.acceptance_hints_status === 'append',
|
|
262
|
+
acceptance_hints_skipped_reason: plan.acceptance_hints_status === 'append' ? null : plan.acceptance_hints_status,
|
|
263
|
+
operator: 'human',
|
|
264
|
+
};
|
|
265
|
+
appendJsonl(root, LEDGER_PATH, ledgerEntry);
|
|
266
|
+
|
|
267
|
+
// ── Summary ───────────────────────────────────────────────────────────
|
|
268
|
+
console.log(chalk.green(`\n Template set to "${templateId}".`));
|
|
269
|
+
if (plan.files_created.length > 0) {
|
|
270
|
+
console.log(` Created: ${plan.files_created.map(f => `.planning/${f}`).join(', ')}`);
|
|
271
|
+
}
|
|
272
|
+
if (plan.prompts_appended.length > 0) {
|
|
273
|
+
console.log(` Appended guidance to: ${plan.prompts_appended.join(', ')} prompts`);
|
|
274
|
+
}
|
|
275
|
+
if (plan.acceptance_hints_status === 'append') {
|
|
276
|
+
console.log(` Appended template guidance to acceptance-matrix.md`);
|
|
277
|
+
}
|
|
278
|
+
console.log('');
|
|
279
|
+
}
|
package/src/commands/validate.js
CHANGED
|
@@ -1,28 +1,37 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { loadConfig } from '../lib/config.js';
|
|
3
|
-
import { validateProject } from '../lib/validation.js';
|
|
2
|
+
import { loadConfig, loadProjectContext } from '../lib/config.js';
|
|
3
|
+
import { validateGovernedProject, validateProject } from '../lib/validation.js';
|
|
4
4
|
|
|
5
5
|
export async function validateCommand(opts) {
|
|
6
|
-
const
|
|
7
|
-
if (!
|
|
6
|
+
const context = loadProjectContext();
|
|
7
|
+
if (!context) {
|
|
8
8
|
console.log(chalk.red('No agentxchain.json found. Run `agentxchain init` first.'));
|
|
9
9
|
process.exit(1);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const { root, config } = result;
|
|
13
12
|
const mode = opts.mode || 'full';
|
|
14
|
-
const validation =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const validation = context.config.protocol_mode === 'governed'
|
|
14
|
+
? validateGovernedProject(context.root, context.rawConfig, context.config, {
|
|
15
|
+
mode,
|
|
16
|
+
expectedAgent: opts.agent || null,
|
|
17
|
+
})
|
|
18
|
+
: validateProject(context.root, loadConfig()?.config || context.rawConfig, {
|
|
19
|
+
mode,
|
|
20
|
+
expectedAgent: opts.agent || null,
|
|
21
|
+
});
|
|
18
22
|
|
|
19
23
|
if (opts.json) {
|
|
20
|
-
console.log(JSON.stringify(
|
|
24
|
+
console.log(JSON.stringify({
|
|
25
|
+
...validation,
|
|
26
|
+
protocol_mode: context.config.protocol_mode,
|
|
27
|
+
version: context.version,
|
|
28
|
+
}, null, 2));
|
|
21
29
|
} else {
|
|
22
30
|
console.log('');
|
|
23
31
|
console.log(chalk.bold(` AgentXchain Validate (${mode})`));
|
|
24
32
|
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
25
|
-
console.log(chalk.dim(` Root: ${root}`));
|
|
33
|
+
console.log(chalk.dim(` Root: ${context.root}`));
|
|
34
|
+
console.log(chalk.dim(` Protocol: ${context.config.protocol_mode} (v${context.version})`));
|
|
26
35
|
console.log('');
|
|
27
36
|
|
|
28
37
|
if (validation.ok) {
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { verifyProtocolConformance } from '../lib/protocol-conformance.js';
|
|
4
|
+
|
|
5
|
+
export async function verifyProtocolCommand(opts) {
|
|
6
|
+
const target = opts.target ? resolve(opts.target) : process.cwd();
|
|
7
|
+
const requestedTier = Number.parseInt(String(opts.tier || '1'), 10);
|
|
8
|
+
const format = opts.format || 'text';
|
|
9
|
+
|
|
10
|
+
let result;
|
|
11
|
+
try {
|
|
12
|
+
result = verifyProtocolConformance({
|
|
13
|
+
targetRoot: target,
|
|
14
|
+
requestedTier,
|
|
15
|
+
surface: opts.surface || null,
|
|
16
|
+
});
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (format === 'json') {
|
|
19
|
+
console.log(JSON.stringify({
|
|
20
|
+
overall: 'error',
|
|
21
|
+
message: error.message,
|
|
22
|
+
}, null, 2));
|
|
23
|
+
} else {
|
|
24
|
+
console.log(chalk.red(`Protocol verification failed: ${error.message}`));
|
|
25
|
+
}
|
|
26
|
+
process.exit(2);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (format === 'json') {
|
|
30
|
+
console.log(JSON.stringify(result.report, null, 2));
|
|
31
|
+
} else {
|
|
32
|
+
printProtocolReport(result.report);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
process.exit(result.exitCode);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function printProtocolReport(report) {
|
|
39
|
+
console.log('');
|
|
40
|
+
console.log(chalk.bold(' AgentXchain Protocol Conformance'));
|
|
41
|
+
console.log(chalk.dim(' ' + '─'.repeat(44)));
|
|
42
|
+
console.log(chalk.dim(` Target: ${report.target_root}`));
|
|
43
|
+
console.log(chalk.dim(` Implementation: ${report.implementation}`));
|
|
44
|
+
console.log(chalk.dim(` Tier requested: ${report.tier_requested}`));
|
|
45
|
+
console.log('');
|
|
46
|
+
|
|
47
|
+
const overallLabel = report.overall === 'pass'
|
|
48
|
+
? chalk.green('PASS')
|
|
49
|
+
: report.overall === 'fail'
|
|
50
|
+
? chalk.red('FAIL')
|
|
51
|
+
: chalk.red('ERROR');
|
|
52
|
+
console.log(` Overall: ${overallLabel}`);
|
|
53
|
+
|
|
54
|
+
for (const [tierKey, tier] of Object.entries(report.results)) {
|
|
55
|
+
const label = tier.status === 'pass'
|
|
56
|
+
? chalk.green('pass')
|
|
57
|
+
: tier.status === 'skipped'
|
|
58
|
+
? chalk.yellow('skipped')
|
|
59
|
+
: chalk.red(tier.status);
|
|
60
|
+
console.log(` ${tierKey}: ${label} (${tier.fixtures_passed}/${tier.fixtures_run} passed)`);
|
|
61
|
+
|
|
62
|
+
for (const failure of tier.failures || []) {
|
|
63
|
+
console.log(chalk.red(` - ${failure.fixture_id}: ${failure.message}`));
|
|
64
|
+
}
|
|
65
|
+
for (const error of tier.errors || []) {
|
|
66
|
+
console.log(chalk.red(` - ${error.fixture_id}: ${error.message}`));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
console.log('');
|
|
71
|
+
}
|