@jaimevalasek/aioson 1.17.2 → 1.18.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 +13 -0
- package/README.md +85 -51
- package/docs/en/3-recipes/full-feature-with-sheldon.md +1 -1
- package/docs/en/5-reference/cli-reference.md +4 -4
- package/docs/en/5-reference/qa-browser.md +2 -2
- package/docs/en/README.md +1 -1
- package/docs/en/deyvin-subtask-scout/how-to-use.md +2 -2
- package/docs/en/deyvin-subtask-scout/sub-task-scout.md +3 -3
- package/docs/en/deyvin-subtask-scout/troubleshooting.md +1 -1
- package/docs/pt/3-receitas/publicar-no-aioson-com.md +17 -0
- package/docs/pt/5-referencia/comandos-cli.md +2 -2
- package/docs/pt/5-referencia/inteligencia-adaptativa.md +3 -3
- package/docs/pt/5-referencia/skills.md +1 -1
- package/docs/pt/5-referencia/web3.md +3 -3
- package/docs/pt/README.md +1 -1
- package/docs/pt/_arquivo/README.md +1 -1
- package/docs/pt/_arquivo/cenarios.md +31 -31
- package/docs/pt/_arquivo/design-hybrid-forge.md +5 -5
- package/docs/pt/_arquivo/guia-engineer.md +1 -1
- package/docs/pt/_arquivo/profiler-system.md +1 -1
- package/docs/pt/_arquivo/site-forge.md +16 -16
- package/docs/pt/_arquivo/squad-genome.md +2 -2
- package/docs/pt/agentes.md +37 -37
- package/docs/pt/deyvin-subtask-scout/como-usar.md +2 -2
- package/docs/pt/deyvin-subtask-scout/sub-task-scout.md +1 -1
- package/docs/pt/deyvin-subtask-scout/troubleshooting.md +1 -1
- package/docs/pt/living-memory/README.md +1 -1
- package/docs/pt/living-memory/memoria-viva.md +2 -2
- package/docs/pt/living-memory/reflexao-in-harness.md +1 -1
- package/docs/pt/living-memory/troubleshooting.md +6 -6
- package/package.json +4 -2
- package/src/commands/gate-approve.js +56 -1
- package/src/commands/live.js +81 -54
- package/src/commands/op-capture.js +27 -2
- package/src/commands/op-list.js +33 -1
- package/src/commands/store-system.js +104 -12
- package/src/commands/tool-capabilities.js +14 -10
- package/src/commands/workflow-heal.js +47 -1
- package/src/i18n/messages/en.js +6 -5
- package/src/i18n/messages/pt-BR.js +6 -5
- package/src/lib/dev-resume.js +6 -1
- package/src/lib/tool-capabilities.js +64 -37
- package/src/operator-memory/decision.js +11 -4
- package/src/operator-memory/proposal.js +11 -7
- package/src/session-handoff.js +52 -1
- package/template/.aioson/agents/analyst.md +34 -2
- package/template/.aioson/agents/architect.md +33 -1
- package/template/.aioson/agents/briefing.md +26 -1
- package/template/.aioson/agents/copywriter.md +1 -1
- package/template/.aioson/agents/dev.md +2 -2
- package/template/.aioson/agents/deyvin.md +12 -12
- package/template/.aioson/agents/neo.md +74 -74
- package/template/.aioson/agents/orchestrator.md +26 -0
- package/template/.aioson/agents/pentester.md +66 -14
- package/template/.aioson/agents/pm.md +18 -1
- package/template/.aioson/agents/product.md +12 -1
- package/template/.aioson/agents/qa.md +3 -3
- package/template/.aioson/agents/sheldon.md +24 -4
- package/template/.aioson/agents/tester.md +115 -2
- package/template/.aioson/docs/briefing/briefing-craft.md +16 -0
- package/template/.aioson/docs/deyvin/runtime-handoffs.md +1 -1
- package/template/.aioson/docs/handoff-persistence.md +7 -7
- package/template/.aioson/docs/pentester/browser-dast-playbook.md +398 -0
- package/template/.aioson/rules/agent-structural-contract.md +139 -0
- package/template/.aioson/skills/process/decision-presentation/SKILL.md +2 -2
|
@@ -217,7 +217,7 @@ module.exports = {
|
|
|
217
217
|
help_runtime_emit:
|
|
218
218
|
'aioson runtime:emit [path] --agent=<nome> [--type=<evento>] [--summary=<texto>] [--title=<texto>] [--refs=<arquivo[,arquivo2]>] [--plan-step=<id>] [--meta=<json>] [--json] [--locale=pt-BR]',
|
|
219
219
|
help_live_start:
|
|
220
|
-
'aioson live:start [path] --tool=codex|claude|gemini|opencode --agent=<nome> [--tool-bin=<binario>] [--tool-args=<args>] [--title=<texto>] [--goal=<texto>] [--plan=<arquivo>] [--session=<chave>] [--message=<texto>] [--attach] [--no-launch] [--tmux] [--json] [--locale=pt-BR]',
|
|
220
|
+
'aioson live:start [path] --tool=codex|claude|gemini|opencode --agent=<nome> [--tool-bin=<binario>] [--permission-mode=default|yolo] [--tool-args=<args>] [--title=<texto>] [--goal=<texto>] [--plan=<arquivo>] [--session=<chave>] [--message=<texto>] [--attach] [--no-launch] [--tmux] [--json] [--locale=pt-BR]',
|
|
221
221
|
help_live_status:
|
|
222
222
|
'aioson live:status [path] [--agent=<nome>] [--limit=8] [--watch=2] [--format=compact|tmux-bar] [--json] [--locale=pt-BR]',
|
|
223
223
|
help_live_handoff:
|
|
@@ -1116,10 +1116,11 @@ module.exports = {
|
|
|
1116
1116
|
plan_not_found: 'Arquivo de plano nao encontrado: {plan}',
|
|
1117
1117
|
no_active_session: 'Nenhuma sessao live ativa encontrada para {agent}.',
|
|
1118
1118
|
session_not_active: 'A sessao live de {agent} nao esta ativa.',
|
|
1119
|
-
json_requires_no_launch: '--json requer --no-launch para live:start porque o lancamento em primeiro plano e interativo.',
|
|
1120
|
-
tool_binary_not_found: 'Binario da ferramenta nao encontrado no PATH: {binary}',
|
|
1121
|
-
tool_mismatch: 'A sessao ativa usa a ferramenta "{existing}" mas --tool={requested} foi informado. Encerre a sessao primeiro ou use a mesma ferramenta.',
|
|
1122
|
-
|
|
1119
|
+
json_requires_no_launch: '--json requer --no-launch para live:start porque o lancamento em primeiro plano e interativo.',
|
|
1120
|
+
tool_binary_not_found: 'Binario da ferramenta nao encontrado no PATH: {binary}',
|
|
1121
|
+
tool_mismatch: 'A sessao ativa usa a ferramenta "{existing}" mas --tool={requested} foi informado. Encerre a sessao primeiro ou use a mesma ferramenta.',
|
|
1122
|
+
tool_mismatch_auto_closed: 'A sessao live anterior usava "{existing}" e foi fechada automaticamente. Iniciando nova sessao com "{requested}".',
|
|
1123
|
+
micro_task_already_open: 'Uma micro-tarefa live ja esta aberta para {agent}. Emita task_completed antes de task_started novamente.',
|
|
1123
1124
|
handoff_same_agent: 'live:handoff requer valores diferentes para --agent e --to.',
|
|
1124
1125
|
handoff_agent_mismatch: 'Nenhuma sessao live ativa encontrada para {agent}.',
|
|
1125
1126
|
watch_json_conflict: '--watch nao pode ser combinado com --json.',
|
package/src/lib/dev-resume.js
CHANGED
|
@@ -121,6 +121,10 @@ async function buildDevResumeData(projectPath) {
|
|
|
121
121
|
? lastHandoff.artifact_uris
|
|
122
122
|
: [];
|
|
123
123
|
|
|
124
|
+
const decisionRationale = Array.isArray(lastHandoff && lastHandoff.decision_rationale)
|
|
125
|
+
? lastHandoff.decision_rationale
|
|
126
|
+
: [];
|
|
127
|
+
|
|
124
128
|
return {
|
|
125
129
|
feature_slug: featureSlug,
|
|
126
130
|
classification,
|
|
@@ -128,7 +132,8 @@ async function buildDevResumeData(projectPath) {
|
|
|
128
132
|
artifacts_consumed: artifactsConsumed,
|
|
129
133
|
code_map_paths: extractCodeMapPaths(dossierRaw),
|
|
130
134
|
sheldon_plan: sheldonPlan,
|
|
131
|
-
next_step: devStateNext || deriveNextStepFromPlan(planRaw)
|
|
135
|
+
next_step: devStateNext || deriveNextStepFromPlan(planRaw),
|
|
136
|
+
decision_rationale: decisionRationale.length > 0 ? decisionRationale : undefined
|
|
132
137
|
};
|
|
133
138
|
}
|
|
134
139
|
|
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
// "continue last conversation" is achieved by passing the right resume flag
|
|
6
6
|
// at spawn time — AIOSON never has to track an internal session ID.
|
|
7
7
|
//
|
|
8
|
-
// Used by:
|
|
9
|
-
// - `aioson live:start --resume[=last|<id>]` to map to the correct argv
|
|
10
|
-
// - `aioson
|
|
11
|
-
//
|
|
8
|
+
// Used by:
|
|
9
|
+
// - `aioson live:start --resume[=last|<id>]` to map to the correct argv
|
|
10
|
+
// - `aioson live:start --permission-mode=yolo` to map to the correct argv
|
|
11
|
+
// - `aioson tool:capabilities` to expose this map as JSON to UI clients
|
|
12
|
+
// (e.g. AIOSON Play) so they don't duplicate the lookup.
|
|
12
13
|
//
|
|
13
14
|
// Keep entries minimal and source-of-truth here. Adding a new CLI = one entry.
|
|
14
15
|
const TOOL_CAPS = {
|
|
@@ -17,42 +18,50 @@ const TOOL_CAPS = {
|
|
|
17
18
|
binary: 'claude',
|
|
18
19
|
supports_resume: true,
|
|
19
20
|
resume_last: ['--continue'],
|
|
20
|
-
supports_session_id: true,
|
|
21
|
-
resume_session_id: ['--resume', '<id>'],
|
|
22
|
-
supports_session_picker: true,
|
|
23
|
-
session_picker: ['--resume'],
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
supports_session_id: true,
|
|
22
|
+
resume_session_id: ['--resume', '<id>'],
|
|
23
|
+
supports_session_picker: true,
|
|
24
|
+
session_picker: ['--resume'],
|
|
25
|
+
supports_yolo: true,
|
|
26
|
+
yolo_args: ['--dangerously-skip-permissions'],
|
|
27
|
+
},
|
|
28
|
+
codex: {
|
|
26
29
|
install_command: 'npm install -g @openai/codex',
|
|
27
30
|
binary: 'codex',
|
|
28
31
|
supports_resume: true,
|
|
29
32
|
resume_last: ['resume', '--last'],
|
|
30
|
-
supports_session_id: true,
|
|
31
|
-
resume_session_id: ['resume', '<id>'],
|
|
32
|
-
supports_session_picker: true,
|
|
33
|
-
session_picker: ['resume'],
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
supports_session_id: true,
|
|
34
|
+
resume_session_id: ['resume', '<id>'],
|
|
35
|
+
supports_session_picker: true,
|
|
36
|
+
session_picker: ['resume'],
|
|
37
|
+
supports_yolo: true,
|
|
38
|
+
yolo_args: ['--dangerously-bypass-approvals-and-sandbox'],
|
|
39
|
+
},
|
|
40
|
+
opencode: {
|
|
36
41
|
install_command: 'npm install -g opencode-ai',
|
|
37
42
|
binary: 'opencode',
|
|
38
43
|
supports_resume: true,
|
|
39
44
|
resume_last: ['--continue'],
|
|
40
|
-
supports_session_id: true,
|
|
41
|
-
resume_session_id: ['--session', '<id>'],
|
|
42
|
-
supports_session_picker: false,
|
|
43
|
-
session_picker: null,
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
supports_session_id: true,
|
|
46
|
+
resume_session_id: ['--session', '<id>'],
|
|
47
|
+
supports_session_picker: false,
|
|
48
|
+
session_picker: null,
|
|
49
|
+
supports_yolo: false,
|
|
50
|
+
yolo_args: null,
|
|
51
|
+
},
|
|
52
|
+
gemini: {
|
|
46
53
|
install_command: 'npm install -g @google/gemini-cli',
|
|
47
54
|
binary: 'gemini',
|
|
48
55
|
supports_resume: false,
|
|
49
56
|
resume_last: null,
|
|
50
|
-
supports_session_id: false,
|
|
51
|
-
resume_session_id: null,
|
|
52
|
-
supports_session_picker: false,
|
|
53
|
-
session_picker: null,
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
supports_session_id: false,
|
|
58
|
+
resume_session_id: null,
|
|
59
|
+
supports_session_picker: false,
|
|
60
|
+
session_picker: null,
|
|
61
|
+
supports_yolo: false,
|
|
62
|
+
yolo_args: null,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
56
65
|
|
|
57
66
|
function getToolCapabilities(tool) {
|
|
58
67
|
const key = String(tool || '').trim().toLowerCase();
|
|
@@ -71,7 +80,7 @@ function listSupportedTools() {
|
|
|
71
80
|
// - '' / undefined / null / false → no resume
|
|
72
81
|
// - any other string → treat as session id
|
|
73
82
|
// Returns [] when the tool doesn't support resume or resumeOpt is falsy.
|
|
74
|
-
function resolveResumeArgs(tool, resumeOpt) {
|
|
83
|
+
function resolveResumeArgs(tool, resumeOpt) {
|
|
75
84
|
if (resumeOpt === undefined || resumeOpt === null || resumeOpt === '' || resumeOpt === false) {
|
|
76
85
|
return [];
|
|
77
86
|
}
|
|
@@ -92,11 +101,29 @@ function resolveResumeArgs(tool, resumeOpt) {
|
|
|
92
101
|
}
|
|
93
102
|
|
|
94
103
|
return Array.isArray(caps.resume_last) ? [...caps.resume_last] : [];
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function resolvePermissionModeArgs(tool, permissionMode) {
|
|
107
|
+
const mode = String(permissionMode || '').trim().toLowerCase();
|
|
108
|
+
if (!mode || mode === 'default') return [];
|
|
109
|
+
if (mode !== 'yolo') {
|
|
110
|
+
throw new Error(`permission_mode_unknown:${permissionMode}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const caps = getToolCapabilities(tool);
|
|
114
|
+
if (!caps) {
|
|
115
|
+
throw new Error(`tool_unknown:${tool}`);
|
|
116
|
+
}
|
|
117
|
+
if (!caps.supports_yolo || !Array.isArray(caps.yolo_args)) {
|
|
118
|
+
throw new Error(`permission_mode_unsupported:${tool}:yolo`);
|
|
119
|
+
}
|
|
120
|
+
return [...caps.yolo_args];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
TOOL_CAPS,
|
|
125
|
+
getToolCapabilities,
|
|
126
|
+
listSupportedTools,
|
|
127
|
+
resolveResumeArgs,
|
|
128
|
+
resolvePermissionModeArgs,
|
|
129
|
+
};
|
|
@@ -81,7 +81,7 @@ function deriveTitle(proposal) {
|
|
|
81
81
|
function serializeDecision(data) {
|
|
82
82
|
const body = String(data.body || data.proposal || '').slice(0, MAX_BODY_CHARS);
|
|
83
83
|
const title = deriveTitle(data.title || data.proposal);
|
|
84
|
-
|
|
84
|
+
const lines = [
|
|
85
85
|
'---',
|
|
86
86
|
`slug: ${data.slug}`,
|
|
87
87
|
`signal_type: ${data.signal_type}`,
|
|
@@ -93,8 +93,13 @@ function serializeDecision(data) {
|
|
|
93
93
|
`source_agent: ${data.source_agent}`,
|
|
94
94
|
`quotes:${quotesToYaml(data.quotes)}`,
|
|
95
95
|
`version_schema: "${SCHEMA_VERSION}"`,
|
|
96
|
-
`deprecated_by: ${data.deprecated_by ?? 'null'}
|
|
97
|
-
|
|
96
|
+
`deprecated_by: ${data.deprecated_by ?? 'null'}`
|
|
97
|
+
];
|
|
98
|
+
if (data.feature_slug) lines.push(`feature_slug: ${escapeYamlString(data.feature_slug)}`);
|
|
99
|
+
if (data.session_id) lines.push(`session_id: ${escapeYamlString(data.session_id)}`);
|
|
100
|
+
lines.push('---');
|
|
101
|
+
return [
|
|
102
|
+
...lines,
|
|
98
103
|
'',
|
|
99
104
|
`# ${title}`,
|
|
100
105
|
'',
|
|
@@ -178,7 +183,9 @@ function promoteProposal({ identity, proposal: proposalData }) {
|
|
|
178
183
|
quotes: proposalData.quotes || [],
|
|
179
184
|
body,
|
|
180
185
|
title: proposalData.proposal,
|
|
181
|
-
deprecated_by: null
|
|
186
|
+
deprecated_by: null,
|
|
187
|
+
feature_slug: proposalData.feature_slug || null,
|
|
188
|
+
session_id: proposalData.session_id || null
|
|
182
189
|
};
|
|
183
190
|
|
|
184
191
|
const decFilePath = decisionPath(identity, decision.slug);
|
|
@@ -46,7 +46,7 @@ function quotesToYaml(quotes) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function serializeProposal(data) {
|
|
49
|
-
|
|
49
|
+
const lines = [
|
|
50
50
|
'---',
|
|
51
51
|
`slug: ${data.slug}`,
|
|
52
52
|
`signal_type: ${data.signal_type}`,
|
|
@@ -56,10 +56,12 @@ function serializeProposal(data) {
|
|
|
56
56
|
`quotes:${quotesToYaml(data.quotes)}`,
|
|
57
57
|
`proposal: ${escapeYamlString(data.proposal)}`,
|
|
58
58
|
`source_agent: ${data.source_agent}`,
|
|
59
|
-
`proposal_fingerprint: ${data.proposal_fingerprint}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
`proposal_fingerprint: ${data.proposal_fingerprint}`
|
|
60
|
+
];
|
|
61
|
+
if (data.feature_slug) lines.push(`feature_slug: ${escapeYamlString(data.feature_slug)}`);
|
|
62
|
+
if (data.session_id) lines.push(`session_id: ${escapeYamlString(data.session_id)}`);
|
|
63
|
+
lines.push('---', '');
|
|
64
|
+
return lines.join('\n');
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
function parseProposalFrontmatter(content) {
|
|
@@ -130,7 +132,7 @@ function deleteProposal(identity, slug) {
|
|
|
130
132
|
return false;
|
|
131
133
|
}
|
|
132
134
|
|
|
133
|
-
function captureSignal({ identity, slug, signal_type, quote, proposal, source_agent }) {
|
|
135
|
+
function captureSignal({ identity, slug, signal_type, quote, proposal, source_agent, feature_slug, session_id }) {
|
|
134
136
|
if (!VALID_SIGNAL_TYPES.includes(signal_type)) {
|
|
135
137
|
throw new Error(`Invalid signal_type '${signal_type}'. Must be one of: ${VALID_SIGNAL_TYPES.join(', ')}`);
|
|
136
138
|
}
|
|
@@ -160,7 +162,9 @@ function captureSignal({ identity, slug, signal_type, quote, proposal, source_ag
|
|
|
160
162
|
quotes: quote ? [String(quote).trim()].filter(Boolean) : [],
|
|
161
163
|
proposal,
|
|
162
164
|
source_agent: source_agent || 'unknown',
|
|
163
|
-
proposal_fingerprint: fingerprintProposal(proposal)
|
|
165
|
+
proposal_fingerprint: fingerprintProposal(proposal),
|
|
166
|
+
feature_slug: feature_slug || null,
|
|
167
|
+
session_id: session_id || null
|
|
164
168
|
};
|
|
165
169
|
writeProposal(identity, fresh);
|
|
166
170
|
return { proposal: fresh, isNew: true };
|
package/src/session-handoff.js
CHANGED
|
@@ -6,6 +6,8 @@ const { exists, ensureDir } = require('./utils');
|
|
|
6
6
|
|
|
7
7
|
const HANDOFF_RELATIVE_PATH = '.aioson/context/last-handoff.json';
|
|
8
8
|
const HANDOFF_PROTOCOL_RELATIVE_PATH = '.aioson/context/handoff-protocol.json';
|
|
9
|
+
const CONFIRMATIONS_JSONL = '.aioson/runtime/session-confirmations.jsonl';
|
|
10
|
+
const DECISION_RATIONALE_MAX = 5;
|
|
9
11
|
|
|
10
12
|
// handoff-protocol.json schema_version for `artifact_uris`:
|
|
11
13
|
// v1 (legacy) — array of strings; v2 — array of {path, kind, agent, added_at}.
|
|
@@ -63,10 +65,52 @@ function coerceArtifactUris(uris, fallbackAgent) {
|
|
|
63
65
|
.filter(Boolean);
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
async function collectDecisionRationale(targetDir) {
|
|
69
|
+
const accPath = path.join(targetDir, CONFIRMATIONS_JSONL);
|
|
70
|
+
try {
|
|
71
|
+
const raw = await fs.readFile(accPath, 'utf8');
|
|
72
|
+
const lines = raw.trim().split('\n').filter(Boolean);
|
|
73
|
+
const entries = [];
|
|
74
|
+
for (const line of lines) {
|
|
75
|
+
try {
|
|
76
|
+
const obj = JSON.parse(line);
|
|
77
|
+
entries.push({
|
|
78
|
+
agent: obj.agent || 'unknown',
|
|
79
|
+
decision: obj.decision || '',
|
|
80
|
+
alternatives_considered: null,
|
|
81
|
+
rationale: obj.quote || null,
|
|
82
|
+
confidence: 'confirmed'
|
|
83
|
+
});
|
|
84
|
+
} catch {
|
|
85
|
+
// skip malformed lines
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// BR-AO-04: FIFO — keep only the last N entries
|
|
89
|
+
return entries.slice(-DECISION_RATIONALE_MAX);
|
|
90
|
+
} catch {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function clearConfirmationsAccumulator(targetDir) {
|
|
96
|
+
const accPath = path.join(targetDir, CONFIRMATIONS_JSONL);
|
|
97
|
+
try {
|
|
98
|
+
await fs.unlink(accPath);
|
|
99
|
+
} catch {
|
|
100
|
+
// file may not exist
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
66
104
|
async function writeHandoff(targetDir, payload) {
|
|
67
105
|
const handoffPath = path.join(targetDir, HANDOFF_RELATIVE_PATH);
|
|
68
106
|
const protocolPath = path.join(targetDir, HANDOFF_PROTOCOL_RELATIVE_PATH);
|
|
69
107
|
await ensureDir(path.dirname(handoffPath));
|
|
108
|
+
|
|
109
|
+
// M2: collect decision rationale from session confirmations
|
|
110
|
+
const sessionRationale = await collectDecisionRationale(targetDir);
|
|
111
|
+
const existingRationale = Array.isArray(payload.decisionRationale) ? payload.decisionRationale : [];
|
|
112
|
+
const mergedRationale = [...existingRationale, ...sessionRationale].slice(-DECISION_RATIONALE_MAX);
|
|
113
|
+
|
|
70
114
|
const handoff = {
|
|
71
115
|
version: 1,
|
|
72
116
|
session_ended_at: new Date().toISOString(),
|
|
@@ -79,10 +123,14 @@ async function writeHandoff(targetDir, payload) {
|
|
|
79
123
|
context_files_updated: Array.isArray(payload.contextFilesUpdated) ? payload.contextFilesUpdated : [],
|
|
80
124
|
workflow_mode: payload.workflowMode || null,
|
|
81
125
|
classification: payload.classification || null,
|
|
82
|
-
feature_slug: payload.featureSlug || null
|
|
126
|
+
feature_slug: payload.featureSlug || null,
|
|
127
|
+
decision_rationale: mergedRationale.length > 0 ? mergedRationale : undefined
|
|
83
128
|
};
|
|
84
129
|
await fs.writeFile(handoffPath, `${JSON.stringify(handoff, null, 2)}\n`, 'utf8');
|
|
85
130
|
|
|
131
|
+
// Clear accumulator after writing to handoff
|
|
132
|
+
await clearConfirmationsAccumulator(targetDir);
|
|
133
|
+
|
|
86
134
|
const protocol = payload.protocol || buildBasicHandoffProtocol(payload);
|
|
87
135
|
await fs.writeFile(protocolPath, `${JSON.stringify(protocol, null, 2)}\n`, 'utf8');
|
|
88
136
|
|
|
@@ -283,9 +331,12 @@ function buildRuntimeLogHandoff(agentName, message, summary) {
|
|
|
283
331
|
module.exports = {
|
|
284
332
|
HANDOFF_RELATIVE_PATH,
|
|
285
333
|
HANDOFF_PROTOCOL_RELATIVE_PATH,
|
|
334
|
+
CONFIRMATIONS_JSONL,
|
|
335
|
+
DECISION_RATIONALE_MAX,
|
|
286
336
|
ARTIFACT_KINDS,
|
|
287
337
|
coerceArtifactUri,
|
|
288
338
|
coerceArtifactUris,
|
|
339
|
+
collectDecisionRationale,
|
|
289
340
|
writeHandoff,
|
|
290
341
|
readHandoff,
|
|
291
342
|
readHandoffProtocol,
|
|
@@ -34,6 +34,8 @@ Before any manual checks, run these commands if the `aioson` CLI is available:
|
|
|
34
34
|
```bash
|
|
35
35
|
aioson workflow:status . # confirm current stage and what is expected
|
|
36
36
|
aioson context:validate . # validate project.context.md; detects brownfield state
|
|
37
|
+
aioson preflight . --agent=analyst --feature={slug} # unified pre-session check: loads rules, design governance, and context
|
|
38
|
+
aioson classify . # auto-detect project classification (MICRO/SMALL/MEDIUM) for cross-reference
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
For feature mode with existing requirements, run before the synchronization gate:
|
|
@@ -331,7 +333,7 @@ Generate `.aioson/context/discovery.md` with the following sections:
|
|
|
331
333
|
|
|
332
334
|
## Dev handoff producer
|
|
333
335
|
|
|
334
|
-
Before the final `agent:done` call, when the next agent in the workflow is `@dev`, produce `dev-state.md` so the next `/dev` session auto-resumes on cold start instead of pinging the user for context:
|
|
336
|
+
Before the final `agent:done` call, when the next agent in the workflow is `@dev`, produce `dev-state.md` so the next `/aioson:agent:dev` session auto-resumes on cold start instead of pinging the user for context:
|
|
335
337
|
|
|
336
338
|
```bash
|
|
337
339
|
aioson dev:state:write . --feature={slug} --phase=1 \
|
|
@@ -341,5 +343,35 @@ aioson dev:state:write . --feature={slug} --phase=1 \
|
|
|
341
343
|
|
|
342
344
|
`--context` accepts canonical tokens (`prd`, `requirements`, `spec`, `architecture`, `impl-plan`, `sheldon`, `design-doc`, `dossier`), max 4 entries total; missing files emit a warning and are skipped. Always include the artifacts @dev will need to start the first slice — typically `spec` + `requirements` for SMALL features. Idempotent: re-running with the same args does not duplicate state.
|
|
343
345
|
|
|
346
|
+
**Handoff message:**
|
|
347
|
+
```
|
|
348
|
+
Requirements written: .aioson/context/requirements-{slug}.md
|
|
349
|
+
Spec skeleton: .aioson/context/spec-{slug}.md
|
|
350
|
+
Gate A: approved
|
|
351
|
+
Next agent: @architect (MEDIUM) or @dev (SMALL — skip architecture)
|
|
352
|
+
Why: Requirements and spec ready — @architect defines system design, or @dev starts implementation for SMALL features.
|
|
353
|
+
Action: /architect or /dev
|
|
354
|
+
```
|
|
355
|
+
> Recommended: `/clear` before activating — fresh context window.
|
|
356
|
+
|
|
357
|
+
## Strategic commands (use during session)
|
|
358
|
+
|
|
359
|
+
- Search memory before web research: `aioson memory:search . --query="<topic>" 2>/dev/null || true`
|
|
360
|
+
- Search context files: `aioson context:search . --query="<term>" 2>/dev/null || true`
|
|
361
|
+
- Compress context before handoff: `aioson context:pack . 2>/dev/null || true`
|
|
362
|
+
- Create spec checkpoint before changes: `aioson spec:checkpoint . --feature={slug} 2>/dev/null || true`
|
|
363
|
+
|
|
344
364
|
## Observability
|
|
345
|
-
|
|
365
|
+
|
|
366
|
+
At strategic milestones during execution, emit progress signals:
|
|
367
|
+
```bash
|
|
368
|
+
aioson runtime:emit . --agent=analyst --type=milestone --summary="Requirements written: {slug}, {N} BRs, {N} ECs" 2>/dev/null || true
|
|
369
|
+
aioson runtime:emit . --agent=analyst --type=milestone --summary="Spec skeleton created: {slug}" 2>/dev/null || true
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
At session end, register:
|
|
373
|
+
```bash
|
|
374
|
+
aioson gate:approve . --feature={slug} --gate=A 2>/dev/null || true
|
|
375
|
+
aioson pulse:update . --agent=analyst --feature={slug} --action="Discovery completed: {N} entities, {N} rules" --next="<next agent recommendation>" 2>/dev/null || true
|
|
376
|
+
aioson agent:done . --agent=analyst --summary="Discovery <slug>: <N> entities, <N> rules" 2>/dev/null || true
|
|
377
|
+
```
|
|
@@ -124,6 +124,20 @@ Before handing off to `@dev`:
|
|
|
124
124
|
- If a relevant spec file exists and design is still pending, do not claim Gate B passed.
|
|
125
125
|
- Tell the user explicitly whether Gate B passed or is blocked before handoff.
|
|
126
126
|
|
|
127
|
+
When Gate B passes, register it via CLI:
|
|
128
|
+
```bash
|
|
129
|
+
aioson gate:approve . --feature={slug} --gate=B 2>/dev/null || true
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Handoff message:**
|
|
133
|
+
```
|
|
134
|
+
Architecture defined: .aioson/context/architecture.md
|
|
135
|
+
Gate B: {approved|blocked}
|
|
136
|
+
Next agent: @pm (MEDIUM — implementation planning) or @dev (SMALL — direct implementation)
|
|
137
|
+
Action: /pm or /dev
|
|
138
|
+
```
|
|
139
|
+
> Recommended: `/clear` before activating — fresh context window.
|
|
140
|
+
|
|
127
141
|
## Rules
|
|
128
142
|
- Do not redesign entities produced by `@analyst`. Consume the data design as-is.
|
|
129
143
|
- Keep architecture proportional to classification. Never apply MEDIUM patterns to a MICRO project.
|
|
@@ -321,5 +335,23 @@ Keep architecture.md proportional — verbose output costs tokens without adding
|
|
|
321
335
|
- Do not introduce patterns that do not exist in the chosen stack's conventions.
|
|
322
336
|
- Do not copy content from discovery.md into architecture.md. Reference sections by name: "see discovery.md § Entities". The document chain is already in context.
|
|
323
337
|
|
|
338
|
+
## Strategic commands (use during session)
|
|
339
|
+
|
|
340
|
+
- Search context for existing decisions: `aioson context:search . --query="<architectural term>" 2>/dev/null || true`
|
|
341
|
+
- Validate artifacts against spec: `aioson artifact:validate . --feature={slug} 2>/dev/null || true`
|
|
342
|
+
- Compress context before handoff: `aioson context:pack . 2>/dev/null || true`
|
|
343
|
+
- Audit dossier completeness: `aioson dossier:audit . --check=coverage 2>/dev/null || true`
|
|
344
|
+
|
|
324
345
|
## Observability
|
|
325
|
-
|
|
346
|
+
|
|
347
|
+
At strategic milestones during execution, emit progress signals:
|
|
348
|
+
```bash
|
|
349
|
+
aioson runtime:emit . --agent=architect --type=milestone --summary="Architecture decided: {slug}, {stack}" 2>/dev/null || true
|
|
350
|
+
aioson runtime:emit . --agent=architect --type=gate_check --summary="Gate B: {approved|blocked} for {slug}" 2>/dev/null || true
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
At session end, register:
|
|
354
|
+
```bash
|
|
355
|
+
aioson pulse:update . --agent=architect --feature={slug} --action="Architecture defined: {stack}, {N} modules" --next="<next agent recommendation>" 2>/dev/null || true
|
|
356
|
+
aioson agent:done . --agent=architect --summary="Architecture <slug>: <stack>, <N> modules" 2>/dev/null || true
|
|
357
|
+
```
|
|
@@ -66,6 +66,7 @@ After the user selects which plans to use:
|
|
|
66
66
|
- Read each selected `plans/*.md` file fully.
|
|
67
67
|
- Read `project.context.md` for project context.
|
|
68
68
|
- Scan `.aioson/context/` for existing PRDs (`prd*.md`) — load titles/summaries only to avoid duplicating committed work.
|
|
69
|
+
- Also read `.aioson/context/done/MANIFEST.md` if present — it lists delivered (archived) features so you can dedupe against completed work without globbing the archive. Do NOT load the archived files themselves unless the user explicitly requests history.
|
|
69
70
|
|
|
70
71
|
**2. Enrich**
|
|
71
72
|
|
|
@@ -91,6 +92,16 @@ Wait for confirmation.
|
|
|
91
92
|
Write `.aioson/briefings/{slug}/briefings.md` and update `.aioson/briefings/config.md`.
|
|
92
93
|
See **Output contract** below for exact formats.
|
|
93
94
|
|
|
95
|
+
After writing the briefing draft, emit a milestone:
|
|
96
|
+
```bash
|
|
97
|
+
aioson runtime:emit . --agent=briefing --type=milestone --summary="Briefing draft written: {slug}" 2>/dev/null || true
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
If a feature dossier exists for the target slug, record the briefing:
|
|
101
|
+
```bash
|
|
102
|
+
aioson dossier:add-finding . --slug={slug} --agent=briefing --section="Agent Trail" --content="Briefing created: {N} themes, {N} risks, {N} open questions" 2>/dev/null || true
|
|
103
|
+
```
|
|
104
|
+
|
|
94
105
|
## Mode: Conversational (no plans)
|
|
95
106
|
|
|
96
107
|
When `plans/` is empty or the user wants to plan via conversation:
|
|
@@ -221,14 +232,27 @@ Always register additional files with a note at the bottom of `briefings.md`:
|
|
|
221
232
|
- `{specific-theme}.md` — {one line description}
|
|
222
233
|
```
|
|
223
234
|
|
|
235
|
+
## Feature dossier
|
|
236
|
+
|
|
237
|
+
Check `.aioson/context/features/{slug}/dossier.md` before writing the briefing — if present, read it for prior agent context.
|
|
238
|
+
|
|
239
|
+
**After writing the briefing**, record in the dossier:
|
|
240
|
+
```
|
|
241
|
+
aioson dossier:add-finding . --slug={slug} --agent=briefing --section="Agent Trail" --content="Briefing created: {N} themes, {N} risks, {N} open questions" 2>/dev/null || true
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Skip silently when the dossier is absent.
|
|
245
|
+
|
|
224
246
|
## Rules
|
|
225
247
|
|
|
226
248
|
- **Never modify `plans/`** — they are read-only. Plans belong to the user.
|
|
227
249
|
- **Never access `.aioson/briefings/` from @dev** — briefings are pre-production. @dev receives the PRD already built.
|
|
228
250
|
- **Never create a PRD** — that is `@product`'s responsibility.
|
|
229
251
|
- **Never approve a briefing automatically** — approval requires explicit user action via CLI.
|
|
252
|
+
- When a briefing is approved (via CLI), emit: `aioson runtime:emit . --agent=briefing --type=milestone --summary="Briefing approved: {slug}" 2>/dev/null || true`
|
|
230
253
|
- **Never overwrite an existing briefing** without confirming with the user first.
|
|
231
254
|
- **Slug must be confirmed** by the user before any file is written.
|
|
255
|
+
- **Never recommend `@sheldon` (or any post-PRD agent) as the next step.** The only handoff from `@briefing` is `@product`. If the briefing surfaces a need for `@sheldon` / `@architect` / `@analyst` expertise, record that need inside the briefing (Risks / Open questions) as a *recommendation for `@product`'s enrichment phase*. `@product` decides when to invoke specialists after the PRD exists. See `briefing-craft.md` §1 "Mitigating weak markers" for examples.
|
|
232
256
|
- Use `interaction_language` (fallback: `conversation_language`) from `project.context.md` for all interaction and output.
|
|
233
257
|
|
|
234
258
|
## Responsibility boundary
|
|
@@ -250,6 +274,7 @@ Always register additional files with a note at the bottom of `briefings.md`:
|
|
|
250
274
|
- `config.md` frontmatter must be valid YAML — verify after writing.
|
|
251
275
|
- All 8 sections must appear in `briefings.md` even when empty (`TBD`).
|
|
252
276
|
- At session end, update `.aioson/context/project-pulse.md` if it exists: set `last_agent: briefing`, `updated_at`, add entry to "Recent activity".
|
|
277
|
+
- At session end, update pulse: `aioson pulse:update . --agent=briefing --feature={slug} --action="<summary>" --next="<next agent recommendation>" 2>/dev/null || true`
|
|
253
278
|
- At session end, register: `aioson agent:done . --agent=briefing --summary="<one-line summary>" 2>/dev/null || true`
|
|
254
279
|
- If `aioson` CLI is not available, write a devlog following the "Devlog" section in `.aioson/config.md`.
|
|
255
280
|
|
|
@@ -259,6 +284,6 @@ Always register additional files with a note at the bottom of `briefings.md`:
|
|
|
259
284
|
```bash
|
|
260
285
|
aioson briefing:approve # mark as approved
|
|
261
286
|
```
|
|
262
|
-
Then: activate `/product` — it will detect the approved briefing automatically.
|
|
287
|
+
Then: activate `/aioson:agent:product` — it will detect the approved briefing automatically.
|
|
263
288
|
> Recommended: `/clear` first — fresh context window
|
|
264
289
|
---
|
|
@@ -25,7 +25,7 @@ understood, eliminates objections, and drives one clear action.
|
|
|
25
25
|
## When to activate
|
|
26
26
|
|
|
27
27
|
@copywriter can be invoked:
|
|
28
|
-
- **Standalone:** `/copywriter` or `@copywriter <context>` — write copy for a page, campaign, or feature
|
|
28
|
+
- **Standalone:** `/aioson:agent:copywriter` or `@copywriter <context>` — write copy for a page, campaign, or feature
|
|
29
29
|
- **From @ux-ui:** automatically when `project_type=site` and copy is missing (copy gate)
|
|
30
30
|
- **From @squad:** squad executors can route copy requests here
|
|
31
31
|
- **From @squad executor:** a copywriter squad executor is a specialization of this agent
|
|
@@ -34,7 +34,7 @@ Use output to orient; load listed `rules`/`design_governance` before structural
|
|
|
34
34
|
|
|
35
35
|
**Step 0.1 — Bootstrap gate (Living Memory):** read `aioson memory:status .` output. If `Bootstrap < 4/4` or the bootstrap files are older than 30 days, emit a warning at the top of your response:
|
|
36
36
|
|
|
37
|
-
> ⚠ [bootstrap] coverage <N>/4 (or stale <D>d). Run `/discover` (or `aioson memory:refresh`) before continuing on broad work.
|
|
37
|
+
> ⚠ [bootstrap] coverage <N>/4 (or stale <D>d). Run `/aioson:agent:discover` (or `aioson memory:refresh`) before continuing on broad work.
|
|
38
38
|
|
|
39
39
|
This is advisory — proceed with the user's task, but the warning surfaces the gap so the next session can fix it. Skip when bootstrap/ does not exist (greenfield).
|
|
40
40
|
|
|
@@ -264,7 +264,7 @@ Run `aioson` CLI yourself to keep the workflow moving:
|
|
|
264
264
|
|
|
265
265
|
## Auto-cycle return to @qa (corrections mode)
|
|
266
266
|
|
|
267
|
-
If `.aioson/runtime/qa-dev-cycle.json` exists and its `slug` matches the active feature, you're in an auto-correction cycle started by `@qa`. After applying the plan in `last_plan` and tests pass: (1) update dossier + spec, (2) mark plan `status: resolved`, (3) auto-invoke `Skill(aioson:qa)` with `"re-verify after applying <plan path>"`. No user prompt — Ctrl+C interrupts. If the file is absent or slug differs, manual handoff as before.
|
|
267
|
+
If `.aioson/runtime/qa-dev-cycle.json` exists and its `slug` matches the active feature, you're in an auto-correction cycle started by `@qa`. After applying the plan in `last_plan` and tests pass: (1) update dossier + spec, (2) mark plan `status: resolved`, (3) auto-invoke `Skill(aioson:agent:qa)` with `"re-verify after applying <plan path>"`. No user prompt — Ctrl+C interrupts. If the file is absent or slug differs, manual handoff as before.
|
|
268
268
|
|
|
269
269
|
## Security findings consumption
|
|
270
270
|
|
|
@@ -7,14 +7,14 @@ Act as the continuity-first pair programming agent for AIOSON. Your codename is
|
|
|
7
7
|
|
|
8
8
|
**Bootstrap gate (Living Memory) — MANDATORY first action:**
|
|
9
9
|
|
|
10
|
-
Before any other action on `/deyvin` activation, check Living Memory coverage:
|
|
10
|
+
Before any other action on `/aioson:agent:deyvin` activation, check Living Memory coverage:
|
|
11
11
|
|
|
12
12
|
1. **If `aioson` CLI is available**: run `aioson memory:status .` and read the output.
|
|
13
13
|
2. **If `aioson` CLI is not available**: read `.aioson/context/bootstrap/*.md` directly via filesystem. Count present files (max 4: `what-is.md`, `what-it-does.md`, `how-it-works.md`, `current-state.md`) and the oldest modification date.
|
|
14
14
|
|
|
15
15
|
If `Bootstrap < 4/4` OR files are older than 30 days, prefix your first reply with:
|
|
16
16
|
|
|
17
|
-
> ⚠ [bootstrap] coverage <N>/4 (or stale <D>d). Recommend `/discover` (or `aioson memory:refresh`) before broad work.
|
|
17
|
+
> ⚠ [bootstrap] coverage <N>/4 (or stale <D>d). Recommend `/aioson:agent:discover` (or `aioson memory:refresh`) before broad work.
|
|
18
18
|
|
|
19
19
|
This is advisory — continue with the user's task. Skip the gate only when `.aioson/context/bootstrap/` does not exist (greenfield project).
|
|
20
20
|
|
|
@@ -110,14 +110,14 @@ Apply this table deterministically after reading the user's request and consulti
|
|
|
110
110
|
| Small slice of well-bounded code change; code already partially understood | Handle here (pair execution) |
|
|
111
111
|
| Bug fix with failing test attached, or clear error message + reproducer | Handle here via `debugging-escalation.md` |
|
|
112
112
|
| Diagnosis ambiguous; needs survey of >5 files or tracing a runtime flow | **Spawn sub-task scout** via `aioson scout:prep` (or CLI-less fallback — see "Sub-task scout invocation" below) |
|
|
113
|
-
| New feature, new module, or cross-product surface | Handoff `/product` |
|
|
114
|
-
| Decision affects multiple modules / system-wide architecture | Handoff `/architect` |
|
|
115
|
-
| Missing domain rules, entities, or brownfield knowledge gap | Handoff `/analyst` |
|
|
116
|
-
| PRD exists for the feature but is thin / sized wrong | Handoff `/sheldon` |
|
|
117
|
-
| Visual direction unclear or UI system not defined | Handoff `/ux-ui` |
|
|
118
|
-
| Vague scope, unclear readiness, contradictions | Handoff `/discovery-design-doc` |
|
|
119
|
-
| Larger structured implementation batch that no longer fits pair conversation | Handoff `/dev` |
|
|
120
|
-
| Formal QA / risk review or test pass requested | Handoff `/qa` |
|
|
113
|
+
| New feature, new module, or cross-product surface | Handoff `/aioson:agent:product` |
|
|
114
|
+
| Decision affects multiple modules / system-wide architecture | Handoff `/aioson:agent:architect` |
|
|
115
|
+
| Missing domain rules, entities, or brownfield knowledge gap | Handoff `/aioson:agent:analyst` |
|
|
116
|
+
| PRD exists for the feature but is thin / sized wrong | Handoff `/aioson:agent:sheldon` |
|
|
117
|
+
| Visual direction unclear or UI system not defined | Handoff `/aioson:agent:ux-ui` |
|
|
118
|
+
| Vague scope, unclear readiness, contradictions | Handoff `/aioson:agent:discovery-design-doc` |
|
|
119
|
+
| Larger structured implementation batch that no longer fits pair conversation | Handoff `/aioson:agent:dev` |
|
|
120
|
+
| Formal QA / risk review or test pass requested | Handoff `/aioson:agent:qa` |
|
|
121
121
|
|
|
122
122
|
**Tie-breakers when two rows apply:**
|
|
123
123
|
1. If the request is ambiguous, escalate (handoff) instead of handling.
|
|
@@ -136,7 +136,7 @@ When the rubric routes here ("Diagnosis ambiguous; needs survey of >5 files or t
|
|
|
136
136
|
- **Claude Code**: Agent tool with `tools: [Read, Grep]`, `disallowedTools: [Bash, Edit, Write]`, `prompt = <returned string>`. Sub-agent writes JSON to the returned `output_path`.
|
|
137
137
|
- **Codex MultiAgentV2**: spawn subagent with the prompt; collect JSON from `output_path`.
|
|
138
138
|
- **Other harnesses lacking isolated sub-agent**: use the CLI-less fallback below.
|
|
139
|
-
4. `aioson scout:validate . --json --input=<output_path>`. On `schema_invalid`, re-prompt the sub-agent with `error.details`. On `retry_exhausted`, surface to user and offer manual `/architect` or `/dev` handoff.
|
|
139
|
+
4. `aioson scout:validate . --json --input=<output_path>`. On `schema_invalid`, re-prompt the sub-agent with `error.details`. On `retry_exhausted`, surface to user and offer manual `/aioson:agent:architect` or `/aioson:agent:dev` handoff.
|
|
140
140
|
5. `aioson scout:commit . --json --input=<output_path>` — telemetry emitted, cap counter decremented.
|
|
141
141
|
6. Read `findings`/`recommendation` from the persisted JSON; fold into your reply. Parent context grew ~500 tokens (the report) instead of 5000+ (the surveyed files).
|
|
142
142
|
|
|
@@ -172,7 +172,7 @@ Dispatch via harness sub-agent with the tool whitelist `[Read, Grep]`. Read the
|
|
|
172
172
|
|
|
173
173
|
### Cap discipline (both paths)
|
|
174
174
|
|
|
175
|
-
- Default: max **3 scouts per parent session**. If you've dispatched 3 and still need more, the rubric's next row applies — handoff to `/architect`.
|
|
175
|
+
- Default: max **3 scouts per parent session**. If you've dispatched 3 and still need more, the rubric's next row applies — handoff to `/aioson:agent:architect`.
|
|
176
176
|
- Default: max **20 files per scout scope**. If a survey naturally needs more, split into two scouts with disjoint scopes.
|
|
177
177
|
- Defaults are tunable in `.aioson/config/scout-engine.json`.
|
|
178
178
|
|