@jaimevalasek/aioson 1.30.2 → 1.36.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/CHANGELOG.md +75 -0
- package/README.md +19 -6
- package/docs/en/1-understand/ecosystem-map.md +45 -29
- package/docs/en/1-understand/glossary.md +5 -5
- package/docs/en/1-understand/what-is-aioson.md +5 -5
- package/docs/en/2-start/existing-project.md +7 -7
- package/docs/en/2-start/first-project.md +32 -38
- package/docs/en/2-start/initial-decisions.md +18 -17
- package/docs/en/3-recipes/README.md +2 -2
- package/docs/en/3-recipes/continuity-between-sessions.md +2 -2
- package/docs/en/3-recipes/from-idea-to-prd-via-briefing.md +5 -2
- package/docs/en/3-recipes/full-feature-with-sheldon.md +327 -338
- package/docs/en/4-agents/README.md +28 -14
- package/docs/en/4-agents/discovery-design-doc.md +20 -8
- package/docs/en/4-agents/forge-run.md +3 -2
- package/docs/en/5-reference/cli-reference.md +51 -48
- package/docs/en/5-reference/executable-verification.md +10 -7
- package/docs/en/5-reference/parallel.md +2 -0
- package/docs/en/5-reference/qa-browser.md +2 -2
- package/docs/en/README.md +3 -3
- package/docs/pt/1-entender/glossario.md +4 -4
- package/docs/pt/1-entender/mapa-do-ecossistema.md +32 -21
- package/docs/pt/2-comecar/decisoes-iniciais.md +16 -16
- package/docs/pt/2-comecar/primeiro-projeto.md +1 -1
- package/docs/pt/3-receitas/README.md +1 -1
- package/docs/pt/3-receitas/app-saas-do-zero.md +35 -122
- package/docs/pt/3-receitas/feature-completa-com-sheldon.md +289 -338
- package/docs/pt/4-agentes/analyst.md +9 -5
- package/docs/pt/4-agentes/architect.md +9 -5
- package/docs/pt/4-agentes/dev.md +19 -7
- package/docs/pt/4-agentes/discovery-design-doc.md +25 -23
- package/docs/pt/4-agentes/orchestrator.md +164 -118
- package/docs/pt/4-agentes/pentester.md +5 -5
- package/docs/pt/4-agentes/pm.md +15 -7
- package/docs/pt/4-agentes/sheldon.md +166 -99
- package/docs/pt/4-agentes/ux-ui.md +7 -4
- package/docs/pt/5-referencia/agent-chain-continuity.md +14 -5
- package/docs/pt/5-referencia/comandos-cli.md +1 -1
- package/docs/pt/5-referencia/harness-retro.md +2 -1
- package/docs/pt/5-referencia/sdd-automation-scripts.md +6 -6
- package/docs/pt/5-referencia/sdd-framework.md +53 -16
- package/docs/pt/5-referencia/sdd-planos-e-estrutura.md +27 -15
- package/docs/pt/README.md +4 -4
- package/docs/pt/agentes.md +48 -50
- package/package.json +2 -2
- package/src/artifact-kinds.js +110 -0
- package/src/cli.js +82 -42
- package/src/commands/agent-epilogue.js +251 -186
- package/src/commands/agents.js +104 -48
- package/src/commands/audit-code.js +344 -0
- package/src/commands/classify.js +75 -13
- package/src/commands/feature-close.js +43 -18
- package/src/commands/harness-check.js +259 -175
- package/src/commands/harness-retro-promote.js +387 -0
- package/src/commands/live.js +24 -0
- package/src/commands/prototype-check.js +163 -0
- package/src/commands/review-feature.js +189 -0
- package/src/commands/runtime.js +81 -66
- package/src/commands/sync-agents-copy.js +115 -0
- package/src/commands/verification-plan.js +116 -0
- package/src/commands/verify-artifact.js +530 -0
- package/src/commands/verify-implementation.js +428 -0
- package/src/commands/workflow-execute.js +309 -309
- package/src/commands/workflow-next.js +361 -19
- package/src/commands/workflow-plan.js +5 -5
- package/src/constants.js +4 -0
- package/src/gateway-pointer-merge.js +7 -1
- package/src/handoff-contract.js +267 -172
- package/src/harness/contract-integrity-gate.js +172 -0
- package/src/harness/contract-integrity.js +111 -0
- package/src/harness/contract-schema.js +377 -332
- package/src/harness/detect-runtime-feature.js +90 -0
- package/src/harness/static-criteria.js +192 -0
- package/src/i18n/messages/en.js +8 -4
- package/src/i18n/messages/es.js +8 -4
- package/src/i18n/messages/fr.js +8 -4
- package/src/i18n/messages/pt-BR.js +8 -4
- package/src/install-wizard.js +8 -8
- package/src/installer.js +13 -6
- package/src/lib/retro/retro-render.js +10 -1
- package/src/lib/retro/retro-sources.js +45 -27
- package/src/lib/retro/verification-reports.js +230 -0
- package/src/parser.js +6 -0
- package/src/preflight-engine.js +12 -12
- package/src/runtime-store.js +13 -9
- package/src/verification/evidence-bundle.js +251 -0
- package/src/verification/ledger-store.js +221 -0
- package/src/verification/path-policy.js +74 -0
- package/src/verification/policy-engine.js +95 -0
- package/src/verification/prompt-package.js +314 -0
- package/src/verification/redaction.js +77 -0
- package/src/verification/report-parser.js +132 -0
- package/src/verification/report-store.js +97 -0
- package/src/verification/result.js +16 -0
- package/src/verification/runners/index.js +319 -0
- package/src/verification/runtime-telemetry.js +144 -0
- package/src/verification/schema.js +276 -0
- package/src/verification/source-discovery.js +153 -0
- package/src/verification-policy.js +398 -0
- package/src/version.js +52 -1
- package/template/.aioson/agents/analyst.md +9 -6
- package/template/.aioson/agents/architect.md +34 -5
- package/template/.aioson/agents/briefing-refiner.md +25 -0
- package/template/.aioson/agents/briefing.md +69 -12
- package/template/.aioson/agents/committer.md +2 -1
- package/template/.aioson/agents/copywriter.md +30 -21
- package/template/.aioson/agents/design-hybrid-forge.md +28 -15
- package/template/.aioson/agents/dev.md +28 -10
- package/template/.aioson/agents/deyvin.md +3 -2
- package/template/.aioson/agents/discover.md +12 -3
- package/template/.aioson/agents/discovery-design-doc.md +7 -2
- package/template/.aioson/agents/genome.md +19 -10
- package/template/.aioson/agents/neo.md +30 -30
- package/template/.aioson/agents/orache.md +20 -11
- package/template/.aioson/agents/orchestrator.md +84 -7
- package/template/.aioson/agents/pm.md +8 -8
- package/template/.aioson/agents/product.md +15 -11
- package/template/.aioson/agents/profiler-enricher.md +20 -11
- package/template/.aioson/agents/profiler-forge.md +20 -11
- package/template/.aioson/agents/profiler-researcher.md +20 -11
- package/template/.aioson/agents/qa.md +87 -17
- package/template/.aioson/agents/scope-check.md +19 -5
- package/template/.aioson/agents/setup.md +12 -1
- package/template/.aioson/agents/sheldon.md +92 -9
- package/template/.aioson/agents/site-forge.md +11 -2
- package/template/.aioson/agents/squad.md +20 -5
- package/template/.aioson/agents/ux-ui.md +4 -2
- package/template/.aioson/agents/validator.md +33 -1
- package/template/.aioson/config/verification.json +61 -0
- package/template/.aioson/config.md +13 -9
- package/template/.aioson/docs/LAYERS.md +2 -2
- package/template/.aioson/docs/autopilot-handoff.md +10 -10
- package/template/.aioson/docs/dev/execution-discipline.md +41 -0
- package/template/.aioson/docs/dev/phase-loop.md +47 -0
- package/template/.aioson/docs/feature-expansion-taxonomy.md +31 -3
- package/template/.aioson/docs/presets/workflow.config.full-merged.json +15 -0
- package/template/.aioson/docs/presets/workflow.config.lean.json +15 -0
- package/template/.aioson/docs/product/prd-contract.md +12 -12
- package/template/.aioson/docs/prototype-contract.md +98 -0
- package/template/.aioson/docs/reference-identity.md +94 -0
- package/template/.aioson/docs/sheldon/harness-contract.md +155 -28
- package/template/.aioson/docs/verification-config.md +82 -0
- package/template/.aioson/docs/verify-artifact-gates.md +91 -0
- package/template/.aioson/docs/workflow-lean-lane.md +129 -0
- package/template/.aioson/skills/design/interface-design/references/intent-and-domain.md +2 -0
- package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +16 -14
- package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +2 -2
- package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +4 -4
- package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +15 -15
- package/template/.aioson/skills/process/briefing-expansion-scout/SKILL.md +25 -3
- package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +5 -3
- package/template/.aioson/skills/process/design-hybrid-forge/references/external-source-ingestion.md +89 -0
- package/template/.aioson/skills/process/product-scope-expansion/SKILL.md +29 -2
- package/template/.aioson/skills/process/prototype-forge/SKILL.md +98 -0
- package/template/.aioson/skills/process/reference-identity-extract/SKILL.md +164 -0
- package/template/.aioson/skills/process/sheldon-expansion-audit/SKILL.md +23 -2
- package/template/.aioson/skills/static/multi-agent-patterns.md +5 -5
- package/template/CLAUDE.md +15 -11
|
@@ -1,186 +1,251 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const path = require('node:path');
|
|
4
|
-
const { runPulseUpdate } = require('./pulse-update');
|
|
5
|
-
const { runDossierAddFinding } = require('./dossier');
|
|
6
|
-
const { runGateApprove } = require('./gate-approve');
|
|
7
|
-
const { runAgentDone } = require('./runtime');
|
|
8
|
-
|
|
9
|
-
function resolveTargetDir(args) {
|
|
10
|
-
return path.resolve(process.cwd(), args[0] || '.');
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function normalizeAgent(value) {
|
|
14
|
-
return String(value || '').trim().replace(/^@/, '');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function normalizeList(value) {
|
|
18
|
-
if (!value) return [];
|
|
19
|
-
if (Array.isArray(value)) return value.map(String).map((item) => item.trim()).filter(Boolean);
|
|
20
|
-
return String(value).split(',').map((item) => item.trim()).filter(Boolean);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function makeSilentLogger() {
|
|
24
|
-
return { log() {}, error() {}, warn() {} };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function pushStep(steps, name, result) {
|
|
28
|
-
steps.push({
|
|
29
|
-
name,
|
|
30
|
-
ok: Boolean(result && result.ok),
|
|
31
|
-
skipped: Boolean(result && result.skipped),
|
|
32
|
-
reason: result && (result.reason || result.error || null)
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function formatAutoAdvance(autoAdvance) {
|
|
37
|
-
if (!autoAdvance) return null;
|
|
38
|
-
if (autoAdvance.advanced) {
|
|
39
|
-
const nextStage = autoAdvance.result && (autoAdvance.result.next || autoAdvance.result.nextStage);
|
|
40
|
-
return `workflow auto-advanced${nextStage ? ` -> ${nextStage}` : ''}`;
|
|
41
|
-
}
|
|
42
|
-
return `workflow skip: ${autoAdvance.skipped || 'not_advanced'}`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function runAgentEpilogue({ args, options = {}, logger, t }) {
|
|
46
|
-
const targetDir = resolveTargetDir(args);
|
|
47
|
-
const agent = normalizeAgent(options.agent);
|
|
48
|
-
const summary = String(options.summary || options.message || '').trim();
|
|
49
|
-
const feature = options.feature ? String(options.feature).trim() : null;
|
|
50
|
-
const action = options.action ? String(options.action).trim() : summary;
|
|
51
|
-
const next = options.next ? String(options.next).trim() : null;
|
|
52
|
-
const gate = options.gate ? String(options.gate).trim() : null;
|
|
53
|
-
const approveGate = options['approve-gate'] || options.approveGate
|
|
54
|
-
? String(options['approve-gate'] || options.approveGate).trim().toUpperCase()
|
|
55
|
-
: null;
|
|
56
|
-
const verdict = options.verdict ? String(options.verdict).trim().toUpperCase() : null;
|
|
57
|
-
const artifacts = normalizeList(options.artifacts);
|
|
58
|
-
const strict = Boolean(options.strict);
|
|
59
|
-
const steps = [];
|
|
60
|
-
const errors = [];
|
|
61
|
-
const silentLogger = makeSilentLogger();
|
|
62
|
-
|
|
63
|
-
if (!agent) {
|
|
64
|
-
const failure = { ok: false, reason: 'missing_agent' };
|
|
65
|
-
if (options.json) return failure;
|
|
66
|
-
logger.error('--agent=<agent> is required.');
|
|
67
|
-
return failure;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (!summary) {
|
|
71
|
-
const failure = { ok: false, reason: 'missing_summary' };
|
|
72
|
-
if (options.json) return failure;
|
|
73
|
-
logger.error('--summary="<summary>" is required.');
|
|
74
|
-
return failure;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (approveGate) {
|
|
78
|
-
const gateResult = await runGateApprove({
|
|
79
|
-
args: [targetDir],
|
|
80
|
-
options: {
|
|
81
|
-
feature,
|
|
82
|
-
gate: approveGate,
|
|
83
|
-
agent,
|
|
84
|
-
json: true
|
|
85
|
-
},
|
|
86
|
-
logger: silentLogger
|
|
87
|
-
});
|
|
88
|
-
pushStep(steps, 'gate:approve', gateResult);
|
|
89
|
-
if (!gateResult.ok) {
|
|
90
|
-
errors.push({ step: 'gate:approve', reason: gateResult.reason || 'gate_failed', result: gateResult });
|
|
91
|
-
if (strict) {
|
|
92
|
-
const failure = { ok: false, reason: 'gate_approve_failed', steps, errors };
|
|
93
|
-
if (options.json) return failure;
|
|
94
|
-
logger.error(`agent:epilogue blocked: gate ${approveGate} approval failed.`);
|
|
95
|
-
return failure;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (!options['no-pulse'] && !options.noPulse) {
|
|
101
|
-
const pulseResult = await runPulseUpdate({
|
|
102
|
-
args: [targetDir],
|
|
103
|
-
options: {
|
|
104
|
-
agent,
|
|
105
|
-
...(feature ? { feature } : {}),
|
|
106
|
-
...(gate || approveGate ? { gate: gate || `Gate ${approveGate}: approved` } : {}),
|
|
107
|
-
...(action ? { action } : {}),
|
|
108
|
-
...(next ? { next } : {}),
|
|
109
|
-
...(verdict ? { verdict } : {}),
|
|
110
|
-
json: true
|
|
111
|
-
},
|
|
112
|
-
logger: silentLogger
|
|
113
|
-
});
|
|
114
|
-
pushStep(steps, 'pulse:update', pulseResult);
|
|
115
|
-
if (!pulseResult.ok) {
|
|
116
|
-
errors.push({ step: 'pulse:update', reason: pulseResult.reason || 'pulse_failed', result: pulseResult });
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
pushStep(steps, 'pulse:update', { ok: true, skipped: true, reason: 'disabled' });
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (feature && !options['no-dossier'] && !options.noDossier) {
|
|
123
|
-
const dossierResult = await runDossierAddFinding({
|
|
124
|
-
args: [targetDir],
|
|
125
|
-
options: {
|
|
126
|
-
slug: feature,
|
|
127
|
-
agent,
|
|
128
|
-
section: options.section ? String(options.section) : 'Agent Trail',
|
|
129
|
-
content: options.content ? String(options.content) : summary,
|
|
130
|
-
json: true
|
|
131
|
-
},
|
|
132
|
-
logger: silentLogger
|
|
133
|
-
});
|
|
134
|
-
pushStep(steps, 'dossier:add-finding', dossierResult);
|
|
135
|
-
if (!dossierResult.ok) {
|
|
136
|
-
errors.push({ step: 'dossier:add-finding', reason: dossierResult.reason || 'dossier_failed', result: dossierResult });
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
pushStep(steps, 'dossier:add-finding', { ok: true, skipped: true, reason: feature ? 'disabled' : 'missing_feature' });
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const doneResult = await runAgentDone({
|
|
143
|
-
args: [targetDir],
|
|
144
|
-
options: {
|
|
145
|
-
agent,
|
|
146
|
-
summary,
|
|
147
|
-
...(feature ? { feature } : {}),
|
|
148
|
-
...(verdict ? { verdict } : {}),
|
|
149
|
-
...(artifacts.length > 0 ? { artifacts: artifacts.join(',') } : {}),
|
|
150
|
-
json: true
|
|
151
|
-
},
|
|
152
|
-
logger: silentLogger,
|
|
153
|
-
t
|
|
154
|
-
});
|
|
155
|
-
pushStep(steps, 'agent:done', doneResult);
|
|
156
|
-
if (!doneResult.ok) {
|
|
157
|
-
errors.push({ step: 'agent:done', reason: doneResult.reason || doneResult.error || 'agent_done_failed', result: doneResult });
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { runPulseUpdate } = require('./pulse-update');
|
|
5
|
+
const { runDossierAddFinding } = require('./dossier');
|
|
6
|
+
const { runGateApprove } = require('./gate-approve');
|
|
7
|
+
const { runAgentDone } = require('./runtime');
|
|
8
|
+
|
|
9
|
+
function resolveTargetDir(args) {
|
|
10
|
+
return path.resolve(process.cwd(), args[0] || '.');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeAgent(value) {
|
|
14
|
+
return String(value || '').trim().replace(/^@/, '');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeList(value) {
|
|
18
|
+
if (!value) return [];
|
|
19
|
+
if (Array.isArray(value)) return value.map(String).map((item) => item.trim()).filter(Boolean);
|
|
20
|
+
return String(value).split(',').map((item) => item.trim()).filter(Boolean);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function makeSilentLogger() {
|
|
24
|
+
return { log() {}, error() {}, warn() {} };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function pushStep(steps, name, result) {
|
|
28
|
+
steps.push({
|
|
29
|
+
name,
|
|
30
|
+
ok: Boolean(result && result.ok),
|
|
31
|
+
skipped: Boolean(result && result.skipped),
|
|
32
|
+
reason: result && (result.reason || result.error || null)
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function formatAutoAdvance(autoAdvance) {
|
|
37
|
+
if (!autoAdvance) return null;
|
|
38
|
+
if (autoAdvance.advanced) {
|
|
39
|
+
const nextStage = autoAdvance.result && (autoAdvance.result.next || autoAdvance.result.nextStage);
|
|
40
|
+
return `workflow auto-advanced${nextStage ? ` -> ${nextStage}` : ''}`;
|
|
41
|
+
}
|
|
42
|
+
return `workflow skip: ${autoAdvance.skipped || 'not_advanced'}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function runAgentEpilogue({ args, options = {}, logger, t }) {
|
|
46
|
+
const targetDir = resolveTargetDir(args);
|
|
47
|
+
const agent = normalizeAgent(options.agent);
|
|
48
|
+
const summary = String(options.summary || options.message || '').trim();
|
|
49
|
+
const feature = options.feature ? String(options.feature).trim() : null;
|
|
50
|
+
const action = options.action ? String(options.action).trim() : summary;
|
|
51
|
+
const next = options.next ? String(options.next).trim() : null;
|
|
52
|
+
const gate = options.gate ? String(options.gate).trim() : null;
|
|
53
|
+
const approveGate = options['approve-gate'] || options.approveGate
|
|
54
|
+
? String(options['approve-gate'] || options.approveGate).trim().toUpperCase()
|
|
55
|
+
: null;
|
|
56
|
+
const verdict = options.verdict ? String(options.verdict).trim().toUpperCase() : null;
|
|
57
|
+
const artifacts = normalizeList(options.artifacts);
|
|
58
|
+
const strict = Boolean(options.strict);
|
|
59
|
+
const steps = [];
|
|
60
|
+
const errors = [];
|
|
61
|
+
const silentLogger = makeSilentLogger();
|
|
62
|
+
|
|
63
|
+
if (!agent) {
|
|
64
|
+
const failure = { ok: false, reason: 'missing_agent' };
|
|
65
|
+
if (options.json) return failure;
|
|
66
|
+
logger.error('--agent=<agent> is required.');
|
|
67
|
+
return failure;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!summary) {
|
|
71
|
+
const failure = { ok: false, reason: 'missing_summary' };
|
|
72
|
+
if (options.json) return failure;
|
|
73
|
+
logger.error('--summary="<summary>" is required.');
|
|
74
|
+
return failure;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (approveGate) {
|
|
78
|
+
const gateResult = await runGateApprove({
|
|
79
|
+
args: [targetDir],
|
|
80
|
+
options: {
|
|
81
|
+
feature,
|
|
82
|
+
gate: approveGate,
|
|
83
|
+
agent,
|
|
84
|
+
json: true
|
|
85
|
+
},
|
|
86
|
+
logger: silentLogger
|
|
87
|
+
});
|
|
88
|
+
pushStep(steps, 'gate:approve', gateResult);
|
|
89
|
+
if (!gateResult.ok) {
|
|
90
|
+
errors.push({ step: 'gate:approve', reason: gateResult.reason || 'gate_failed', result: gateResult });
|
|
91
|
+
if (strict) {
|
|
92
|
+
const failure = { ok: false, reason: 'gate_approve_failed', steps, errors };
|
|
93
|
+
if (options.json) return failure;
|
|
94
|
+
logger.error(`agent:epilogue blocked: gate ${approveGate} approval failed.`);
|
|
95
|
+
return failure;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!options['no-pulse'] && !options.noPulse) {
|
|
101
|
+
const pulseResult = await runPulseUpdate({
|
|
102
|
+
args: [targetDir],
|
|
103
|
+
options: {
|
|
104
|
+
agent,
|
|
105
|
+
...(feature ? { feature } : {}),
|
|
106
|
+
...(gate || approveGate ? { gate: gate || `Gate ${approveGate}: approved` } : {}),
|
|
107
|
+
...(action ? { action } : {}),
|
|
108
|
+
...(next ? { next } : {}),
|
|
109
|
+
...(verdict ? { verdict } : {}),
|
|
110
|
+
json: true
|
|
111
|
+
},
|
|
112
|
+
logger: silentLogger
|
|
113
|
+
});
|
|
114
|
+
pushStep(steps, 'pulse:update', pulseResult);
|
|
115
|
+
if (!pulseResult.ok) {
|
|
116
|
+
errors.push({ step: 'pulse:update', reason: pulseResult.reason || 'pulse_failed', result: pulseResult });
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
pushStep(steps, 'pulse:update', { ok: true, skipped: true, reason: 'disabled' });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (feature && !options['no-dossier'] && !options.noDossier) {
|
|
123
|
+
const dossierResult = await runDossierAddFinding({
|
|
124
|
+
args: [targetDir],
|
|
125
|
+
options: {
|
|
126
|
+
slug: feature,
|
|
127
|
+
agent,
|
|
128
|
+
section: options.section ? String(options.section) : 'Agent Trail',
|
|
129
|
+
content: options.content ? String(options.content) : summary,
|
|
130
|
+
json: true
|
|
131
|
+
},
|
|
132
|
+
logger: silentLogger
|
|
133
|
+
});
|
|
134
|
+
pushStep(steps, 'dossier:add-finding', dossierResult);
|
|
135
|
+
if (!dossierResult.ok) {
|
|
136
|
+
errors.push({ step: 'dossier:add-finding', reason: dossierResult.reason || 'dossier_failed', result: dossierResult });
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
pushStep(steps, 'dossier:add-finding', { ok: true, skipped: true, reason: feature ? 'disabled' : 'missing_feature' });
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const doneResult = await runAgentDone({
|
|
143
|
+
args: [targetDir],
|
|
144
|
+
options: {
|
|
145
|
+
agent,
|
|
146
|
+
summary,
|
|
147
|
+
...(feature ? { feature } : {}),
|
|
148
|
+
...(verdict ? { verdict } : {}),
|
|
149
|
+
...(artifacts.length > 0 ? { artifacts: artifacts.join(',') } : {}),
|
|
150
|
+
json: true
|
|
151
|
+
},
|
|
152
|
+
logger: silentLogger,
|
|
153
|
+
t
|
|
154
|
+
});
|
|
155
|
+
pushStep(steps, 'agent:done', doneResult);
|
|
156
|
+
if (!doneResult.ok) {
|
|
157
|
+
errors.push({ step: 'agent:done', reason: doneResult.reason || doneResult.error || 'agent_done_failed', result: doneResult });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Surface the advisory artifact done-gate that agent:done resolved for this
|
|
161
|
+
// agent (peripheral artifact-producing agents only; null otherwise). Advisory:
|
|
162
|
+
// a skipped/failed check is shown but never added to `errors`, so it cannot
|
|
163
|
+
// flip `ok`.
|
|
164
|
+
if (doneResult && doneResult.verify_artifact) {
|
|
165
|
+
const va = doneResult.verify_artifact;
|
|
166
|
+
pushStep(steps, 'verify:artifact', { ok: va.skipped ? true : va.ok, skipped: va.skipped, reason: va.reason });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Advisory contract-integrity signal for untracked (prompt-only) dev/qa
|
|
170
|
+
// completions. The tracked `workflow:next --complete` / `feature:close` paths
|
|
171
|
+
// enforce this as a HARD gate; a direct Claude Code session never calls them,
|
|
172
|
+
// so we surface the same signal here without blocking the best-effort
|
|
173
|
+
// epilogue (never added to `errors`, so it cannot flip `ok`).
|
|
174
|
+
if ((agent === 'dev' || agent === 'qa') && feature) {
|
|
175
|
+
let advisory = null;
|
|
176
|
+
try {
|
|
177
|
+
const { evaluateContractIntegrityGate } = require('../harness/contract-integrity-gate');
|
|
178
|
+
advisory = await evaluateContractIntegrityGate(targetDir, feature, { runChecks: false });
|
|
179
|
+
} catch {
|
|
180
|
+
advisory = null;
|
|
181
|
+
}
|
|
182
|
+
if (advisory && advisory.ok === false) {
|
|
183
|
+
pushStep(steps, 'contract:integrity', {
|
|
184
|
+
ok: false,
|
|
185
|
+
reason: `${advisory.errors.map((e) => e.code).join(', ')} — advisory only; the tracked workflow blocks on this. Run: aioson harness:check . --slug=${feature} --json`
|
|
186
|
+
});
|
|
187
|
+
} else if (advisory) {
|
|
188
|
+
pushStep(steps, 'contract:integrity', { ok: true });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Advisory code-quality signal (audit:code) over the CHANGED files for dev/qa
|
|
193
|
+
// completions — the non-security categories (anti-patterns, TODOs, dead code,
|
|
194
|
+
// duplication). Scoped to the diff so it is about the work just done; advisory
|
|
195
|
+
// only (never added to `errors`, so it cannot flip `ok`). This is what makes the
|
|
196
|
+
// scan auto-fire in the flow instead of relying on the agent to remember to run
|
|
197
|
+
// it; @qa still treats a HIGH as a Gate-D blocker.
|
|
198
|
+
if (agent === 'dev' || agent === 'qa') {
|
|
199
|
+
let codeAudit = null;
|
|
200
|
+
try {
|
|
201
|
+
const { runAuditCode } = require('./audit-code');
|
|
202
|
+
codeAudit = await runAuditCode({
|
|
203
|
+
args: [targetDir],
|
|
204
|
+
options: { changed: true, json: true, suppressExitCode: true },
|
|
205
|
+
logger: silentLogger
|
|
206
|
+
});
|
|
207
|
+
} catch {
|
|
208
|
+
codeAudit = null;
|
|
209
|
+
}
|
|
210
|
+
if (codeAudit && codeAudit.by_severity && codeAudit.scanned_files > 0) {
|
|
211
|
+
const high = codeAudit.by_severity.HIGH || 0;
|
|
212
|
+
const med = codeAudit.by_severity.MED || 0;
|
|
213
|
+
if (high > 0) {
|
|
214
|
+
const cats = Object.keys(codeAudit.by_category || {}).join(', ');
|
|
215
|
+
pushStep(steps, 'audit:code', {
|
|
216
|
+
ok: false,
|
|
217
|
+
reason: `${high} HIGH${med ? ` / ${med} MED` : ''} in changed files (${cats}) — advisory; @qa treats HIGH as a Gate-D blocker. See .aioson/context/audit-code.json`
|
|
218
|
+
});
|
|
219
|
+
} else {
|
|
220
|
+
pushStep(steps, 'audit:code', { ok: true, reason: med ? `${med} MED advisory` : null });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const ok = doneResult.ok && (strict ? errors.length === 0 : !errors.some((error) => error.step === 'agent:done'));
|
|
226
|
+
const result = {
|
|
227
|
+
ok,
|
|
228
|
+
targetDir,
|
|
229
|
+
agent: `@${agent}`,
|
|
230
|
+
feature,
|
|
231
|
+
summary,
|
|
232
|
+
steps,
|
|
233
|
+
errors,
|
|
234
|
+
agent_done: doneResult
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
if (options.json) return result;
|
|
238
|
+
|
|
239
|
+
logger.log(`agent:epilogue — @${agent} (${ok ? 'ok' : 'issues'})`);
|
|
240
|
+
for (const step of steps) {
|
|
241
|
+
const marker = step.skipped ? 'skip' : step.ok ? 'ok' : 'fail';
|
|
242
|
+
logger.log(` ${marker} ${step.name}${step.reason ? ` (${step.reason})` : ''}`);
|
|
243
|
+
}
|
|
244
|
+
const autoAdvanceMessage = formatAutoAdvance(doneResult.auto_advance);
|
|
245
|
+
if (autoAdvanceMessage) logger.log(` ${autoAdvanceMessage}`);
|
|
246
|
+
return result;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
module.exports = {
|
|
250
|
+
runAgentEpilogue
|
|
251
|
+
};
|
package/src/commands/agents.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const path = require('node:path');
|
|
4
|
+
const fsp = require('node:fs/promises');
|
|
4
5
|
const {
|
|
5
6
|
normalizeAgentName,
|
|
6
7
|
listAgentDefinitions,
|
|
@@ -20,51 +21,102 @@ const { readAutonomyProtocol, resolveEffectiveMode } = require('../autonomy-poli
|
|
|
20
21
|
const { readAgentManifest, buildAgentCapabilitySummary } = require('../agent-manifests');
|
|
21
22
|
const { emitSecurityRuntimeEvent } = require('../lib/security/runtime-events');
|
|
22
23
|
|
|
23
|
-
const WORKFLOW_AGENT_IDS = new Set([
|
|
24
|
-
'setup',
|
|
25
|
-
'product',
|
|
26
|
-
'analyst',
|
|
27
|
-
'scope-check',
|
|
28
|
-
'architect',
|
|
24
|
+
const WORKFLOW_AGENT_IDS = new Set([
|
|
25
|
+
'setup',
|
|
26
|
+
'product',
|
|
27
|
+
'analyst',
|
|
28
|
+
'scope-check',
|
|
29
|
+
'architect',
|
|
29
30
|
'ux-ui',
|
|
30
31
|
'pm',
|
|
31
32
|
'orchestrator',
|
|
32
33
|
'dev',
|
|
33
|
-
'qa'
|
|
34
|
-
]);
|
|
35
|
-
const SCOPE_CHECK_MODES = new Set(['pre-dev', 'post-dev', 'post-fix', 'final']);
|
|
36
|
-
|
|
37
|
-
function normalizeScopeCheckMode(input) {
|
|
38
|
-
const mode = String(input || '').trim().toLowerCase();
|
|
39
|
-
return SCOPE_CHECK_MODES.has(mode) ? mode : null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function getScopeCheckModeOption(options = {}) {
|
|
43
|
-
return normalizeScopeCheckMode(
|
|
44
|
-
options.scopeMode ||
|
|
45
|
-
options['scope-mode'] ||
|
|
46
|
-
options.checkMode ||
|
|
47
|
-
options['check-mode'] ||
|
|
48
|
-
options.mode
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function buildScopeCheckActivationContext(options = {}) {
|
|
53
|
-
const mode = getScopeCheckModeOption(options) || 'pre-dev';
|
|
54
|
-
const lines = [`Scope-check mode: ${mode}.`];
|
|
55
|
-
const featureSlug = String(options.feature || options.slug || '').trim();
|
|
56
|
-
if (featureSlug) lines.push(`Feature slug: ${featureSlug}.`);
|
|
57
|
-
if (mode === 'pre-dev') {
|
|
58
|
-
lines.push('Compare user intent against planning artifacts before implementation.');
|
|
59
|
-
} else if (mode === 'post-dev') {
|
|
60
|
-
lines.push('Compare the approved planning artifacts against the actual implementation diff and changed files before QA.');
|
|
61
|
-
} else if (mode === 'post-fix') {
|
|
62
|
-
lines.push('Compare approved scope, QA/tester/pentester findings, and correction diff; confirm the fix did not change product intent.');
|
|
63
|
-
} else if (mode === 'final') {
|
|
64
|
-
lines.push('Reconcile intent, plan, delivered behavior, and remaining exclusions before close/commit/release.');
|
|
65
|
-
}
|
|
66
|
-
return lines.join('\n');
|
|
67
|
-
}
|
|
34
|
+
'qa'
|
|
35
|
+
]);
|
|
36
|
+
const SCOPE_CHECK_MODES = new Set(['pre-dev', 'post-dev', 'post-fix', 'final']);
|
|
37
|
+
|
|
38
|
+
function normalizeScopeCheckMode(input) {
|
|
39
|
+
const mode = String(input || '').trim().toLowerCase();
|
|
40
|
+
return SCOPE_CHECK_MODES.has(mode) ? mode : null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getScopeCheckModeOption(options = {}) {
|
|
44
|
+
return normalizeScopeCheckMode(
|
|
45
|
+
options.scopeMode ||
|
|
46
|
+
options['scope-mode'] ||
|
|
47
|
+
options.checkMode ||
|
|
48
|
+
options['check-mode'] ||
|
|
49
|
+
options.mode
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function buildScopeCheckActivationContext(options = {}) {
|
|
54
|
+
const mode = getScopeCheckModeOption(options) || 'pre-dev';
|
|
55
|
+
const lines = [`Scope-check mode: ${mode}.`];
|
|
56
|
+
const featureSlug = String(options.feature || options.slug || '').trim();
|
|
57
|
+
if (featureSlug) lines.push(`Feature slug: ${featureSlug}.`);
|
|
58
|
+
if (mode === 'pre-dev') {
|
|
59
|
+
lines.push('Compare user intent against planning artifacts before implementation.');
|
|
60
|
+
} else if (mode === 'post-dev') {
|
|
61
|
+
lines.push('Compare the approved planning artifacts against the actual implementation diff and changed files before QA.');
|
|
62
|
+
} else if (mode === 'post-fix') {
|
|
63
|
+
lines.push('Compare approved scope, QA/tester/pentester findings, and correction diff; confirm the fix did not change product intent.');
|
|
64
|
+
} else if (mode === 'final') {
|
|
65
|
+
lines.push('Reconcile intent, plan, delivered behavior, and remaining exclusions before close/commit/release.');
|
|
66
|
+
}
|
|
67
|
+
return lines.join('\n');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildTesterActivationContext(options = {}) {
|
|
71
|
+
// Lets `agent:prompt tester --feature=<slug>` pin the slug into the prompt.
|
|
72
|
+
// Without it, @tester resolves the slug via `feature:current`, which returns
|
|
73
|
+
// none once a feature is closed — so a standalone post-close test pass would
|
|
74
|
+
// wrongly fall back to project mode. An explicit slug keeps it feature-scoped.
|
|
75
|
+
const featureSlug = String(options.feature || options.slug || '').trim();
|
|
76
|
+
if (!featureSlug) return '';
|
|
77
|
+
return [
|
|
78
|
+
`Feature slug: ${featureSlug}.`,
|
|
79
|
+
`This is a standalone test pass over the already-implemented feature "${featureSlug}"; write test-plan-${featureSlug}.md and test-inventory-${featureSlug}.md.`,
|
|
80
|
+
'The feature may already be closed — do NOT fall back to project mode or pick a different slug.'
|
|
81
|
+
].join('\n');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// True when the project's workflow.config.json routes @sheldon directly into
|
|
85
|
+
// @dev in any sequence — i.e. the lean lane is active. Best-effort + read-only.
|
|
86
|
+
async function detectLeanLaneFromConfig(targetDir) {
|
|
87
|
+
try {
|
|
88
|
+
const configPath = path.join(targetDir, '.aioson', 'context', 'workflow.config.json');
|
|
89
|
+
const config = JSON.parse(await fsp.readFile(configPath, 'utf8'));
|
|
90
|
+
const groups = [config.feature, config.project].filter((g) => g && typeof g === 'object');
|
|
91
|
+
for (const group of groups) {
|
|
92
|
+
for (const seq of Object.values(group)) {
|
|
93
|
+
if (!Array.isArray(seq)) continue;
|
|
94
|
+
const norm = seq.map((s) => String(s || '').trim().toLowerCase().replace(/^@/, ''));
|
|
95
|
+
const i = norm.indexOf('sheldon');
|
|
96
|
+
if (i !== -1 && norm[i + 1] === 'dev') return true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Deterministic lane signal for a directly-activated @sheldon. When the project
|
|
106
|
+
// opted into the lean lane (workflow.config.json routes @sheldon -> @dev) but the
|
|
107
|
+
// session is started by hand (slash / agent:prompt, not workflow:next), nothing
|
|
108
|
+
// otherwise tells @sheldon to run RF-LEAN — so it falls back to enrichment mode
|
|
109
|
+
// and hands to @analyst. This pins the lane into the prompt so the LLM does not
|
|
110
|
+
// have to infer it. Empty string ⇒ full chain (unchanged default behavior).
|
|
111
|
+
async function buildSheldonActivationContext(targetDir, options) {
|
|
112
|
+
if (!(await detectLeanLaneFromConfig(targetDir))) return '';
|
|
113
|
+
const slug = String(options.feature || options.slug || '').trim() || '{slug}';
|
|
114
|
+
return [
|
|
115
|
+
'Active lane: LEAN (workflow.config.json routes @sheldon -> @dev).',
|
|
116
|
+
`Run RF-LEAN as the single spec authority — in this one pass produce requirements-${slug}.md, spec-${slug}.md (gates A/B/C marked approved), design-doc-${slug}.md, readiness-${slug}.md, implementation-plan-${slug}.md (status: approved), and the §2c RG-* harness-contract for a runtime feature.`,
|
|
117
|
+
'Hand off to @dev when done. Do NOT route to @analyst/@architect/@discovery-design-doc/@pm — they are collapsed into you in this lane.'
|
|
118
|
+
].join('\n');
|
|
119
|
+
}
|
|
68
120
|
|
|
69
121
|
function normalizePentesterTargetMode(input) {
|
|
70
122
|
const mode = String(input || '').trim().toLowerCase();
|
|
@@ -207,12 +259,16 @@ async function runAgentPrompt({ args, options, logger, t }) {
|
|
|
207
259
|
|
|
208
260
|
if (!prompt) {
|
|
209
261
|
instructionPath = await resolveExistingInstructionPath(targetDir, promptAgent, locale);
|
|
210
|
-
if (promptAgent.id === 'pentester') {
|
|
211
|
-
pentesterTargetMode = normalizePentesterTargetMode(options.mode);
|
|
212
|
-
activationContext = buildPentesterActivationContext(options, t);
|
|
213
|
-
} else if (promptAgent.id === 'scope-check') {
|
|
214
|
-
activationContext = buildScopeCheckActivationContext(options);
|
|
215
|
-
}
|
|
262
|
+
if (promptAgent.id === 'pentester') {
|
|
263
|
+
pentesterTargetMode = normalizePentesterTargetMode(options.mode);
|
|
264
|
+
activationContext = buildPentesterActivationContext(options, t);
|
|
265
|
+
} else if (promptAgent.id === 'scope-check') {
|
|
266
|
+
activationContext = buildScopeCheckActivationContext(options);
|
|
267
|
+
} else if (promptAgent.id === 'tester') {
|
|
268
|
+
activationContext = buildTesterActivationContext(options);
|
|
269
|
+
} else if (promptAgent.id === 'sheldon') {
|
|
270
|
+
activationContext = await buildSheldonActivationContext(targetDir, options);
|
|
271
|
+
}
|
|
216
272
|
const autonomyProtocol = await readAutonomyProtocol(targetDir);
|
|
217
273
|
const manifest = await readAgentManifest(targetDir, promptAgent.id);
|
|
218
274
|
effectiveMode = resolveEffectiveMode({
|
|
@@ -220,7 +276,7 @@ async function runAgentPrompt({ args, options, logger, t }) {
|
|
|
220
276
|
tool,
|
|
221
277
|
agentId: promptAgent.id,
|
|
222
278
|
manifest,
|
|
223
|
-
requestedMode: promptAgent.id === 'scope-check' && getScopeCheckModeOption(options) ? null : options.mode || null
|
|
279
|
+
requestedMode: promptAgent.id === 'scope-check' && getScopeCheckModeOption(options) ? null : options.mode || null
|
|
224
280
|
});
|
|
225
281
|
prompt = buildAgentPrompt(promptAgent, tool, {
|
|
226
282
|
instructionPath,
|