agentxchain 2.38.0 → 2.39.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.
package/README.md CHANGED
@@ -9,7 +9,9 @@ Legacy IDE-window coordination is still shipped as a compatibility mode for team
9
9
  ## Docs
10
10
 
11
11
  - [Quickstart](https://agentxchain.dev/docs/quickstart/)
12
+ - [Getting Started](https://agentxchain.dev/docs/getting-started/)
12
13
  - [CLI reference](https://agentxchain.dev/docs/cli/)
14
+ - [Templates](https://agentxchain.dev/docs/templates/)
13
15
  - [Export schema reference](https://agentxchain.dev/docs/export-schema/)
14
16
  - [Adapter reference](https://agentxchain.dev/docs/adapters/)
15
17
  - [Protocol spec (v6)](https://agentxchain.dev/docs/protocol/)
@@ -90,6 +92,15 @@ Built-in governed templates:
90
92
  - `web-app`: user flows, UI acceptance, browser support
91
93
  - `enterprise-app`: enterprise planning artifacts plus blueprint-backed `architect` and `security_reviewer` phases
92
94
 
95
+ Inspect the shipped template surfaces instead of inferring them from docs:
96
+
97
+ ```bash
98
+ agentxchain template list
99
+ agentxchain template list --phase-templates
100
+ ```
101
+
102
+ `template list` enumerates governed project templates. `template list --phase-templates` enumerates the reusable workflow-kit phase-template bundles you can reference from `workflow_kit.phases.<phase>.template`.
103
+
93
104
  `step` writes a turn-scoped bundle under `.agentxchain/dispatch/turns/<turn_id>/` and expects a staged result at `.agentxchain/staging/<turn_id>/turn-result.json`. Typical continuation:
94
105
 
95
106
  ```bash
@@ -411,6 +411,7 @@ templateCmd
411
411
  .command('list')
412
412
  .description('List available governed templates')
413
413
  .option('-j, --json', 'Output as JSON')
414
+ .option('--phase-templates', 'List workflow-kit phase templates instead of governed project templates')
414
415
  .action(templateListCommand);
415
416
 
416
417
  templateCmd
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.38.0",
3
+ "version": "2.39.0",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -6,7 +6,7 @@ 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
8
  import { loadGovernedTemplate, VALID_GOVERNED_TEMPLATE_IDS, buildSystemSpecContent } from '../lib/governed-templates.js';
9
- import { VALID_PROMPT_TRANSPORTS } from '../lib/normalized-config.js';
9
+ import { normalizeWorkflowKit, VALID_PROMPT_TRANSPORTS } from '../lib/normalized-config.js';
10
10
 
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
  const TEMPLATES_DIR = join(__dirname, '../templates');
@@ -628,6 +628,9 @@ export function scaffoldGoverned(dir, projectName, projectId, templateId = 'gene
628
628
  const { runtime: localDevRuntime } = resolveGovernedLocalDevRuntime(runtimeOptions);
629
629
  const scaffoldConfig = buildScaffoldConfigFromTemplate(template, localDevRuntime, workflowKitConfig);
630
630
  const { roles, runtimes, routing, gates, prompts, workflowKitConfig: effectiveWorkflowKitConfig } = scaffoldConfig;
631
+ const scaffoldWorkflowKitConfig = effectiveWorkflowKitConfig
632
+ ? normalizeWorkflowKit(effectiveWorkflowKitConfig, Object.keys(routing))
633
+ : null;
631
634
  const initialPhase = Object.keys(routing)[0] || 'planning';
632
635
  const phaseGateStatus = Object.fromEntries(
633
636
  [...new Set(
@@ -709,7 +712,7 @@ export function scaffoldGoverned(dir, projectName, projectId, templateId = 'gene
709
712
  const basePrompt = buildGovernedPrompt(roleId, role, {
710
713
  routing,
711
714
  gates,
712
- workflowKitConfig: effectiveWorkflowKitConfig,
715
+ workflowKitConfig: scaffoldWorkflowKitConfig,
713
716
  });
714
717
  const prompt = appendPromptOverride(basePrompt, template.prompt_overrides?.[roleId]);
715
718
  writeFileSync(join(dir, '.agentxchain', 'prompts', `${roleId}.md`), prompt);
@@ -736,7 +739,7 @@ export function scaffoldGoverned(dir, projectName, projectId, templateId = 'gene
736
739
 
737
740
  // Workflow-kit custom artifacts — only scaffold files from explicit workflow_kit config
738
741
  // that are not already handled by the default scaffold above
739
- if (effectiveWorkflowKitConfig && effectiveWorkflowKitConfig.phases && typeof effectiveWorkflowKitConfig.phases === 'object') {
742
+ if (scaffoldWorkflowKitConfig && scaffoldWorkflowKitConfig.phases && typeof scaffoldWorkflowKitConfig.phases === 'object') {
740
743
  const defaultScaffoldPaths = new Set([
741
744
  '.planning/PM_SIGNOFF.md',
742
745
  '.planning/ROADMAP.md',
@@ -747,7 +750,7 @@ export function scaffoldGoverned(dir, projectName, projectId, templateId = 'gene
747
750
  '.planning/RELEASE_NOTES.md',
748
751
  ]);
749
752
 
750
- for (const phaseConfig of Object.values(effectiveWorkflowKitConfig.phases)) {
753
+ for (const phaseConfig of Object.values(scaffoldWorkflowKitConfig.phases)) {
751
754
  if (!Array.isArray(phaseConfig.artifacts)) continue;
752
755
  for (const artifact of phaseConfig.artifacts) {
753
756
  if (!artifact.path || defaultScaffoldPaths.has(artifact.path)) continue;
@@ -782,7 +785,7 @@ export function scaffoldGoverned(dir, projectName, projectId, templateId = 'gene
782
785
  }
783
786
  }
784
787
 
785
- return { config, state };
788
+ return { config, state, scaffoldWorkflowKitConfig };
786
789
  }
787
790
 
788
791
  async function initGoverned(opts) {
@@ -882,7 +885,7 @@ async function initGoverned(opts) {
882
885
  }
883
886
  }
884
887
 
885
- const { config } = scaffoldGoverned(dir, projectName, projectId, templateId, opts, workflowKitConfig);
888
+ const { config, scaffoldWorkflowKitConfig } = scaffoldGoverned(dir, projectName, projectId, templateId, opts, workflowKitConfig);
886
889
 
887
890
  console.log('');
888
891
  console.log(chalk.green(` ✓ Created governed project ${chalk.bold(targetLabel)}/`));
@@ -897,7 +900,7 @@ async function initGoverned(opts) {
897
900
  console.log(` ${chalk.dim('│')} ${chalk.dim('├──')} reviews/`);
898
901
  console.log(` ${chalk.dim('│')} ${chalk.dim('└──')} dispatch/`);
899
902
  console.log(` ${chalk.dim('├──')} .planning/`);
900
- const planningSummaryLines = buildPlanningSummaryLines(selectedTemplate, config.workflow_kit);
903
+ const planningSummaryLines = buildPlanningSummaryLines(selectedTemplate, scaffoldWorkflowKitConfig);
901
904
  for (const [index, line] of planningSummaryLines.entries()) {
902
905
  const branch = index === planningSummaryLines.length - 1 ? '└──' : '├──';
903
906
  console.log(` ${chalk.dim('│')} ${chalk.dim(branch)} ${line}`);
@@ -1,7 +1,12 @@
1
1
  import chalk from 'chalk';
2
2
  import { loadAllGovernedTemplates, VALID_GOVERNED_TEMPLATE_IDS } from '../lib/governed-templates.js';
3
+ import { listWorkflowKitPhaseTemplates } from '../lib/workflow-kit-phase-templates.js';
3
4
 
4
5
  export function templateListCommand(opts) {
6
+ if (opts.phaseTemplates) {
7
+ return listPhaseTemplates(opts);
8
+ }
9
+
5
10
  if (opts.json) {
6
11
  const templates = loadAllGovernedTemplates();
7
12
  const output = templates.map((t) => ({
@@ -34,4 +39,40 @@ export function templateListCommand(opts) {
34
39
  console.log('');
35
40
  }
36
41
  console.log(chalk.dim(` Usage: agentxchain template set <id>\n`));
42
+ console.log(chalk.dim(` Tip: use --phase-templates to list workflow-kit phase templates.\n`));
43
+ }
44
+
45
+ function listPhaseTemplates(opts) {
46
+ const templates = listWorkflowKitPhaseTemplates();
47
+
48
+ if (opts.json) {
49
+ const output = templates.map((t) => ({
50
+ id: t.id,
51
+ description: t.description,
52
+ artifacts: t.artifacts.map((a) => ({
53
+ path: a.path,
54
+ semantics: a.semantics || null,
55
+ semantics_config: a.semantics_config || null,
56
+ required: a.required,
57
+ })),
58
+ }));
59
+ console.log(JSON.stringify(output, null, 2));
60
+ return;
61
+ }
62
+
63
+ console.log(chalk.bold('\n Workflow-kit phase templates:\n'));
64
+ for (const t of templates) {
65
+ console.log(` ${chalk.cyan(t.id)} — ${t.description}`);
66
+ for (const a of t.artifacts) {
67
+ const req = a.required ? chalk.green('required') : chalk.dim('optional');
68
+ const sem = a.semantics ? chalk.yellow(a.semantics) : chalk.dim('none');
69
+ console.log(` ${a.path} [${req}] [semantics: ${sem}]`);
70
+ if (a.semantics === 'section_check' && a.semantics_config?.required_sections) {
71
+ console.log(` sections: ${a.semantics_config.required_sections.join(', ')}`);
72
+ }
73
+ }
74
+ console.log('');
75
+ }
76
+ console.log(chalk.dim(' Usage in agentxchain.json:'));
77
+ console.log(chalk.dim(' "workflow_kit": { "phases": { "<phase>": { "template": "<id>" } } }\n'));
37
78
  }
@@ -15,6 +15,12 @@
15
15
  import { validateHooksConfig } from './hook-runner.js';
16
16
  import { validateNotificationsConfig } from './notification-runner.js';
17
17
  import { SUPPORTED_TOKEN_COUNTER_PROVIDERS } from './token-counter.js';
18
+ import {
19
+ buildDefaultWorkflowKitArtifactsForPhase,
20
+ expandWorkflowKitPhaseArtifacts,
21
+ isWorkflowKitPhaseTemplateId,
22
+ VALID_WORKFLOW_KIT_PHASE_TEMPLATE_IDS,
23
+ } from './workflow-kit-phase-templates.js';
18
24
 
19
25
  const VALID_WRITE_AUTHORITIES = ['authoritative', 'proposed', 'review_only'];
20
26
  const VALID_RUNTIME_TYPES = ['manual', 'local_cli', 'api_proxy', 'mcp', 'remote_agent'];
@@ -26,25 +32,6 @@ export { DEFAULT_PHASES };
26
32
  const VALID_PHASE_NAME = /^[a-z][a-z0-9_-]*$/;
27
33
  const VALID_SEMANTIC_IDS = ['pm_signoff', 'system_spec', 'implementation_notes', 'acceptance_matrix', 'ship_verdict', 'release_notes', 'section_check'];
28
34
 
29
- /**
30
- * Default artifact map for phases when workflow_kit is absent from config.
31
- * Only phases present in this map get default artifacts.
32
- */
33
- const DEFAULT_PHASE_ARTIFACTS = {
34
- planning: [
35
- { path: '.planning/PM_SIGNOFF.md', semantics: 'pm_signoff', required: true },
36
- { path: '.planning/SYSTEM_SPEC.md', semantics: 'system_spec', required: true },
37
- { path: '.planning/ROADMAP.md', semantics: null, required: true },
38
- ],
39
- implementation: [
40
- { path: '.planning/IMPLEMENTATION_NOTES.md', semantics: 'implementation_notes', required: true },
41
- ],
42
- qa: [
43
- { path: '.planning/acceptance-matrix.md', semantics: 'acceptance_matrix', required: true },
44
- { path: '.planning/ship-verdict.md', semantics: 'ship_verdict', required: true },
45
- { path: '.planning/RELEASE_NOTES.md', semantics: 'release_notes', required: true },
46
- ],
47
- };
48
35
  const VALID_API_PROXY_RETRY_JITTER = ['none', 'full'];
49
36
  const VALID_API_PROXY_RETRY_CLASSES = [
50
37
  'rate_limited',
@@ -566,14 +553,35 @@ export function validateWorkflowKitConfig(wk, routing, roles) {
566
553
  continue;
567
554
  }
568
555
 
569
- if (!Array.isArray(phaseConfig.artifacts)) {
556
+ let templateValid = true;
557
+ if (phaseConfig.template !== undefined) {
558
+ if (typeof phaseConfig.template !== 'string' || !phaseConfig.template.trim()) {
559
+ errors.push(`workflow_kit.phases.${phase}.template must be a non-empty string`);
560
+ templateValid = false;
561
+ } else if (!isWorkflowKitPhaseTemplateId(phaseConfig.template)) {
562
+ errors.push(
563
+ `workflow_kit.phases.${phase}.template "${phaseConfig.template}" is unknown; valid values: ${VALID_WORKFLOW_KIT_PHASE_TEMPLATE_IDS.join(', ')}`,
564
+ );
565
+ templateValid = false;
566
+ }
567
+ }
568
+
569
+ if (phaseConfig.artifacts !== undefined && !Array.isArray(phaseConfig.artifacts)) {
570
570
  errors.push(`workflow_kit.phases.${phase}.artifacts must be an array`);
571
571
  continue;
572
572
  }
573
573
 
574
+ if (phaseConfig.template === undefined && phaseConfig.artifacts === undefined) {
575
+ errors.push(`workflow_kit.phases.${phase} must declare template, artifacts, or both`);
576
+ continue;
577
+ }
578
+
574
579
  const seenPaths = new Set();
575
- for (let i = 0; i < phaseConfig.artifacts.length; i++) {
576
- const artifact = phaseConfig.artifacts[i];
580
+ const expandedArtifacts = templateValid
581
+ ? expandWorkflowKitPhaseArtifacts(phaseConfig)
582
+ : Array.isArray(phaseConfig.artifacts) ? phaseConfig.artifacts : [];
583
+ for (let i = 0; i < expandedArtifacts.length; i++) {
584
+ const artifact = expandedArtifacts[i];
577
585
  const prefix = `workflow_kit.phases.${phase}.artifacts[${i}]`;
578
586
 
579
587
  if (!artifact || typeof artifact !== 'object') {
@@ -845,7 +853,7 @@ export function getMaxConcurrentTurns(config, phase) {
845
853
 
846
854
  /**
847
855
  * Normalize workflow_kit config.
848
- * When absent, builds defaults from routing phases using DEFAULT_PHASE_ARTIFACTS.
856
+ * When absent, builds defaults from routing phases using the built-in phase templates.
849
857
  * When present, normalizes artifact entries.
850
858
  */
851
859
  export function normalizeWorkflowKit(raw, routingPhases) {
@@ -862,7 +870,7 @@ export function normalizeWorkflowKit(raw, routingPhases) {
862
870
  if (raw.phases) {
863
871
  for (const [phase, phaseConfig] of Object.entries(raw.phases)) {
864
872
  phases[phase] = {
865
- artifacts: (phaseConfig.artifacts || []).map(a => ({
873
+ artifacts: expandWorkflowKitPhaseArtifacts(phaseConfig).map(a => ({
866
874
  path: a.path,
867
875
  semantics: a.semantics || null,
868
876
  semantics_config: a.semantics_config || null,
@@ -879,9 +887,10 @@ export function normalizeWorkflowKit(raw, routingPhases) {
879
887
  function buildDefaultWorkflowKit(routingPhases) {
880
888
  const phases = {};
881
889
  for (const phase of routingPhases) {
882
- if (DEFAULT_PHASE_ARTIFACTS[phase]) {
890
+ const templateArtifacts = buildDefaultWorkflowKitArtifactsForPhase(phase);
891
+ if (templateArtifacts) {
883
892
  phases[phase] = {
884
- artifacts: DEFAULT_PHASE_ARTIFACTS[phase].map(a => ({ ...a, semantics_config: null })),
893
+ artifacts: templateArtifacts.map(a => ({ ...a, semantics_config: a.semantics_config || null })),
885
894
  };
886
895
  }
887
896
  }
@@ -0,0 +1,102 @@
1
+ function cloneJsonCompatible(value) {
2
+ return value == null ? value : JSON.parse(JSON.stringify(value));
3
+ }
4
+
5
+ const WORKFLOW_KIT_PHASE_TEMPLATE_REGISTRY = Object.freeze({
6
+ 'planning-default': {
7
+ description: 'Core planning proof surface for governed repos.',
8
+ artifacts: [
9
+ { path: '.planning/PM_SIGNOFF.md', semantics: 'pm_signoff', required: true },
10
+ { path: '.planning/SYSTEM_SPEC.md', semantics: 'system_spec', required: true },
11
+ { path: '.planning/ROADMAP.md', semantics: null, required: true },
12
+ ],
13
+ },
14
+ 'implementation-default': {
15
+ description: 'Implementation proof surface for governed repos.',
16
+ artifacts: [
17
+ { path: '.planning/IMPLEMENTATION_NOTES.md', semantics: 'implementation_notes', required: true },
18
+ ],
19
+ },
20
+ 'qa-default': {
21
+ description: 'QA and ship-verdict proof surface for governed repos.',
22
+ artifacts: [
23
+ { path: '.planning/acceptance-matrix.md', semantics: 'acceptance_matrix', required: true },
24
+ { path: '.planning/ship-verdict.md', semantics: 'ship_verdict', required: true },
25
+ { path: '.planning/RELEASE_NOTES.md', semantics: 'release_notes', required: true },
26
+ ],
27
+ },
28
+ 'architecture-review': {
29
+ description: 'Structured architecture-review document with required sections.',
30
+ artifacts: [
31
+ {
32
+ path: '.planning/ARCHITECTURE.md',
33
+ semantics: 'section_check',
34
+ semantics_config: {
35
+ required_sections: ['## Context', '## Proposed Design', '## Trade-offs', '## Risks'],
36
+ },
37
+ required: true,
38
+ },
39
+ ],
40
+ },
41
+ 'security-review': {
42
+ description: 'Structured security-review document with required sections.',
43
+ artifacts: [
44
+ {
45
+ path: '.planning/SECURITY_REVIEW.md',
46
+ semantics: 'section_check',
47
+ semantics_config: {
48
+ required_sections: ['## Threat Model', '## Findings', '## Verdict'],
49
+ },
50
+ required: true,
51
+ },
52
+ ],
53
+ },
54
+ });
55
+
56
+ export const VALID_WORKFLOW_KIT_PHASE_TEMPLATE_IDS = Object.freeze(
57
+ Object.keys(WORKFLOW_KIT_PHASE_TEMPLATE_REGISTRY),
58
+ );
59
+
60
+ const DEFAULT_WORKFLOW_KIT_PHASE_TEMPLATE_BY_PHASE = Object.freeze({
61
+ planning: 'planning-default',
62
+ implementation: 'implementation-default',
63
+ qa: 'qa-default',
64
+ });
65
+
66
+ export function listWorkflowKitPhaseTemplates() {
67
+ return VALID_WORKFLOW_KIT_PHASE_TEMPLATE_IDS.map((id) => loadWorkflowKitPhaseTemplate(id));
68
+ }
69
+
70
+ export function isWorkflowKitPhaseTemplateId(templateId) {
71
+ return VALID_WORKFLOW_KIT_PHASE_TEMPLATE_IDS.includes(templateId);
72
+ }
73
+
74
+ export function loadWorkflowKitPhaseTemplate(templateId) {
75
+ if (!isWorkflowKitPhaseTemplateId(templateId)) {
76
+ throw new Error(
77
+ `Unknown workflow-kit phase template "${templateId}". Valid templates: ${VALID_WORKFLOW_KIT_PHASE_TEMPLATE_IDS.join(', ')}`,
78
+ );
79
+ }
80
+
81
+ const template = WORKFLOW_KIT_PHASE_TEMPLATE_REGISTRY[templateId];
82
+ return {
83
+ id: templateId,
84
+ description: template.description,
85
+ artifacts: cloneJsonCompatible(template.artifacts),
86
+ };
87
+ }
88
+
89
+ export function expandWorkflowKitPhaseArtifacts(phaseConfig = {}) {
90
+ const templateArtifacts = phaseConfig.template
91
+ ? loadWorkflowKitPhaseTemplate(phaseConfig.template).artifacts
92
+ : [];
93
+ const explicitArtifacts = Array.isArray(phaseConfig.artifacts)
94
+ ? cloneJsonCompatible(phaseConfig.artifacts)
95
+ : [];
96
+ return [...templateArtifacts, ...explicitArtifacts];
97
+ }
98
+
99
+ export function buildDefaultWorkflowKitArtifactsForPhase(phase) {
100
+ const templateId = DEFAULT_WORKFLOW_KIT_PHASE_TEMPLATE_BY_PHASE[phase];
101
+ return templateId ? loadWorkflowKitPhaseTemplate(templateId).artifacts : null;
102
+ }