@jaimevalasek/aioson 1.29.1 → 1.30.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 +19 -0
- package/README.md +7 -5
- package/docs/en/5-reference/cli-reference.md +40 -10
- package/docs/pt/4-agentes/pm.md +1 -1
- package/docs/pt/5-referencia/autopilot-handoff.md +4 -4
- package/docs/pt/5-referencia/comandos-cli.md +5 -3
- package/docs/pt/5-referencia/fluxo-artefatos.md +1 -1
- package/docs/pt/5-referencia/memoria-e-contexto.md +2 -2
- package/docs/pt/_arquivo/monitor-de-contexto.md +2 -2
- package/package.json +4 -2
- package/src/cli.js +67 -24
- package/src/commands/ac-test-audit.js +45 -0
- package/src/commands/artifact-validate.js +62 -50
- package/src/commands/classify.js +73 -2
- package/src/commands/context-brief.js +59 -0
- package/src/commands/context-guard.js +88 -0
- package/src/commands/context-monitor.js +1 -1
- package/src/commands/context-search.js +101 -52
- package/src/commands/context-select.js +11 -2
- package/src/commands/feature-archive.js +21 -12
- package/src/commands/feature-current.js +82 -0
- package/src/commands/gate-check.js +32 -15
- package/src/commands/harness-check.js +17 -1
- package/src/commands/hooks-install.js +169 -26
- package/src/commands/hygiene-scan.js +423 -0
- package/src/commands/rules-lint.js +11 -3
- package/src/commands/sdd-benchmark.js +134 -0
- package/src/commands/spec-analyze.js +6 -4
- package/src/commands/store-system.js +329 -49
- package/src/constants.js +8 -3
- package/src/context-brief.js +585 -0
- package/src/context-guard.js +209 -0
- package/src/context-search.js +796 -96
- package/src/context-selector.js +802 -444
- package/src/handoff-contract.js +14 -6
- package/src/harness/contract-schema.js +1 -1
- package/src/i18n/messages/en.js +12 -5
- package/src/i18n/messages/es.js +11 -4
- package/src/i18n/messages/fr.js +11 -4
- package/src/i18n/messages/pt-BR.js +12 -5
- package/src/lib/ac-test-audit.js +194 -0
- package/src/preflight-engine.js +10 -6
- package/src/squad/state-manager.js +1 -1
- package/template/.aioson/agents/analyst.md +41 -17
- package/template/.aioson/agents/architect.md +4 -2
- package/template/.aioson/agents/briefing-refiner.md +15 -2
- package/template/.aioson/agents/briefing.md +12 -8
- package/template/.aioson/agents/committer.md +1 -1
- package/template/.aioson/agents/copywriter.md +20 -9
- package/template/.aioson/agents/design-hybrid-forge.md +9 -5
- package/template/.aioson/agents/dev.md +22 -25
- package/template/.aioson/agents/deyvin.md +126 -124
- package/template/.aioson/agents/discover.md +3 -1
- package/template/.aioson/agents/discovery-design-doc.md +11 -2
- package/template/.aioson/agents/forge-run.md +3 -0
- package/template/.aioson/agents/genome.md +9 -5
- package/template/.aioson/agents/neo.md +30 -24
- package/template/.aioson/agents/orache.md +10 -6
- package/template/.aioson/agents/orchestrator.md +4 -2
- package/template/.aioson/agents/pentester.md +22 -12
- package/template/.aioson/agents/pm.md +5 -3
- package/template/.aioson/agents/product.md +25 -18
- package/template/.aioson/agents/profiler-enricher.md +10 -6
- package/template/.aioson/agents/profiler-forge.md +10 -6
- package/template/.aioson/agents/profiler-researcher.md +10 -6
- package/template/.aioson/agents/qa.md +21 -19
- package/template/.aioson/agents/scope-check.md +9 -3
- package/template/.aioson/agents/sheldon.md +22 -8
- package/template/.aioson/agents/site-forge.md +2 -0
- package/template/.aioson/agents/squad.md +4 -2
- package/template/.aioson/agents/tester.md +19 -15
- package/template/.aioson/agents/ux-ui.md +16 -8
- package/template/.aioson/config.md +4 -3
- package/template/.aioson/design-docs/agent-loading-contract.md +3 -3
- package/template/.aioson/docs/autopilot-handoff.md +3 -3
- package/template/.aioson/docs/dev/simple-plan-lane.md +73 -27
- package/template/.aioson/docs/dev/stack-conventions.md +1 -1
- package/template/.aioson/docs/deyvin/continuity-recovery.md +1 -1
- package/template/.aioson/docs/deyvin/runtime-handoffs.md +3 -3
- package/template/.aioson/docs/feature-expansion-taxonomy.md +53 -0
- package/template/.aioson/docs/handoff-persistence.md +14 -12
- package/template/.aioson/docs/product/conversation-playbook.md +1 -1
- package/template/.aioson/docs/sheldon/enrichment-paths.md +44 -1
- package/template/.aioson/docs/sheldon/harness-contract.md +23 -21
- package/template/.aioson/docs/tester/coverage-quality.md +1 -1
- package/template/.aioson/docs/ux-ui/design-execution.md +9 -7
- package/template/.aioson/rules/README.md +35 -17
- package/template/.aioson/rules/agent-structural-contract.md +165 -160
- package/template/.aioson/rules/aioson-context-boundary.md +5 -4
- package/template/.aioson/rules/canonical-path-contract.md +5 -4
- package/template/.aioson/rules/data-format-convention.md +5 -4
- package/template/.aioson/rules/disk-first-artifacts.md +2 -2
- package/template/.aioson/rules/implementation-structure-and-data-access.md +50 -0
- package/template/.aioson/rules/security-baseline.md +4 -3
- package/template/.aioson/rules/simple-plan-lane.md +18 -6
- package/template/.aioson/rules/source-code-language-convention.md +34 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/artifact-map.md +24 -23
- package/template/.aioson/skills/process/aioson-spec-driven/references/classification-map.md +4 -0
- package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +2 -2
- package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +1 -1
- package/template/.aioson/skills/process/briefing-expansion-scout/SKILL.md +72 -0
- package/template/.aioson/skills/process/product-scope-expansion/SKILL.md +74 -0
- package/template/.aioson/skills/process/sheldon-expansion-audit/SKILL.md +67 -0
- package/template/.aioson/skills/static/context-budget-guide.md +1 -1
- package/template/.aioson/skills/static/multi-agent-patterns.md +5 -4
- package/template/AGENTS.md +36 -19
- package/template/CLAUDE.md +9 -5
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { buildContextBrief, extractDocConstraints } = require('./context-brief');
|
|
5
|
+
const { parseFrontmatter, readFileSafe } = require('./preflight-engine');
|
|
6
|
+
|
|
7
|
+
// Harness-agnostic core for `context:guard`.
|
|
8
|
+
//
|
|
9
|
+
// Operational retrieval loop: a harness extension point (e.g. a Claude Code
|
|
10
|
+
// PreToolUse hook) feeds the pending tool event in, and the guard derives a
|
|
11
|
+
// query from the artifact itself — never from a model-emitted keyword list —
|
|
12
|
+
// runs the proven context:brief engine, and returns an injection payload when a
|
|
13
|
+
// project rule is genuinely salient to the change about to be written.
|
|
14
|
+
|
|
15
|
+
// File-mutating tools whose payload is worth checking against project rules.
|
|
16
|
+
const MUTATING_TOOLS = new Set(['Write', 'Edit', 'MultiEdit', 'NotebookEdit']);
|
|
17
|
+
|
|
18
|
+
// A rule only counts when context:brief routed it through a hard signal, never
|
|
19
|
+
// through a foundation always-load or a pure semantic guess.
|
|
20
|
+
const HARD_SIGNAL = /(?:triggers|paths|entities|aliases|task_types):/;
|
|
21
|
+
|
|
22
|
+
// Salience gate: a rule opts into guard injection by declaring `entities` or
|
|
23
|
+
// `aliases`, or by explicitly setting `guard: true` in frontmatter. The explicit
|
|
24
|
+
// opt-in is for project contracts that are path/task-bound but not domain-entity
|
|
25
|
+
// rules (e.g. agent prompt structure). Generic baseline rules remain silent.
|
|
26
|
+
const DOMAIN_SIGNAL = /(?:entities|aliases):/;
|
|
27
|
+
|
|
28
|
+
// A guard rule that declares `paths` is a contract over those files. It may
|
|
29
|
+
// still surface in the brief via fuzzy trigger/description keyword overlap when
|
|
30
|
+
// an UNRELATED file is edited — but it must only inject when the edited path is
|
|
31
|
+
// actually in scope. This `paths:` reason is the proof the file matched.
|
|
32
|
+
const PATH_MATCH_SIGNAL = /\bpaths:/;
|
|
33
|
+
|
|
34
|
+
// Tunable relevance gate.
|
|
35
|
+
const GUARD_GATE = {
|
|
36
|
+
minConfidence: 'medium', // 'low' briefs never inject
|
|
37
|
+
maxConstraints: 10,
|
|
38
|
+
maxForbidden: 6,
|
|
39
|
+
maxContentChars: 4000
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const CONFIDENCE_RANK = { low: 0, medium: 1, high: 2 };
|
|
43
|
+
|
|
44
|
+
function emptyResponse() {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function extractEditedContent(toolInput = {}) {
|
|
49
|
+
const parts = [];
|
|
50
|
+
if (typeof toolInput.content === 'string') parts.push(toolInput.content);
|
|
51
|
+
if (typeof toolInput.new_string === 'string') parts.push(toolInput.new_string);
|
|
52
|
+
if (typeof toolInput.old_string === 'string') parts.push(toolInput.old_string);
|
|
53
|
+
if (typeof toolInput.new_source === 'string') parts.push(toolInput.new_source);
|
|
54
|
+
if (Array.isArray(toolInput.edits)) {
|
|
55
|
+
for (const edit of toolInput.edits) {
|
|
56
|
+
if (edit && typeof edit.new_string === 'string') parts.push(edit.new_string);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return parts.join('\n');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function deriveQuery(filePath, content, limit = GUARD_GATE.maxContentChars) {
|
|
63
|
+
const base = filePath
|
|
64
|
+
? path.basename(String(filePath)).replace(/\.[^.]+$/, '').replace(/[_-]+/g, ' ')
|
|
65
|
+
: '';
|
|
66
|
+
const body = String(content || '').slice(0, limit);
|
|
67
|
+
return `${base} ${body}`.trim();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function matchedRules(brief) {
|
|
71
|
+
return (brief.must_load || []).filter((item) => (
|
|
72
|
+
item.surface === 'rules' && HARD_SIGNAL.test(item.reason || '')
|
|
73
|
+
));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function truthyFrontmatter(value) {
|
|
77
|
+
return ['1', 'true', 'yes', 'on'].includes(String(value || '').trim().toLowerCase());
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function ruleDeclaresPaths(frontmatter) {
|
|
81
|
+
return Boolean(frontmatter && (frontmatter.paths || frontmatter.globs));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function ruleAllowsGuard(rule, frontmatter) {
|
|
85
|
+
const reason = rule.reason || '';
|
|
86
|
+
if (DOMAIN_SIGNAL.test(reason)) return true;
|
|
87
|
+
if (!truthyFrontmatter(frontmatter.guard) || !HARD_SIGNAL.test(reason)) return false;
|
|
88
|
+
// Path-scoped guard rule: inject only when the edited file is genuinely in its
|
|
89
|
+
// declared path scope, never on fuzzy keyword spill from an unrelated file.
|
|
90
|
+
if (ruleDeclaresPaths(frontmatter) && !PATH_MATCH_SIGNAL.test(reason)) return false;
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function confidenceAllows(confidence, gate) {
|
|
95
|
+
const have = CONFIDENCE_RANK[confidence] ?? 0;
|
|
96
|
+
const need = CONFIDENCE_RANK[gate.minConfidence] ?? 1;
|
|
97
|
+
return have >= need;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function dedupeStrings(items) {
|
|
101
|
+
const seen = new Set();
|
|
102
|
+
const out = [];
|
|
103
|
+
for (const item of items || []) {
|
|
104
|
+
const text = String(item || '').trim();
|
|
105
|
+
if (!text || seen.has(text)) continue;
|
|
106
|
+
seen.add(text);
|
|
107
|
+
out.push(text);
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function normalizeRuleLine(value) {
|
|
113
|
+
return String(value || '').trim().toLowerCase();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Read each salient rule file and extract ITS OWN constraints — so the
|
|
117
|
+
// injection is attributed per rule and never carries the generic concern-based
|
|
118
|
+
// constraints the brief aggregates from the whole selection.
|
|
119
|
+
async function buildRuleBlocks(targetDir, salient, gate) {
|
|
120
|
+
const blocks = [];
|
|
121
|
+
for (const rule of salient) {
|
|
122
|
+
const content = await readFileSafe(path.join(targetDir, rule.path));
|
|
123
|
+
if (!content) continue;
|
|
124
|
+
const frontmatter = parseFrontmatter(content);
|
|
125
|
+
if (!ruleAllowsGuard(rule, frontmatter)) continue;
|
|
126
|
+
const extracted = extractDocConstraints(content);
|
|
127
|
+
const constraints = dedupeStrings(extracted.constraints).slice(0, gate.maxConstraints);
|
|
128
|
+
const constraintSet = new Set(constraints.map(normalizeRuleLine));
|
|
129
|
+
const forbidden = dedupeStrings(extracted.forbidden_patterns)
|
|
130
|
+
.filter((item) => !constraintSet.has(normalizeRuleLine(item)))
|
|
131
|
+
.slice(0, gate.maxForbidden);
|
|
132
|
+
if (constraints.length === 0 && forbidden.length === 0) continue;
|
|
133
|
+
blocks.push({ path: rule.path, constraints, forbidden });
|
|
134
|
+
}
|
|
135
|
+
return blocks;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function formatInjectionText(filePath, ruleBlocks) {
|
|
139
|
+
const target = filePath ? path.basename(String(filePath)) : 'this change';
|
|
140
|
+
const lines = [`[AIOSON context:guard] Project rules apply to ${target}:`];
|
|
141
|
+
for (const block of ruleBlocks) {
|
|
142
|
+
lines.push(`Rule ${block.path}:`);
|
|
143
|
+
for (const constraint of block.constraints) lines.push(`- ${constraint}`);
|
|
144
|
+
for (const pattern of block.forbidden) lines.push(`- (forbidden) ${pattern}`);
|
|
145
|
+
}
|
|
146
|
+
return lines.join('\n');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function formatForTool(tool, additionalContext) {
|
|
150
|
+
// Only the Claude Code adapter exists today; other harnesses default to it
|
|
151
|
+
// until their own extension point is wired.
|
|
152
|
+
switch (tool) {
|
|
153
|
+
case 'claude':
|
|
154
|
+
default:
|
|
155
|
+
return {
|
|
156
|
+
hookSpecificOutput: {
|
|
157
|
+
hookEventName: 'PreToolUse',
|
|
158
|
+
additionalContext
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function buildGuardResponse(event, targetDir, options = {}) {
|
|
165
|
+
const gate = { ...GUARD_GATE, ...(options.gate || {}) };
|
|
166
|
+
const toolName = event && event.tool_name;
|
|
167
|
+
const toolInput = (event && event.tool_input) || {};
|
|
168
|
+
if (!MUTATING_TOOLS.has(toolName)) return emptyResponse();
|
|
169
|
+
|
|
170
|
+
const filePath = toolInput.file_path || toolInput.notebook_path || '';
|
|
171
|
+
const content = extractEditedContent(toolInput);
|
|
172
|
+
if (!filePath && !content) return emptyResponse();
|
|
173
|
+
|
|
174
|
+
const query = deriveQuery(filePath, content, gate.maxContentChars);
|
|
175
|
+
if (!query) return emptyResponse();
|
|
176
|
+
|
|
177
|
+
const brief = await buildContextBrief(targetDir, {
|
|
178
|
+
agent: options.agent || 'dev',
|
|
179
|
+
mode: 'executing',
|
|
180
|
+
task: query,
|
|
181
|
+
paths: filePath
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const ruled = matchedRules(brief);
|
|
185
|
+
if (ruled.length === 0) return emptyResponse();
|
|
186
|
+
if (!confidenceAllows(brief.confidence, gate)) return emptyResponse();
|
|
187
|
+
|
|
188
|
+
const ruleBlocks = await buildRuleBlocks(targetDir, ruled, gate);
|
|
189
|
+
if (ruleBlocks.length === 0) return emptyResponse();
|
|
190
|
+
|
|
191
|
+
const additionalContext = formatInjectionText(filePath, ruleBlocks);
|
|
192
|
+
const response = formatForTool(options.tool || 'claude', additionalContext);
|
|
193
|
+
response._guard = {
|
|
194
|
+
injected: true,
|
|
195
|
+
rules: ruleBlocks.map((block) => block.path),
|
|
196
|
+
confidence: brief.confidence
|
|
197
|
+
};
|
|
198
|
+
return response;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
module.exports = {
|
|
202
|
+
buildGuardResponse,
|
|
203
|
+
deriveQuery,
|
|
204
|
+
extractEditedContent,
|
|
205
|
+
matchedRules,
|
|
206
|
+
ruleAllowsGuard,
|
|
207
|
+
MUTATING_TOOLS,
|
|
208
|
+
GUARD_GATE
|
|
209
|
+
};
|