@jaimevalasek/aioson 1.33.1 → 1.37.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 +76 -0
- package/docs/en/1-understand/ecosystem-map.md +45 -29
- package/docs/en/1-understand/glossary.md +6 -6
- 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 +33 -39
- package/docs/en/2-start/initial-decisions.md +22 -19
- 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 +13 -3
- package/docs/en/3-recipes/full-feature-with-sheldon.md +329 -338
- package/docs/en/4-agents/README.md +33 -16
- package/docs/en/4-agents/briefing-refiner.md +146 -0
- 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/README.md +1 -0
- package/docs/en/5-reference/autopilot-handoff.md +286 -0
- package/docs/en/5-reference/cli-reference.md +57 -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 +5 -5
- package/docs/pt/1-entender/mapa-do-ecossistema.md +33 -22
- package/docs/pt/2-comecar/decisoes-iniciais.md +20 -18
- package/docs/pt/2-comecar/primeiro-projeto.md +2 -2
- 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/da-ideia-ao-prd-via-briefing.md +8 -1
- package/docs/pt/3-receitas/feature-completa-com-sheldon.md +291 -338
- package/docs/pt/4-agentes/README.md +13 -11
- package/docs/pt/4-agentes/analyst.md +9 -5
- package/docs/pt/4-agentes/architect.md +9 -5
- package/docs/pt/4-agentes/briefing-refiner.md +64 -40
- package/docs/pt/4-agentes/briefing.md +6 -1
- package/docs/pt/4-agentes/dev.md +38 -8
- package/docs/pt/4-agentes/deyvin.md +4 -0
- package/docs/pt/4-agentes/discover.md +4 -0
- package/docs/pt/4-agentes/discovery-design-doc.md +25 -23
- package/docs/pt/4-agentes/neo.md +4 -0
- package/docs/pt/4-agentes/orache.md +6 -0
- package/docs/pt/4-agentes/orchestrator.md +176 -118
- package/docs/pt/4-agentes/pentester.md +11 -5
- package/docs/pt/4-agentes/pm.md +15 -7
- package/docs/pt/4-agentes/product.md +19 -1
- package/docs/pt/4-agentes/qa.md +10 -2
- package/docs/pt/4-agentes/setup.md +3 -1
- package/docs/pt/4-agentes/sheldon.md +178 -99
- package/docs/pt/4-agentes/tester.md +6 -0
- package/docs/pt/4-agentes/ux-ui.md +9 -5
- package/docs/pt/5-referencia/README.md +1 -1
- package/docs/pt/5-referencia/agent-chain-continuity.md +14 -5
- package/docs/pt/5-referencia/autopilot-handoff.md +191 -74
- package/docs/pt/5-referencia/comandos-cli.md +16 -7
- 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/5-referencia/skills.md +2 -0
- package/docs/pt/README.md +4 -4
- package/docs/pt/agentes.md +50 -50
- package/package.json +2 -2
- package/src/agents.js +1 -1
- package/src/artifact-kinds.js +111 -0
- package/src/autopilot-signal.js +71 -0
- package/src/cli.js +112 -81
- package/src/commands/agent-epilogue.js +251 -186
- package/src/commands/agents.js +122 -50
- package/src/commands/audit-code.js +344 -0
- package/src/commands/briefing.js +337 -1
- package/src/commands/classify.js +389 -389
- package/src/commands/feature-close.js +122 -4
- package/src/commands/harness-check.js +259 -175
- package/src/commands/live.js +71 -11
- 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/update.js +5 -1
- package/src/commands/verification-plan.js +143 -0
- package/src/commands/verify-artifact.js +593 -0
- package/src/commands/workflow-execute.js +434 -316
- package/src/commands/workflow-next.js +193 -20
- package/src/commands/workflow-plan.js +5 -5
- package/src/constants.js +2 -0
- package/src/doctor.js +4 -2
- 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 +10 -5
- package/src/i18n/messages/es.js +10 -5
- package/src/i18n/messages/fr.js +10 -5
- package/src/i18n/messages/pt-BR.js +10 -5
- package/src/install-wizard.js +8 -8
- package/src/installer.js +13 -6
- package/src/lib/briefing-refiner/apply-feedback.js +18 -4
- package/src/lib/briefing-refiner/feedback-schema.js +73 -4
- package/src/lib/briefing-refiner/refinement-report.js +11 -0
- package/src/lib/briefing-refiner/review-html.js +388 -68
- package/src/parser.js +12 -0
- package/src/preflight-engine.js +12 -12
- package/src/verification/policy-engine.js +95 -95
- package/src/verification-policy.js +398 -0
- package/src/version.js +52 -1
- package/template/.aioson/agents/architect.md +34 -5
- package/template/.aioson/agents/briefing-refiner.md +91 -48
- package/template/.aioson/agents/briefing.md +4 -0
- package/template/.aioson/agents/committer.md +2 -1
- package/template/.aioson/agents/copywriter.md +30 -21
- package/template/.aioson/agents/design-hybrid-forge.md +10 -1
- package/template/.aioson/agents/dev.md +35 -23
- package/template/.aioson/agents/deyvin.md +4 -0
- package/template/.aioson/agents/discover.md +16 -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 +34 -30
- package/template/.aioson/agents/orache.md +24 -11
- package/template/.aioson/agents/orchestrator.md +100 -7
- package/template/.aioson/agents/pentester.md +4 -0
- package/template/.aioson/agents/pm.md +8 -8
- package/template/.aioson/agents/product.md +25 -1
- 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 +96 -31
- package/template/.aioson/agents/setup.md +12 -1
- package/template/.aioson/agents/sheldon.md +103 -13
- package/template/.aioson/agents/site-forge.md +11 -2
- package/template/.aioson/agents/squad.md +20 -5
- package/template/.aioson/agents/tester.md +4 -0
- package/template/.aioson/agents/ux-ui.md +2 -1
- 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/agent-help.md +126 -0
- package/template/.aioson/docs/autopilot-handoff.md +30 -20
- package/template/.aioson/docs/dev/execution-discipline.md +41 -0
- package/template/.aioson/docs/dev/phase-loop.md +50 -0
- package/template/.aioson/docs/play/llm-data-and-bindings.md +70 -7
- 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 +21 -4
- 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/SKILL.md +17 -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/prototype-forge/SKILL.md +6 -0
- package/template/.aioson/skills/process/reference-identity-extract/SKILL.md +164 -0
- package/template/.aioson/skills/static/multi-agent-patterns.md +5 -5
- package/template/AGENTS.md +36 -36
- package/template/CLAUDE.md +15 -11
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const { execFileSync } = require('node:child_process');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Deterministically detect whether a feature ships a runtime surface, using
|
|
9
|
+
* ONLY signals the framework can locate reliably:
|
|
10
|
+
*
|
|
11
|
+
* - a prototype-manifest (`.aioson/briefings/{slug}/prototype-manifest.md`)
|
|
12
|
+
* ⇒ a clickable prototype whose Core interactions must work on the real
|
|
13
|
+
* stack (the flow-deck failure class);
|
|
14
|
+
* - a migration / Prisma path among `progress.completed_steps` ⇒ a DB feature
|
|
15
|
+
* whose migrations must actually apply.
|
|
16
|
+
*
|
|
17
|
+
* The Play `manifest.json` `has_api` flag lives in the TARGET app and is not
|
|
18
|
+
* reliably locatable from the framework, so that trigger stays with the LLM
|
|
19
|
+
* @validator (harness-contract.md §2c). A `false` result here means "no reliable
|
|
20
|
+
* runtime signal", NOT "proven non-runtime" — callers treat it conservatively.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
// Path segments / extensions that reliably indicate database migration work.
|
|
24
|
+
const MIGRATION_HINTS = [
|
|
25
|
+
/(?:^|[\\/])migrations?(?:[\\/]|$)/i,
|
|
26
|
+
/(?:^|[\\/])prisma(?:[\\/]|$)/i,
|
|
27
|
+
/\.prisma$/i,
|
|
28
|
+
/(?:^|[\\/])migrate(?:[\\/]|$)/i
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
function looksLikeMigration(step) {
|
|
32
|
+
const value = String(step || '');
|
|
33
|
+
return MIGRATION_HINTS.some((re) => re.test(value));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The set of paths git considers changed in the working tree: unstaged,
|
|
38
|
+
* staged, and untracked-but-not-ignored. Read-only and best-effort — a
|
|
39
|
+
* non-git project (or any git failure) yields `[]`, so callers degrade to
|
|
40
|
+
* prototype/progress-based detection. Lives here (not only in the gate) so
|
|
41
|
+
* the standalone `aioson harness:check` detects the same migration surface
|
|
42
|
+
* the workflow finalize / feature:close gate does.
|
|
43
|
+
*/
|
|
44
|
+
function gitChangedFiles(targetDir) {
|
|
45
|
+
const files = new Set();
|
|
46
|
+
const commands = [
|
|
47
|
+
['diff', '--name-only'],
|
|
48
|
+
['diff', '--name-only', '--cached'],
|
|
49
|
+
['ls-files', '--others', '--exclude-standard']
|
|
50
|
+
];
|
|
51
|
+
for (const args of commands) {
|
|
52
|
+
try {
|
|
53
|
+
const output = execFileSync('git', args, {
|
|
54
|
+
cwd: targetDir,
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
|
57
|
+
});
|
|
58
|
+
for (const line of output.split(/\r?\n/)) {
|
|
59
|
+
const trimmed = line.trim();
|
|
60
|
+
if (trimmed) files.add(trimmed);
|
|
61
|
+
}
|
|
62
|
+
} catch {
|
|
63
|
+
// Non-git projects still get prototype/progress based detection.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return [...files];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function detectRuntimeFeature(targetDir, slug, options = {}) {
|
|
70
|
+
const signals = [];
|
|
71
|
+
|
|
72
|
+
if (slug) {
|
|
73
|
+
try {
|
|
74
|
+
const protoManifest = path.join(
|
|
75
|
+
targetDir, '.aioson', 'briefings', String(slug), 'prototype-manifest.md'
|
|
76
|
+
);
|
|
77
|
+
if (fs.existsSync(protoManifest)) signals.push('prototype-manifest');
|
|
78
|
+
} catch {
|
|
79
|
+
/* ignore — detection is best-effort */
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const completedSteps = Array.isArray(options.completedSteps) ? options.completedSteps : [];
|
|
84
|
+
const changedFiles = Array.isArray(options.changedFiles) ? options.changedFiles : [];
|
|
85
|
+
if ([...completedSteps, ...changedFiles].some(looksLikeMigration)) signals.push('migrations');
|
|
86
|
+
|
|
87
|
+
return { isRuntimeFeature: signals.length > 0, signals };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { detectRuntimeFeature, looksLikeMigration, gitChangedFiles };
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Static-pattern criteria (SG-* — Static Gate) — the build-independent half of
|
|
5
|
+
* the harness contract.
|
|
6
|
+
*
|
|
7
|
+
* Where an RG-* criterion EXECUTES a command (a working build, migrations, a
|
|
8
|
+
* booted app — expensive, run once at the last gate per A5), an SG-* criterion
|
|
9
|
+
* proves a claim by READING the changed files: a required pattern is present
|
|
10
|
+
* (the code was actually written / wired) and a forbidden pattern is absent (no
|
|
11
|
+
* placeholder, stub or anti-pattern snuck in). Pure fs + RegExp +
|
|
12
|
+
* `node --check`/`JSON.parse` — no shell, no build — so it costs ~milliseconds
|
|
13
|
+
* and runs at EVERY gate (@dev-done, @qa-done; advisory in per-phase
|
|
14
|
+
* `harness:check`), cross-platform by construction.
|
|
15
|
+
*
|
|
16
|
+
* Two build-free pattern checks over the declared files:
|
|
17
|
+
* must_match OR-across-files — each pattern must appear in >= 1 file
|
|
18
|
+
* must_not_match absent-in-all — no pattern may appear in ANY file
|
|
19
|
+
* plus a post-write parse-check (the `parseable` gate) to catch a truncated or
|
|
20
|
+
* corrupted write before it reaches a human.
|
|
21
|
+
*
|
|
22
|
+
* An invalid regex degrades to a literal substring test instead of throwing — a
|
|
23
|
+
* contract typo never crashes the gate, it just matches literally.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const path = require('node:path');
|
|
28
|
+
const { spawnSync } = require('node:child_process');
|
|
29
|
+
|
|
30
|
+
/** A criterion is static when it carries a non-empty must_match or must_not_match. */
|
|
31
|
+
function isStaticCriterion(criterion) {
|
|
32
|
+
if (!criterion || typeof criterion !== 'object') return false;
|
|
33
|
+
return (
|
|
34
|
+
(Array.isArray(criterion.must_match) && criterion.must_match.length > 0) ||
|
|
35
|
+
(Array.isArray(criterion.must_not_match) && criterion.must_not_match.length > 0)
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compile a pattern to a tester: a real RegExp when the source is valid, else a
|
|
41
|
+
* literal-substring test (a contract typo matches literally, never throws).
|
|
42
|
+
*/
|
|
43
|
+
function patternTester(pattern) {
|
|
44
|
+
const src = String(pattern);
|
|
45
|
+
try {
|
|
46
|
+
const re = new RegExp(src);
|
|
47
|
+
return (text) => re.test(text);
|
|
48
|
+
} catch {
|
|
49
|
+
return (text) => text.includes(src);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Parse-check a file by extension. Returns { checked, ok, reason }.
|
|
55
|
+
* `checked: false` means no stdlib parser applies to this extension (skipped,
|
|
56
|
+
* like the harness skips .ts/.md) — never a failure.
|
|
57
|
+
*/
|
|
58
|
+
function parseCheckFile(absPath) {
|
|
59
|
+
const ext = path.extname(absPath).toLowerCase();
|
|
60
|
+
if (ext === '.json') {
|
|
61
|
+
let content;
|
|
62
|
+
try {
|
|
63
|
+
content = fs.readFileSync(absPath, 'utf8');
|
|
64
|
+
} catch (err) {
|
|
65
|
+
return { checked: true, ok: false, reason: `cannot read file (${err.code || err.message})` };
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
JSON.parse(content);
|
|
69
|
+
return { checked: true, ok: true, reason: null };
|
|
70
|
+
} catch (err) {
|
|
71
|
+
return { checked: true, ok: false, reason: `invalid JSON (${err.message})` };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
|
75
|
+
// `node --check` is the syntax parser shipped with the runtime AIOSON runs
|
|
76
|
+
// on — guaranteed present, cross-platform, no extra dependency.
|
|
77
|
+
const res = spawnSync(process.execPath, ['--check', absPath], { encoding: 'utf8', timeout: 10000 });
|
|
78
|
+
if (res.status === 0) return { checked: true, ok: true, reason: null };
|
|
79
|
+
const detail = String(res.stderr || res.stdout || '')
|
|
80
|
+
.split('\n')
|
|
81
|
+
.map((l) => l.trim())
|
|
82
|
+
.find((l) => l) || `exit ${res.status}`;
|
|
83
|
+
return { checked: true, ok: false, reason: `syntax error (${detail})` };
|
|
84
|
+
}
|
|
85
|
+
// No stdlib parser for this extension (.ts/.tsx/.md/...): skip, like the harness.
|
|
86
|
+
return { checked: false, ok: true, reason: null };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Evaluate one static criterion against the working tree.
|
|
91
|
+
*
|
|
92
|
+
* @param {object} criterion — a criteria[] entry with must_match/must_not_match/files
|
|
93
|
+
* @param {string} cwd — project root the relative file paths resolve against
|
|
94
|
+
* @returns {{ id, kind:'static', ok, files, missing_files, must_match_misses,
|
|
95
|
+
* must_not_match_hits, parse_failures, detail }}
|
|
96
|
+
*/
|
|
97
|
+
function evaluateStaticCriterion(criterion, cwd) {
|
|
98
|
+
const id = criterion.id;
|
|
99
|
+
const files = Array.isArray(criterion.files) ? criterion.files.slice() : [];
|
|
100
|
+
const mustMatch = Array.isArray(criterion.must_match) ? criterion.must_match : [];
|
|
101
|
+
const mustNotMatch = Array.isArray(criterion.must_not_match) ? criterion.must_not_match : [];
|
|
102
|
+
|
|
103
|
+
const resolved = files.map((rel) => ({ rel, abs: path.resolve(cwd, rel) }));
|
|
104
|
+
const missingFiles = resolved.filter((f) => !fs.existsSync(f.abs)).map((f) => f.rel);
|
|
105
|
+
|
|
106
|
+
// Read each existing file once, and parse-check it for truncation/corruption.
|
|
107
|
+
const contents = new Map();
|
|
108
|
+
const parseFailures = [];
|
|
109
|
+
for (const f of resolved) {
|
|
110
|
+
if (!fs.existsSync(f.abs)) continue;
|
|
111
|
+
let text = '';
|
|
112
|
+
try {
|
|
113
|
+
text = fs.readFileSync(f.abs, 'utf8');
|
|
114
|
+
} catch {
|
|
115
|
+
// Unreadable file is reported by the parse-check below.
|
|
116
|
+
}
|
|
117
|
+
contents.set(f.rel, text);
|
|
118
|
+
const pc = parseCheckFile(f.abs);
|
|
119
|
+
if (pc.checked && !pc.ok) parseFailures.push(`${f.rel}: ${pc.reason}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// must_match — OR across files: each pattern must appear in >= 1 file.
|
|
123
|
+
const mustMatchMisses = [];
|
|
124
|
+
for (const pat of mustMatch) {
|
|
125
|
+
const test = patternTester(pat);
|
|
126
|
+
const hit = [...contents.values()].some((text) => test(text));
|
|
127
|
+
if (!hit) mustMatchMisses.push(String(pat));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// must_not_match — absent in all: no pattern may appear in ANY file.
|
|
131
|
+
const mustNotMatchHits = [];
|
|
132
|
+
for (const pat of mustNotMatch) {
|
|
133
|
+
const test = patternTester(pat);
|
|
134
|
+
for (const [rel, text] of contents) {
|
|
135
|
+
if (test(text)) mustNotMatchHits.push({ pattern: String(pat), file: rel });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const reasons = [];
|
|
140
|
+
if (missingFiles.length) reasons.push(`missing files: ${missingFiles.join(', ')}`);
|
|
141
|
+
if (parseFailures.length) reasons.push(`parse: ${parseFailures.join('; ')}`);
|
|
142
|
+
if (mustMatchMisses.length) reasons.push(`must_match not found: ${mustMatchMisses.map((p) => `/${p}/`).join(', ')}`);
|
|
143
|
+
if (mustNotMatchHits.length) {
|
|
144
|
+
reasons.push(`must_not_match present: ${mustNotMatchHits.map((h) => `/${h.pattern}/ in ${h.file}`).join(', ')}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const ok =
|
|
148
|
+
missingFiles.length === 0 &&
|
|
149
|
+
parseFailures.length === 0 &&
|
|
150
|
+
mustMatchMisses.length === 0 &&
|
|
151
|
+
mustNotMatchHits.length === 0;
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
id,
|
|
155
|
+
kind: 'static',
|
|
156
|
+
ok,
|
|
157
|
+
files,
|
|
158
|
+
missing_files: missingFiles,
|
|
159
|
+
must_match_misses: mustMatchMisses,
|
|
160
|
+
must_not_match_hits: mustNotMatchHits,
|
|
161
|
+
parse_failures: parseFailures,
|
|
162
|
+
detail: ok ? null : reasons.join(' | ')
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Evaluate every static criterion in a contract's criteria[]. Runtime criteria
|
|
168
|
+
* (those with a `verification` command) are ignored here.
|
|
169
|
+
*
|
|
170
|
+
* @param {object} params
|
|
171
|
+
* @param {Array} params.criteria — criteria[] from the contract
|
|
172
|
+
* @param {string} params.cwd — project root
|
|
173
|
+
* @returns {{ checks: Array, ok: boolean, total: number, failed: number }}
|
|
174
|
+
*/
|
|
175
|
+
function evaluateStaticCriteria({ criteria = [], cwd } = {}) {
|
|
176
|
+
const root = cwd || process.cwd();
|
|
177
|
+
const checks = [];
|
|
178
|
+
for (const criterion of Array.isArray(criteria) ? criteria : []) {
|
|
179
|
+
if (!isStaticCriterion(criterion)) continue;
|
|
180
|
+
checks.push(evaluateStaticCriterion(criterion, root));
|
|
181
|
+
}
|
|
182
|
+
const failed = checks.filter((c) => !c.ok).length;
|
|
183
|
+
return { checks, ok: failed === 0, total: checks.length, failed };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = {
|
|
187
|
+
isStaticCriterion,
|
|
188
|
+
patternTester,
|
|
189
|
+
parseCheckFile,
|
|
190
|
+
evaluateStaticCriterion,
|
|
191
|
+
evaluateStaticCriteria
|
|
192
|
+
};
|
package/src/i18n/messages/en.js
CHANGED
|
@@ -13,10 +13,10 @@ module.exports = {
|
|
|
13
13
|
help_setup:
|
|
14
14
|
'aioson setup [path] [--defaults] [--framework=<name>] [--lang=<bcp47-tag>] [--project-name=<name>] [--force] [--dry-run] [--tool=codex|claude|opencode] [--locale=en]',
|
|
15
15
|
help_update: 'aioson update [path] [--dry-run] [--lang=<bcp47-tag>] [--locale=en]',
|
|
16
|
-
help_info: 'aioson info [path] [--json] [--locale=en]',
|
|
17
|
-
help_doctor: 'aioson doctor [path] [--fix] [--dry-run] [--json] [--locale=en]',
|
|
18
|
-
help_hygiene_scan: 'aioson hygiene:scan [path] [--json] [--locale=en]',
|
|
19
|
-
help_i18n_add: 'aioson i18n:add <locale> [--force] [--dry-run] [--locale=en]',
|
|
16
|
+
help_info: 'aioson info [path] [--json] [--locale=en]',
|
|
17
|
+
help_doctor: 'aioson doctor [path] [--fix] [--dry-run] [--json] [--locale=en]',
|
|
18
|
+
help_hygiene_scan: 'aioson hygiene:scan [path] [--json] [--locale=en]',
|
|
19
|
+
help_i18n_add: 'aioson i18n:add <locale> [--force] [--dry-run] [--locale=en]',
|
|
20
20
|
help_agents: 'aioson agents [path] [--lang=<bcp47-tag>] [--locale=en]',
|
|
21
21
|
help_agent_prompt:
|
|
22
22
|
'aioson agent:prompt <agent> [path] [--tool=codex|claude|opencode] [--lang=<bcp47-tag>] [--locale=en]',
|
|
@@ -133,6 +133,8 @@ module.exports = {
|
|
|
133
133
|
'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=en]',
|
|
134
134
|
help_review_cycle:
|
|
135
135
|
'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=en]',
|
|
136
|
+
help_review_feature:
|
|
137
|
+
'aioson review:feature [path] [--feature=<slug>] [--scope=<target>] [--skip-audit] [--out-dir=<dir>] [--tool=<tool>] [--json] [--locale=en]',
|
|
136
138
|
help_parallel_init:
|
|
137
139
|
'aioson parallel:init [path] [--workers=2..6] [--force] [--dry-run] [--json] [--locale=en]',
|
|
138
140
|
help_parallel_doctor:
|
|
@@ -169,6 +171,8 @@ module.exports = {
|
|
|
169
171
|
'aioson harness:retro [path] --feature=<slug> | --last=<N> [--json] [--locale=en]',
|
|
170
172
|
help_harness_preview:
|
|
171
173
|
'aioson harness:preview <file> [--max-bytes=8192] [--json] [--locale=en]',
|
|
174
|
+
help_verification_plan:
|
|
175
|
+
'aioson verification:plan [path] [--feature=<slug>] [--trigger=per-phase|end-of-feature|sensitive-surface] [--host=claude|codex|opencode] [--classification=MICRO|SMALL|MEDIUM] [--sensitive] [--json] [--locale=en]',
|
|
172
176
|
harnessRetro: {
|
|
173
177
|
need_target: 'harness:retro requires --feature=<slug> or --last=<N>.',
|
|
174
178
|
invalid_slug: 'Invalid slug: {slug} (must match ^[a-z0-9][a-z0-9-]*$).',
|
|
@@ -408,6 +412,7 @@ module.exports = {
|
|
|
408
412
|
update: {
|
|
409
413
|
not_installed: 'No AIOSON installation found in {targetDir}.',
|
|
410
414
|
done_at: 'Update completed at: {targetDir}',
|
|
415
|
+
template_version: 'Template version applied: {version}',
|
|
411
416
|
files_updated: 'Files updated: {count}',
|
|
412
417
|
backups_created: 'Backups created: {count}',
|
|
413
418
|
profile_renamed: 'i Profile `beginner` renamed to `creator` in project.context.md to better describe the user. Behavior unchanged. Edit the file to switch to `developer` if desired.',
|
|
@@ -487,7 +492,7 @@ module.exports = {
|
|
|
487
492
|
features_dir_present: 'Features directory present (.aioson/context/features/)',
|
|
488
493
|
features_dir_present_hint: 'Create .aioson/context/features/ to host per-feature dossiers (doctor --fix will create it).',
|
|
489
494
|
auto_handoff_declared: 'Autopilot handoff flag declared (auto_handoff in project.context.md)',
|
|
490
|
-
auto_handoff_declared_hint: 'The autopilot-handoff protocol is installed
|
|
495
|
+
auto_handoff_declared_hint: 'The autopilot-handoff protocol is installed and auto_handoff is not set — @product asks the run mode on screen at each feature kickoff (Autopilot / Step by step / Always). Set auto_handoff: true to always autopilot and skip the question, or false to always go step-by-step.',
|
|
491
496
|
claude_commands_present: 'Claude slash commands present ({missing} missing of {required})',
|
|
492
497
|
claude_commands_present_hint: 'Missing: {paths}. Run `aioson doctor . --fix` to restore them from the template.',
|
|
493
498
|
version_drift: 'CLI version matches project.context.md (context: {context}, CLI: {cli})',
|
package/src/i18n/messages/es.js
CHANGED
|
@@ -14,10 +14,10 @@ module.exports = {
|
|
|
14
14
|
'aioson setup [path] [--defaults] [--framework=<nombre>] [--lang=<bcp47-tag>] [--project-name=<nombre>] [--force] [--dry-run] [--tool=codex|claude|opencode] [--locale=es]',
|
|
15
15
|
help_update:
|
|
16
16
|
'aioson update [path] [--dry-run] [--lang=<bcp47-tag>] [--locale=es]',
|
|
17
|
-
help_info: 'aioson info [path] [--json] [--locale=es]',
|
|
18
|
-
help_doctor: 'aioson doctor [path] [--fix] [--dry-run] [--json] [--locale=es]',
|
|
19
|
-
help_hygiene_scan: 'aioson hygiene:scan [path] [--json] [--locale=es]',
|
|
20
|
-
help_i18n_add: 'aioson i18n:add <locale> [--force] [--dry-run] [--locale=es]',
|
|
17
|
+
help_info: 'aioson info [path] [--json] [--locale=es]',
|
|
18
|
+
help_doctor: 'aioson doctor [path] [--fix] [--dry-run] [--json] [--locale=es]',
|
|
19
|
+
help_hygiene_scan: 'aioson hygiene:scan [path] [--json] [--locale=es]',
|
|
20
|
+
help_i18n_add: 'aioson i18n:add <locale> [--force] [--dry-run] [--locale=es]',
|
|
21
21
|
help_agents: 'aioson agents [path] [--lang=<bcp47-tag>] [--locale=es]',
|
|
22
22
|
help_agent_prompt:
|
|
23
23
|
'aioson agent:prompt <agent> [path] [--tool=codex|claude|opencode] [--lang=<bcp47-tag>] [--locale=es]',
|
|
@@ -123,6 +123,8 @@ module.exports = {
|
|
|
123
123
|
'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=es]',
|
|
124
124
|
help_review_cycle:
|
|
125
125
|
'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=es]',
|
|
126
|
+
help_review_feature:
|
|
127
|
+
'aioson review:feature [path] [--feature=<slug>] [--scope=<target>] [--skip-audit] [--out-dir=<dir>] [--tool=<tool>] [--json] [--locale=es]',
|
|
126
128
|
help_parallel_init:
|
|
127
129
|
'aioson parallel:init [path] [--workers=2..6] [--force] [--dry-run] [--json] [--locale=es]',
|
|
128
130
|
help_parallel_doctor:
|
|
@@ -155,6 +157,8 @@ module.exports = {
|
|
|
155
157
|
'aioson harness:retro [path] --feature=<slug> | --last=<N> [--json] [--locale=es]',
|
|
156
158
|
help_harness_preview:
|
|
157
159
|
'aioson harness:preview <file> [--max-bytes=8192] [--json] [--locale=es]',
|
|
160
|
+
help_verification_plan:
|
|
161
|
+
'aioson verification:plan [path] [--feature=<slug>] [--trigger=per-phase|end-of-feature|sensitive-surface] [--host=claude|codex|opencode] [--classification=MICRO|SMALL|MEDIUM] [--sensitive] [--json] [--locale=es]',
|
|
158
162
|
harnessRetro: {
|
|
159
163
|
need_target: 'harness:retro requiere --feature=<slug> o --last=<N>.',
|
|
160
164
|
invalid_slug: 'Slug inválido: {slug} (debe cumplir ^[a-z0-9][a-z0-9-]*$).',
|
|
@@ -276,6 +280,7 @@ module.exports = {
|
|
|
276
280
|
update: {
|
|
277
281
|
not_installed: 'No se encontro instalacion de AIOSON en {targetDir}.',
|
|
278
282
|
done_at: 'Actualizacion completada en: {targetDir}',
|
|
283
|
+
template_version: 'Version de template aplicada: {version}',
|
|
279
284
|
files_updated: 'Archivos actualizados: {count}',
|
|
280
285
|
backups_created: 'Backups creados: {count}',
|
|
281
286
|
profile_renamed: 'i Perfil `beginner` renombrado a `creator` en project.context.md para describir mejor al usuario. Comportamiento sin cambios. Edita el archivo para cambiar a `developer` si lo prefieres.',
|
|
@@ -361,7 +366,7 @@ module.exports = {
|
|
|
361
366
|
features_dir_present: 'Directorio de features presente (.aioson/context/features/)',
|
|
362
367
|
features_dir_present_hint: 'Cree .aioson/context/features/ para hospedar dossiers por feature (doctor --fix lo crea).',
|
|
363
368
|
auto_handoff_declared: 'Flag de autopilot handoff declarada (auto_handoff en project.context.md)',
|
|
364
|
-
auto_handoff_declared_hint: 'El protocolo autopilot-handoff esta instalado
|
|
369
|
+
auto_handoff_declared_hint: 'El protocolo autopilot-handoff esta instalado y auto_handoff no esta definido — @product pregunta el modo en pantalla al inicio de cada feature (Autopilot / Paso a paso / Siempre). Defina auto_handoff: true para siempre autopilot y omitir la pregunta, o false para siempre paso a paso.',
|
|
365
370
|
claude_commands_present: 'Slash commands de Claude presentes ({missing} ausentes de {required})',
|
|
366
371
|
claude_commands_present_hint: 'Ausentes: {paths}. Ejecute `aioson doctor . --fix` para restaurarlos.',
|
|
367
372
|
version_drift: 'Version del CLI coincide con project.context.md (contexto: {context}, CLI: {cli})',
|
package/src/i18n/messages/fr.js
CHANGED
|
@@ -14,10 +14,10 @@ module.exports = {
|
|
|
14
14
|
'aioson setup [path] [--defaults] [--framework=<nom>] [--lang=<bcp47-tag>] [--project-name=<nom>] [--force] [--dry-run] [--tool=codex|claude|opencode] [--locale=fr]',
|
|
15
15
|
help_update:
|
|
16
16
|
'aioson update [path] [--dry-run] [--lang=<bcp47-tag>] [--locale=fr]',
|
|
17
|
-
help_info: 'aioson info [path] [--json] [--locale=fr]',
|
|
18
|
-
help_doctor: 'aioson doctor [path] [--fix] [--dry-run] [--json] [--locale=fr]',
|
|
19
|
-
help_hygiene_scan: 'aioson hygiene:scan [path] [--json] [--locale=fr]',
|
|
20
|
-
help_i18n_add: 'aioson i18n:add <locale> [--force] [--dry-run] [--locale=fr]',
|
|
17
|
+
help_info: 'aioson info [path] [--json] [--locale=fr]',
|
|
18
|
+
help_doctor: 'aioson doctor [path] [--fix] [--dry-run] [--json] [--locale=fr]',
|
|
19
|
+
help_hygiene_scan: 'aioson hygiene:scan [path] [--json] [--locale=fr]',
|
|
20
|
+
help_i18n_add: 'aioson i18n:add <locale> [--force] [--dry-run] [--locale=fr]',
|
|
21
21
|
help_agents: 'aioson agents [path] [--lang=<bcp47-tag>] [--locale=fr]',
|
|
22
22
|
help_agent_prompt:
|
|
23
23
|
'aioson agent:prompt <agent> [path] [--tool=codex|claude|opencode] [--lang=<bcp47-tag>] [--locale=fr]',
|
|
@@ -123,6 +123,8 @@ module.exports = {
|
|
|
123
123
|
'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=fr]',
|
|
124
124
|
help_review_cycle:
|
|
125
125
|
'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=fr]',
|
|
126
|
+
help_review_feature:
|
|
127
|
+
'aioson review:feature [path] [--feature=<slug>] [--scope=<target>] [--skip-audit] [--out-dir=<dir>] [--tool=<tool>] [--json] [--locale=fr]',
|
|
126
128
|
help_parallel_init:
|
|
127
129
|
'aioson parallel:init [path] [--workers=2..6] [--force] [--dry-run] [--json] [--locale=fr]',
|
|
128
130
|
help_parallel_doctor:
|
|
@@ -155,6 +157,8 @@ module.exports = {
|
|
|
155
157
|
'aioson harness:retro [path] --feature=<slug> | --last=<N> [--json] [--locale=fr]',
|
|
156
158
|
help_harness_preview:
|
|
157
159
|
'aioson harness:preview <file> [--max-bytes=8192] [--json] [--locale=fr]',
|
|
160
|
+
help_verification_plan:
|
|
161
|
+
'aioson verification:plan [path] [--feature=<slug>] [--trigger=per-phase|end-of-feature|sensitive-surface] [--host=claude|codex|opencode] [--classification=MICRO|SMALL|MEDIUM] [--sensitive] [--json] [--locale=fr]',
|
|
158
162
|
harnessRetro: {
|
|
159
163
|
need_target: 'harness:retro requiert --feature=<slug> ou --last=<N>.',
|
|
160
164
|
invalid_slug: 'Slug invalide : {slug} (doit correspondre à ^[a-z0-9][a-z0-9-]*$).',
|
|
@@ -275,6 +279,7 @@ module.exports = {
|
|
|
275
279
|
update: {
|
|
276
280
|
not_installed: 'Aucune installation AIOSON trouvee dans {targetDir}.',
|
|
277
281
|
done_at: 'Mise a jour terminee dans : {targetDir}',
|
|
282
|
+
template_version: 'Version du template appliquee : {version}',
|
|
278
283
|
files_updated: 'Fichiers mis a jour : {count}',
|
|
279
284
|
backups_created: 'Sauvegardes creees : {count}',
|
|
280
285
|
profile_renamed: 'i Profil `beginner` renomme en `creator` dans project.context.md pour mieux decrire l utilisateur. Comportement inchange. Modifiez le fichier pour passer a `developer` si vous le souhaitez.',
|
|
@@ -360,7 +365,7 @@ module.exports = {
|
|
|
360
365
|
features_dir_present: 'Repertoire features present (.aioson/context/features/)',
|
|
361
366
|
features_dir_present_hint: 'Creez .aioson/context/features/ pour heberger les dossiers par feature (doctor --fix le cree).',
|
|
362
367
|
auto_handoff_declared: 'Flag autopilot handoff declare (auto_handoff dans project.context.md)',
|
|
363
|
-
auto_handoff_declared_hint: 'Le protocole autopilot-handoff est installe
|
|
368
|
+
auto_handoff_declared_hint: 'Le protocole autopilot-handoff est installe et auto_handoff n\'est pas defini — @product demande le mode a l\'ecran au debut de chaque feature (Autopilot / Pas a pas / Toujours). Definissez auto_handoff: true pour toujours autopilot et sauter la question, ou false pour toujours pas a pas.',
|
|
364
369
|
claude_commands_present: 'Slash commands de Claude presents ({missing} absents sur {required})',
|
|
365
370
|
claude_commands_present_hint: 'Absents : {paths}. Executez `aioson doctor . --fix` pour les restaurer.',
|
|
366
371
|
version_drift: 'Version du CLI conforme a project.context.md (contexte : {context}, CLI : {cli})',
|
|
@@ -14,10 +14,10 @@ module.exports = {
|
|
|
14
14
|
'aioson setup [path] [--defaults] [--framework=<nome>] [--lang=<bcp47-tag>] [--project-name=<nome>] [--force] [--dry-run] [--tool=codex|claude|opencode] [--locale=pt-BR]',
|
|
15
15
|
help_update:
|
|
16
16
|
'aioson update [path] [--dry-run] [--lang=<bcp47-tag>] [--locale=pt-BR]',
|
|
17
|
-
help_info: 'aioson info [path] [--json] [--locale=pt-BR]',
|
|
18
|
-
help_doctor: 'aioson doctor [path] [--fix] [--dry-run] [--json] [--locale=pt-BR]',
|
|
19
|
-
help_hygiene_scan: 'aioson hygiene:scan [path] [--json] [--locale=pt-BR]',
|
|
20
|
-
help_i18n_add: 'aioson i18n:add <locale> [--force] [--dry-run] [--locale=pt-BR]',
|
|
17
|
+
help_info: 'aioson info [path] [--json] [--locale=pt-BR]',
|
|
18
|
+
help_doctor: 'aioson doctor [path] [--fix] [--dry-run] [--json] [--locale=pt-BR]',
|
|
19
|
+
help_hygiene_scan: 'aioson hygiene:scan [path] [--json] [--locale=pt-BR]',
|
|
20
|
+
help_i18n_add: 'aioson i18n:add <locale> [--force] [--dry-run] [--locale=pt-BR]',
|
|
21
21
|
help_agents: 'aioson agents [path] [--lang=<bcp47-tag>] [--locale=pt-BR]',
|
|
22
22
|
help_agent_prompt:
|
|
23
23
|
'aioson agent:prompt <agent> [path] [--tool=codex|claude|opencode] [--lang=<bcp47-tag>] [--locale=pt-BR]',
|
|
@@ -135,6 +135,8 @@ module.exports = {
|
|
|
135
135
|
'aioson workflow:execute [path] [--feature=<slug>] [--agentic] [--max-dev-qa-cycles=<n>] [--max-tester-cycles=<n>] [--max-pentester-cycles=<n>] [--dry-run] [--lane=<n>] [--json] [--locale=pt-BR]',
|
|
136
136
|
help_review_cycle:
|
|
137
137
|
'aioson review-cycle:<status|advance|resolve|reset> [path] --feature=<slug> [--plan=<path>] [--source=qa|tester|pentester] [--json] [--locale=pt-BR]',
|
|
138
|
+
help_review_feature:
|
|
139
|
+
'aioson review:feature [path] [--feature=<slug>] [--scope=<target>] [--skip-audit] [--out-dir=<dir>] [--tool=<tool>] [--json] [--locale=pt-BR]',
|
|
138
140
|
help_parallel_init:
|
|
139
141
|
'aioson parallel:init [path] [--workers=2..6] [--force] [--dry-run] [--json] [--locale=pt-BR]',
|
|
140
142
|
help_parallel_doctor:
|
|
@@ -171,6 +173,8 @@ module.exports = {
|
|
|
171
173
|
'aioson harness:retro [path] --feature=<slug> | --last=<N> [--json] [--locale=pt-BR]',
|
|
172
174
|
help_harness_preview:
|
|
173
175
|
'aioson harness:preview <file> [--max-bytes=8192] [--json] [--locale=pt-BR]',
|
|
176
|
+
help_verification_plan:
|
|
177
|
+
'aioson verification:plan [path] [--feature=<slug>] [--trigger=per-phase|end-of-feature|sensitive-surface] [--host=claude|codex|opencode] [--classification=MICRO|SMALL|MEDIUM] [--sensitive] [--json] [--locale=pt-BR]',
|
|
174
178
|
harnessRetro: {
|
|
175
179
|
need_target: 'harness:retro requer --feature=<slug> ou --last=<N>.',
|
|
176
180
|
invalid_slug: 'Slug inválido: {slug} (deve casar ^[a-z0-9][a-z0-9-]*$).',
|
|
@@ -375,6 +379,7 @@ module.exports = {
|
|
|
375
379
|
update: {
|
|
376
380
|
not_installed: 'Nenhuma instalacao do AIOSON encontrada em {targetDir}.',
|
|
377
381
|
done_at: 'Atualizacao concluida em: {targetDir}',
|
|
382
|
+
template_version: 'Versao do template aplicada: {version}',
|
|
378
383
|
files_updated: 'Arquivos atualizados: {count}',
|
|
379
384
|
backups_created: 'Backups criados: {count}',
|
|
380
385
|
profile_renamed: 'i Perfil `beginner` renomeado para `creator` em project.context.md para descrever melhor o usuario. Comportamento inalterado. Edite o arquivo para mudar para `developer` se preferir.',
|
|
@@ -461,7 +466,7 @@ module.exports = {
|
|
|
461
466
|
features_dir_present: 'Diretorio de features presente (.aioson/context/features/)',
|
|
462
467
|
features_dir_present_hint: 'Crie .aioson/context/features/ para hospedar dossies por feature (doctor --fix cria automaticamente).',
|
|
463
468
|
auto_handoff_declared: 'Flag de autopilot handoff declarada (auto_handoff no project.context.md)',
|
|
464
|
-
auto_handoff_declared_hint: 'O protocolo autopilot-handoff esta instalado
|
|
469
|
+
auto_handoff_declared_hint: 'O protocolo autopilot-handoff esta instalado e auto_handoff nao esta definido — o @product pergunta o modo na tela no inicio de cada feature (Autopilot / Passo a passo / Sempre). Defina auto_handoff: true para sempre autopilot e pular a pergunta, ou false para sempre passo a passo.',
|
|
465
470
|
claude_commands_present: 'Slash commands do Claude presentes ({missing} ausentes de {required})',
|
|
466
471
|
claude_commands_present_hint: 'Ausentes: {paths}. Rode `aioson doctor . --fix` para restaurar a partir do template.',
|
|
467
472
|
version_drift: 'Versao do CLI bate com project.context.md (contexto: {context}, CLI: {cli})',
|
package/src/install-wizard.js
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
const readline = require('node:readline');
|
|
4
4
|
const { getCliVersionSync } = require('./version');
|
|
5
5
|
|
|
6
|
-
const TOOLS = [
|
|
7
|
-
{ id: 'claude', label: 'Claude Code', desc: 'Slash commands, CLAUDE.md, .claude/' },
|
|
8
|
-
{ id: 'codex', label: 'Codex (OpenAI)', desc: 'AGENTS.md protocol' },
|
|
9
|
-
{ id: 'opencode', label: 'OpenCode', desc: 'OPENCODE.md protocol' }
|
|
10
|
-
];
|
|
6
|
+
const TOOLS = [
|
|
7
|
+
{ id: 'claude', label: 'Claude Code', desc: 'Slash commands, CLAUDE.md, .claude/' },
|
|
8
|
+
{ id: 'codex', label: 'Codex (OpenAI)', desc: 'AGENTS.md protocol' },
|
|
9
|
+
{ id: 'opencode', label: 'OpenCode', desc: 'OPENCODE.md protocol' }
|
|
10
|
+
];
|
|
11
11
|
|
|
12
12
|
const USES = [
|
|
13
13
|
{
|
|
14
14
|
id: 'development',
|
|
15
15
|
label: 'Development',
|
|
16
|
-
desc: 'Agent workflow: setup → analyst → scope-check → architect → dev → qa',
|
|
16
|
+
desc: 'Agent workflow: setup → analyst → scope-check → architect → dev → qa',
|
|
17
17
|
locked: true
|
|
18
18
|
},
|
|
19
19
|
{
|
|
@@ -111,7 +111,7 @@ function renderScreen1(cursor, selected, warn, stdout) {
|
|
|
111
111
|
const tool = TOOLS[i];
|
|
112
112
|
const pointer = i === cursor ? '►' : ' ';
|
|
113
113
|
const check = selected.has(tool.id) ? '✓' : ' ';
|
|
114
|
-
stdout.write(` ${pointer} [${check}] ${tool.label.padEnd(20)} ${tool.desc}\n`);
|
|
114
|
+
stdout.write(` ${pointer} [${check}] ${tool.label.padEnd(20)} ${tool.desc}\n`);
|
|
115
115
|
}
|
|
116
116
|
if (warn) stdout.write('\n ⚠ Select at least one tool to continue.\n');
|
|
117
117
|
stdout.write('\n');
|
|
@@ -161,7 +161,7 @@ function renderScreen4(cursor, stdout) {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
function renderConfirm(tools, uses, design, locale, existingProfile, t, stdout) {
|
|
164
|
-
const TOOL_NAMES = { claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode' };
|
|
164
|
+
const TOOL_NAMES = { claude: 'Claude Code', codex: 'Codex', opencode: 'OpenCode' };
|
|
165
165
|
const toolNames = tools.map(id => TOOL_NAMES[id] || id).join(', ');
|
|
166
166
|
const modeLabel = uses.includes('squads') ? 'Development + Squads' : 'Development';
|
|
167
167
|
// design can be string (single/id/all) or string[] (multiple)
|
package/src/installer.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('node:fs/promises');
|
|
4
4
|
const path = require('node:path');
|
|
5
5
|
const { MANAGED_FILES } = require('./constants');
|
|
6
|
-
const { getCliVersion } = require('./version');
|
|
6
|
+
const { getCliVersion, getGitBuildInfoSync } = require('./version');
|
|
7
7
|
const { exists, ensureDir, copyFileWithDir, nowStamp, toRelativeSafe } = require('./utils');
|
|
8
8
|
const { ensureProjectRuntime } = require('./execution-gateway');
|
|
9
9
|
const { shouldIncludeForProfile } = require('./install-profile');
|
|
@@ -34,10 +34,10 @@ const GITIGNORE_POLICY_LINES = [
|
|
|
34
34
|
'# AIOSON — keep shared project memory and tool contracts',
|
|
35
35
|
'!AGENTS.md',
|
|
36
36
|
'!CLAUDE.md',
|
|
37
|
-
'!OPENCODE.md',
|
|
38
|
-
'!.claude/',
|
|
39
|
-
'!.claude/**',
|
|
40
|
-
'!.aioson/',
|
|
37
|
+
'!OPENCODE.md',
|
|
38
|
+
'!.claude/',
|
|
39
|
+
'!.claude/**',
|
|
40
|
+
'!.aioson/',
|
|
41
41
|
'!.aioson/**',
|
|
42
42
|
'# AIOSON — managed framework files (do not commit)',
|
|
43
43
|
'.aioson/config.md',
|
|
@@ -238,10 +238,17 @@ async function writeInstallMetadata(targetDir, action, frameworkDetection, insta
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
const version = await getCliVersion();
|
|
241
|
+
// Stamp the exact framework commit when installing/updating from a git checkout
|
|
242
|
+
// (e.g. an `npm link`ed dev framework). With a plain npm install this is null —
|
|
243
|
+
// template_version alone identifies it. Lets a project record precisely which
|
|
244
|
+
// commit it was last updated from.
|
|
245
|
+
const gitInfo = getGitBuildInfoSync();
|
|
241
246
|
const data = {
|
|
242
247
|
...existing,
|
|
243
248
|
managed_by: 'aioson',
|
|
244
249
|
template_version: version,
|
|
250
|
+
template_git_sha: gitInfo ? gitInfo.sha : null,
|
|
251
|
+
template_git_date: gitInfo ? gitInfo.date : null,
|
|
245
252
|
last_action: action,
|
|
246
253
|
last_action_at: new Date().toISOString(),
|
|
247
254
|
framework_detected: frameworkDetection || existing.framework_detected || null,
|
|
@@ -343,7 +350,7 @@ async function installTemplate(targetDir, options = {}) {
|
|
|
343
350
|
const dest = path.join(targetDir, rel);
|
|
344
351
|
const destExists = await exists(dest);
|
|
345
352
|
|
|
346
|
-
// Gateway pointer files (CLAUDE.md, AGENTS.md, OPENCODE.md)
|
|
353
|
+
// Gateway pointer files (CLAUDE.md, AGENTS.md, OPENCODE.md)
|
|
347
354
|
// get block-merged so that an existing project-authored file keeps its content
|
|
348
355
|
// and only the AIOSON-managed block is created or refreshed in place.
|
|
349
356
|
if (isGatewayPointerPath(rel)) {
|
|
@@ -39,7 +39,10 @@ function summarizeSkippedChanges(feedback) {
|
|
|
39
39
|
return [...sectionChanges, ...decisionChanges];
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
// Declining never writes to briefings.md, so a stale source hash is irrelevant
|
|
43
|
+
// to safety — validating it would only dead-end the user whose briefing moved
|
|
44
|
+
// on. Hence allowStale defaults to TRUE here (and only here).
|
|
45
|
+
async function applyDeclinedFeedback(projectDir, slug, feedback, { allowStale = true } = {}) {
|
|
43
46
|
const briefingPath = resolveBriefingPath(projectDir, slug, 'briefings.md');
|
|
44
47
|
const currentMarkdown = await fs.readFile(briefingPath, 'utf8');
|
|
45
48
|
const currentSourceHash = hashText(currentMarkdown);
|
|
@@ -57,10 +60,12 @@ async function applyDeclinedFeedback(projectDir, slug, feedback, { allowStale =
|
|
|
57
60
|
source_hash: feedback.source_hash,
|
|
58
61
|
applied_hash: currentSourceHash,
|
|
59
62
|
status: 'declined',
|
|
63
|
+
round: feedback.round || 1,
|
|
60
64
|
applied_changes: [],
|
|
61
65
|
skipped_changes: skippedChanges,
|
|
62
66
|
unresolved_comments: unresolvedComments,
|
|
63
67
|
blocking_items: feedback.blocking_items || [],
|
|
68
|
+
findings: feedback.findings || [],
|
|
64
69
|
next_action: 'rerun_review'
|
|
65
70
|
});
|
|
66
71
|
|
|
@@ -104,15 +109,22 @@ async function applyConfirmedFeedback(projectDir, slug, feedback, { confirmed =
|
|
|
104
109
|
if (returnedToDraft) {
|
|
105
110
|
returnApprovedBriefingToDraft(registry, slug);
|
|
106
111
|
}
|
|
112
|
+
// A blocking finding the user left pending blocks the PRD the same way an
|
|
113
|
+
// explicit blocking item does — even if the export step never mirrored it
|
|
114
|
+
// into blocking_items (hand-written feedback).
|
|
115
|
+
const pendingBlockingFindings = (feedback.findings || []).filter(
|
|
116
|
+
(finding) => finding.blocking && finding.status === 'pending'
|
|
117
|
+
);
|
|
118
|
+
const hasBlockers = (feedback.blocking_items || []).length > 0 || pendingBlockingFindings.length > 0;
|
|
107
119
|
markRefinementState(registry, slug, {
|
|
108
|
-
refinement_status:
|
|
120
|
+
refinement_status: hasBlockers ? 'blocked' : 'applied',
|
|
109
121
|
review_html: `.aioson/briefings/${slug}/review.html`,
|
|
110
122
|
refinement_report: `.aioson/briefings/${slug}/refinement-report.md`
|
|
111
123
|
});
|
|
112
124
|
await writeBriefingRegistry(projectDir, registry);
|
|
113
125
|
|
|
114
126
|
const unresolvedComments = (feedback.comments || []).filter((comment) => !comment.resolved);
|
|
115
|
-
const nextAction =
|
|
127
|
+
const nextAction = hasBlockers ? 'resolve_blockers' : 'approve_briefing';
|
|
116
128
|
const report = buildRefinementReport({
|
|
117
129
|
briefing_slug: slug,
|
|
118
130
|
source_briefing_path: `.aioson/briefings/${slug}/briefings.md`,
|
|
@@ -120,15 +132,17 @@ async function applyConfirmedFeedback(projectDir, slug, feedback, { confirmed =
|
|
|
120
132
|
source_hash: feedback.source_hash,
|
|
121
133
|
applied_hash: appliedHash,
|
|
122
134
|
status: 'applied',
|
|
135
|
+
round: feedback.round || 1,
|
|
123
136
|
applied_changes: appliedChanges,
|
|
124
137
|
skipped_changes: [],
|
|
125
138
|
unresolved_comments: unresolvedComments,
|
|
126
139
|
blocking_items: feedback.blocking_items || [],
|
|
140
|
+
findings: feedback.findings || [],
|
|
127
141
|
next_action: nextAction
|
|
128
142
|
});
|
|
129
143
|
await fs.writeFile(resolveBriefingPath(projectDir, slug, 'refinement-report.md'), report, 'utf8');
|
|
130
144
|
|
|
131
|
-
return { ok: true, appliedChanges, nextAction, appliedHash, returnedToDraft };
|
|
145
|
+
return { ok: true, appliedChanges, nextAction, appliedHash, returnedToDraft, pendingBlockingFindings: pendingBlockingFindings.length };
|
|
132
146
|
}
|
|
133
147
|
|
|
134
148
|
module.exports = { applyConfirmedFeedback, applyDeclinedFeedback };
|