@jaimevalasek/aioson 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -2
- package/docs/pt/README.md +62 -2
- package/docs/pt/advisor-spec.md +5 -5
- package/docs/pt/agentes-customizados.md +670 -0
- package/docs/pt/agentes.md +111 -13
- package/docs/pt/automacao-squads.md +407 -0
- package/docs/pt/cenarios.md +3 -3
- package/docs/pt/clientes-ai.md +62 -0
- package/docs/pt/comandos-cli.md +167 -17
- package/docs/pt/deyvin.md +115 -0
- package/docs/pt/genome-3.0-spec.md +11 -11
- package/docs/pt/inicio-rapido.md +45 -0
- package/docs/pt/memoria-contexto.md +255 -0
- package/docs/pt/output-strategy-delivery.md +655 -0
- package/docs/pt/profiler-system.md +17 -17
- package/docs/pt/runtime-observability.md +5 -1
- package/docs/pt/skills.md +175 -0
- package/docs/pt/{squad-genoma.md → squad-genome.md} +81 -75
- package/docs/testing/genome-2.0-rollout.md +1 -1
- package/package.json +3 -3
- package/src/agents.js +21 -5
- package/src/backup-provider.js +303 -0
- package/src/cli.js +178 -2
- package/src/commands/agents.js +22 -4
- package/src/commands/backup.js +533 -0
- package/src/commands/cloud.js +17 -17
- package/src/commands/context-pack.js +45 -0
- package/src/commands/implementation-plan.js +340 -0
- package/src/commands/learning.js +134 -0
- package/src/commands/live.js +1583 -0
- package/src/commands/runtime.js +833 -2
- package/src/commands/scan-project.js +288 -24
- package/src/commands/setup-context.js +23 -0
- package/src/commands/skill.js +558 -0
- package/src/commands/squad-agent-create.js +788 -0
- package/src/commands/squad-doctor.js +51 -1
- package/src/commands/squad-investigate.js +261 -0
- package/src/commands/squad-learning.js +209 -0
- package/src/commands/squad-pipeline.js +247 -1
- package/src/commands/squad-plan.js +329 -0
- package/src/commands/squad-status.js +1 -1
- package/src/commands/squad-validate.js +57 -1
- package/src/commands/test-agents.js +6 -1
- package/src/commands/workflow-next.js +8 -1
- package/src/commands/workflow-status.js +250 -0
- package/src/constants.js +80 -16
- package/src/context-memory.js +837 -0
- package/src/context-writer.js +2 -0
- package/src/delivery-runner.js +319 -0
- package/src/genome-files.js +1 -1
- package/src/genome-format.js +1 -1
- package/src/i18n/messages/en.js +206 -7
- package/src/i18n/messages/es.js +123 -6
- package/src/i18n/messages/fr.js +122 -5
- package/src/i18n/messages/pt-BR.js +205 -12
- package/src/installer.js +30 -2
- package/src/lib/genomes/compat.js +1 -1
- package/src/runtime-store.js +780 -42
- package/src/session-handoff.js +77 -0
- package/template/.aioson/agents/analyst.md +36 -9
- package/template/.aioson/agents/architect.md +20 -5
- package/template/.aioson/agents/dev.md +135 -15
- package/template/.aioson/agents/deyvin.md +166 -0
- package/template/.aioson/agents/discovery-design-doc.md +25 -1
- package/template/.aioson/agents/{genoma.md → genome.md} +20 -20
- package/template/.aioson/agents/orache.md +371 -0
- package/template/.aioson/agents/orchestrator.md +37 -2
- package/template/.aioson/agents/pair.md +5 -0
- package/template/.aioson/agents/pm.md +17 -5
- package/template/.aioson/agents/product.md +58 -22
- package/template/.aioson/agents/profiler-enricher.md +1 -1
- package/template/.aioson/agents/profiler-forge.md +9 -9
- package/template/.aioson/agents/profiler-researcher.md +1 -1
- package/template/.aioson/agents/qa.md +17 -5
- package/template/.aioson/agents/setup.md +81 -5
- package/template/.aioson/agents/squad.md +675 -28
- package/template/.aioson/agents/ux-ui.md +277 -34
- package/template/.aioson/config.md +175 -0
- package/template/.aioson/context/spec.md.template +17 -0
- package/template/.aioson/genomes/.gitkeep +0 -0
- package/template/.aioson/installed-skills/.gitkeep +0 -0
- package/template/.aioson/locales/en/agents/analyst.md +26 -4
- package/template/.aioson/locales/en/agents/architect.md +10 -0
- package/template/.aioson/locales/en/agents/dev.md +89 -4
- package/template/.aioson/locales/en/agents/deyvin.md +129 -0
- package/template/.aioson/locales/en/agents/{genoma.md → genome.md} +14 -14
- package/template/.aioson/locales/en/agents/orchestrator.md +36 -2
- package/template/.aioson/locales/en/agents/pair.md +5 -0
- package/template/.aioson/locales/en/agents/pm.md +7 -0
- package/template/.aioson/locales/en/agents/product.md +35 -17
- package/template/.aioson/locales/en/agents/qa.md +7 -0
- package/template/.aioson/locales/en/agents/setup.md +51 -5
- package/template/.aioson/locales/en/agents/squad.md +203 -15
- package/template/.aioson/locales/en/agents/ux-ui.md +375 -35
- package/template/.aioson/locales/es/agents/analyst.md +16 -4
- package/template/.aioson/locales/es/agents/architect.md +10 -0
- package/template/.aioson/locales/es/agents/dev.md +70 -2
- package/template/.aioson/locales/es/agents/deyvin.md +89 -0
- package/template/.aioson/locales/es/agents/{genoma.md → genome.md} +13 -13
- package/template/.aioson/locales/es/agents/orache.md +103 -0
- package/template/.aioson/locales/es/agents/orchestrator.md +36 -2
- package/template/.aioson/locales/es/agents/pair.md +5 -0
- package/template/.aioson/locales/es/agents/pm.md +7 -0
- package/template/.aioson/locales/es/agents/product.md +13 -3
- package/template/.aioson/locales/es/agents/qa.md +7 -0
- package/template/.aioson/locales/es/agents/setup.md +28 -5
- package/template/.aioson/locales/es/agents/squad.md +221 -15
- package/template/.aioson/locales/es/agents/ux-ui.md +26 -25
- package/template/.aioson/locales/fr/agents/analyst.md +16 -4
- package/template/.aioson/locales/fr/agents/architect.md +10 -0
- package/template/.aioson/locales/fr/agents/dev.md +70 -2
- package/template/.aioson/locales/fr/agents/deyvin.md +89 -0
- package/template/.aioson/locales/fr/agents/{genoma.md → genome.md} +7 -7
- package/template/.aioson/locales/fr/agents/orache.md +104 -0
- package/template/.aioson/locales/fr/agents/orchestrator.md +36 -2
- package/template/.aioson/locales/fr/agents/pair.md +5 -0
- package/template/.aioson/locales/fr/agents/pm.md +7 -0
- package/template/.aioson/locales/fr/agents/product.md +13 -3
- package/template/.aioson/locales/fr/agents/qa.md +7 -0
- package/template/.aioson/locales/fr/agents/setup.md +28 -5
- package/template/.aioson/locales/fr/agents/squad.md +216 -10
- package/template/.aioson/locales/fr/agents/ux-ui.md +26 -25
- package/template/.aioson/locales/pt-BR/agents/analyst.md +26 -4
- package/template/.aioson/locales/pt-BR/agents/architect.md +10 -0
- package/template/.aioson/locales/pt-BR/agents/dev.md +93 -4
- package/template/.aioson/locales/pt-BR/agents/deyvin.md +129 -0
- package/template/.aioson/locales/pt-BR/agents/{genoma.md → genome.md} +49 -49
- package/template/.aioson/locales/pt-BR/agents/orache.md +137 -0
- package/template/.aioson/locales/pt-BR/agents/orchestrator.md +36 -2
- package/template/.aioson/locales/pt-BR/agents/pair.md +5 -0
- package/template/.aioson/locales/pt-BR/agents/pm.md +7 -0
- package/template/.aioson/locales/pt-BR/agents/product.md +35 -17
- package/template/.aioson/locales/pt-BR/agents/qa.md +7 -0
- package/template/.aioson/locales/pt-BR/agents/setup.md +51 -5
- package/template/.aioson/locales/pt-BR/agents/squad.md +486 -47
- package/template/.aioson/locales/pt-BR/agents/ux-ui.md +361 -22
- package/template/.aioson/my-agents/.gitkeep +0 -0
- package/template/.aioson/rules/.gitkeep +0 -0
- package/template/.aioson/rules/squad/.gitkeep +0 -0
- package/template/.aioson/rules/squad/README.md +50 -0
- package/template/.aioson/schemas/genome-meta.schema.json +1 -1
- package/template/.aioson/schemas/genome.schema.json +1 -1
- package/template/.aioson/schemas/squad-blueprint.schema.json +11 -0
- package/template/.aioson/schemas/squad-manifest.schema.json +257 -1
- package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +157 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/components.md +407 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/dashboards.md +172 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +490 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +237 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +289 -0
- package/template/.aioson/skills/design/cognitive-core-ui/references/websites.md +350 -0
- package/template/.aioson/skills/design/interface-design/SKILL.md +47 -0
- package/template/.aioson/skills/design/interface-design/references/components-and-states.md +105 -0
- package/template/.aioson/skills/design/interface-design/references/design-directions.md +101 -0
- package/template/.aioson/skills/design/interface-design/references/handoff-and-quality.md +71 -0
- package/template/.aioson/skills/design/interface-design/references/intent-and-domain.md +74 -0
- package/template/.aioson/skills/design/interface-design/references/tokens-and-depth.md +173 -0
- package/template/.aioson/skills/design/premium-command-center-ui/SKILL.md +62 -0
- package/template/.aioson/skills/design/premium-command-center-ui/references/operations.md +74 -0
- package/template/.aioson/skills/design/premium-command-center-ui/references/patterns.md +116 -0
- package/template/.aioson/skills/design/premium-command-center-ui/references/validation.md +47 -0
- package/template/.aioson/skills/design/premium-command-center-ui/references/visual-system.md +215 -0
- package/template/.aioson/skills/design-system/SKILL.md +92 -0
- package/template/.aioson/skills/design-system/cognitive-core-ui.skill +0 -0
- package/template/.aioson/skills/design-system/components/SKILL.md +274 -0
- package/template/.aioson/skills/design-system/components/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/dashboards/SKILL.md +184 -0
- package/template/.aioson/skills/design-system/dashboards/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/foundations/SKILL.md +250 -0
- package/template/.aioson/skills/design-system/foundations/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/motion/SKILL.md +197 -0
- package/template/.aioson/skills/design-system/motion/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/design-system/patterns/SKILL.md +231 -0
- package/template/.aioson/skills/design-system/patterns/SKILL.md:Zone.Identifier +0 -0
- package/template/.aioson/skills/squad/SKILL.md +58 -0
- package/template/.aioson/skills/squad/domains/.gitkeep +0 -0
- package/template/.aioson/skills/squad/formats/.gitkeep +0 -0
- package/template/.aioson/skills/squad/patterns/.gitkeep +0 -0
- package/template/.aioson/skills/squad/references/.gitkeep +0 -0
- package/template/.aioson/tasks/implementation-plan.md +288 -0
- package/template/.aioson/tasks/squad-create.md +1 -1
- package/template/.aioson/tasks/squad-execution-plan.md +279 -0
- package/template/.aioson/tasks/squad-export.md +1 -1
- package/template/.aioson/tasks/squad-investigate.md +44 -0
- package/template/.aioson/tasks/squad-learning-review.md +44 -0
- package/template/.aioson/tasks/squad-output-config.md +177 -0
- package/template/.aioson/tasks/squad-validate.md +1 -1
- package/template/.claude/commands/aioson/agent/deyvin.md +5 -0
- package/template/.claude/commands/aioson/agent/discovery-design-doc.md +5 -0
- package/template/.claude/commands/aioson/agent/genome.md +5 -0
- package/template/.claude/commands/aioson/agent/product.md +5 -0
- package/template/.claude/commands/aioson/agent/profiler-enricher.md +5 -0
- package/template/.claude/commands/aioson/agent/profiler-forge.md +5 -0
- package/template/.claude/commands/aioson/agent/profiler-researcher.md +5 -0
- package/template/.claude/commands/aioson/agent/squad.md +5 -0
- package/template/.gemini/GEMINI.md +2 -0
- package/template/.gemini/commands/aios-deyvin.toml +6 -0
- package/template/.gemini/commands/aios-pair.toml +6 -0
- package/template/AGENTS.md +34 -6
- package/template/CLAUDE.md +31 -4
- package/template/OPENCODE.md +6 -2
- package/template/squad-searches/.gitkeep +0 -0
- package/template/.aioson/skills/static/interface-design.md +0 -372
- package/template/.aioson/skills/static/premium-command-center-ui.md +0 -190
- /package/template/.aioson/{genomas → docs}/.gitkeep +0 -0
- /package/template/.claude/commands/aioson/{analyst.md → agent/analyst.md} +0 -0
- /package/template/.claude/commands/aioson/{architect.md → agent/architect.md} +0 -0
- /package/template/.claude/commands/aioson/{dev.md → agent/dev.md} +0 -0
- /package/template/.claude/commands/aioson/{orchestrator.md → agent/orchestrator.md} +0 -0
- /package/template/.claude/commands/aioson/{pm.md → agent/pm.md} +0 -0
- /package/template/.claude/commands/aioson/{qa.md → agent/qa.md} +0 -0
- /package/template/.claude/commands/aioson/{setup.md → agent/setup.md} +0 -0
- /package/template/.claude/commands/aioson/{ux-ui.md → agent/ux-ui.md} +0 -0
|
@@ -120,7 +120,7 @@ async function parseSquadPaths(targetDir, slug) {
|
|
|
120
120
|
? path.join(targetDir, normalizeRel(packageInfo.agentsDir || `.aioson/squads/${slug}/agents`))
|
|
121
121
|
: path.join(targetDir, 'agents', slug),
|
|
122
122
|
outputDir: path.join(targetDir, normalizeRel(rules.outputsDir || `output/${slug}`)),
|
|
123
|
-
logsDir: path.join(targetDir, normalizeRel(rules.logsDir || `
|
|
123
|
+
logsDir: path.join(targetDir, normalizeRel(rules.logsDir || `aioson-logs/${slug}`)),
|
|
124
124
|
mediaDir: path.join(targetDir, normalizeRel(rules.mediaDir || `media/${slug}`))
|
|
125
125
|
};
|
|
126
126
|
}
|
|
@@ -209,6 +209,56 @@ async function runSquadDoctor({ args, options = {}, logger, t }) {
|
|
|
209
209
|
checks.push(makeCheck('output_dir', outputExists, outputExists ? 'info' : 'warn', t('squad_doctor.check_output_dir', { path: paths.outputDir })));
|
|
210
210
|
checks.push(makeCheck('media_dir', mediaExists, mediaExists ? 'info' : 'warn', t('squad_doctor.check_media_dir', { path: paths.mediaDir })));
|
|
211
211
|
|
|
212
|
+
// Output strategy checks
|
|
213
|
+
const outputStrategy = manifest.outputStrategy && typeof manifest.outputStrategy === 'object'
|
|
214
|
+
? manifest.outputStrategy
|
|
215
|
+
: null;
|
|
216
|
+
|
|
217
|
+
if (outputStrategy) {
|
|
218
|
+
const delivery = outputStrategy.delivery && typeof outputStrategy.delivery === 'object'
|
|
219
|
+
? outputStrategy.delivery
|
|
220
|
+
: {};
|
|
221
|
+
const webhooks = Array.isArray(delivery.webhooks) ? delivery.webhooks : [];
|
|
222
|
+
const hasDelivery = webhooks.length > 0 || delivery.cloudPublish;
|
|
223
|
+
|
|
224
|
+
checks.push(makeCheck(
|
|
225
|
+
'output_strategy',
|
|
226
|
+
true,
|
|
227
|
+
'info',
|
|
228
|
+
`Output strategy: mode=${outputStrategy.mode || 'unknown'}, webhooks=${webhooks.length}, cloudPublish=${Boolean(delivery.cloudPublish)}, autoPublish=${Boolean(delivery.autoPublish)}`
|
|
229
|
+
));
|
|
230
|
+
|
|
231
|
+
if (delivery.autoPublish && !hasDelivery) {
|
|
232
|
+
checks.push(makeCheck(
|
|
233
|
+
'output_auto_publish',
|
|
234
|
+
false,
|
|
235
|
+
'warn',
|
|
236
|
+
'autoPublish is enabled but no delivery targets configured (no webhooks, no cloudPublish)'
|
|
237
|
+
));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
for (const wh of webhooks) {
|
|
241
|
+
if (wh.url && wh.url.includes('{{ENV:')) {
|
|
242
|
+
const envMatch = wh.url.match(/\{\{ENV:(\w+)\}\}/);
|
|
243
|
+
if (envMatch && !process.env[envMatch[1]]) {
|
|
244
|
+
checks.push(makeCheck(
|
|
245
|
+
`webhook_env_${wh.slug}`,
|
|
246
|
+
false,
|
|
247
|
+
'warn',
|
|
248
|
+
`Webhook "${wh.slug}" references unset env var: ${envMatch[1]}`
|
|
249
|
+
));
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
checks.push(makeCheck(
|
|
255
|
+
'output_strategy',
|
|
256
|
+
true,
|
|
257
|
+
'info',
|
|
258
|
+
'No output strategy configured (default file output will be used)'
|
|
259
|
+
));
|
|
260
|
+
}
|
|
261
|
+
|
|
212
262
|
const runtimeHandle = await openRuntimeDb(targetDir, { mustExist: true });
|
|
213
263
|
if (!runtimeHandle) {
|
|
214
264
|
checks.push(makeCheck('runtime_store', false, 'warn', t('squad_doctor.check_runtime_missing')));
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises');
|
|
4
|
+
const path = require('node:path');
|
|
5
|
+
const {
|
|
6
|
+
openRuntimeDb,
|
|
7
|
+
insertInvestigation,
|
|
8
|
+
listInvestigations,
|
|
9
|
+
getInvestigation,
|
|
10
|
+
linkInvestigation
|
|
11
|
+
} = require('../runtime-store');
|
|
12
|
+
|
|
13
|
+
const SEARCHES_DIR = 'squad-searches';
|
|
14
|
+
const DIMENSION_HEADERS = ['D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7'];
|
|
15
|
+
|
|
16
|
+
async function pathExists(targetPath) {
|
|
17
|
+
try {
|
|
18
|
+
await fs.access(targetPath);
|
|
19
|
+
return true;
|
|
20
|
+
} catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Count how many of the 7 investigation dimensions are present in a report.
|
|
27
|
+
* Looks for headers like "## D1:", "## D2:", ..., "## D7:" in the markdown.
|
|
28
|
+
*/
|
|
29
|
+
function countDimensions(content) {
|
|
30
|
+
const text = String(content || '');
|
|
31
|
+
let count = 0;
|
|
32
|
+
for (const dim of DIMENSION_HEADERS) {
|
|
33
|
+
const pattern = new RegExp(`^##\\s+${dim}[:\\s]`, 'm');
|
|
34
|
+
if (pattern.test(text)) count++;
|
|
35
|
+
}
|
|
36
|
+
return count;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Calculate an investigation completeness score (0-1).
|
|
41
|
+
*/
|
|
42
|
+
function scoreCompleteness(content) {
|
|
43
|
+
const covered = countDimensions(content);
|
|
44
|
+
const total = DIMENSION_HEADERS.length;
|
|
45
|
+
return { covered, total, score: Math.round((covered / total) * 100) / 100 };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Subcommand: list
|
|
50
|
+
* Lists all investigations registered in the runtime SQLite.
|
|
51
|
+
*/
|
|
52
|
+
async function handleList(projectDir, { logger, t }) {
|
|
53
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
54
|
+
if (!handle) {
|
|
55
|
+
logger.error(t('squad_investigate.no_runtime'));
|
|
56
|
+
return { investigations: [], count: 0 };
|
|
57
|
+
}
|
|
58
|
+
const { db } = handle;
|
|
59
|
+
try {
|
|
60
|
+
const rows = listInvestigations(db);
|
|
61
|
+
if (rows.length === 0) {
|
|
62
|
+
logger.log(t('squad_investigate.no_investigations'));
|
|
63
|
+
return { investigations: [], count: 0 };
|
|
64
|
+
}
|
|
65
|
+
for (const row of rows) {
|
|
66
|
+
const linked = row.linked_squad_slug ? ` → ${row.linked_squad_slug}` : '';
|
|
67
|
+
const dims = `${row.dimensions_covered}/${row.total_dimensions}`;
|
|
68
|
+
logger.log(` ${row.investigation_slug} [${row.mode}] ${row.domain} ${dims} conf=${row.confidence}${linked}`);
|
|
69
|
+
}
|
|
70
|
+
return { investigations: rows, count: rows.length };
|
|
71
|
+
} finally {
|
|
72
|
+
db.close();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Subcommand: show <slug>
|
|
78
|
+
* Shows the investigation report content.
|
|
79
|
+
*/
|
|
80
|
+
async function handleShow(projectDir, slug, { logger, t }) {
|
|
81
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
82
|
+
if (!handle) {
|
|
83
|
+
logger.error(t('squad_investigate.no_runtime'));
|
|
84
|
+
return { found: false };
|
|
85
|
+
}
|
|
86
|
+
const { db } = handle;
|
|
87
|
+
try {
|
|
88
|
+
const row = getInvestigation(db, slug);
|
|
89
|
+
if (!row) {
|
|
90
|
+
logger.error(t('squad_investigate.not_found', { slug }));
|
|
91
|
+
return { found: false };
|
|
92
|
+
}
|
|
93
|
+
logger.log(`Investigation: ${row.investigation_slug}`);
|
|
94
|
+
logger.log(`Domain: ${row.domain}`);
|
|
95
|
+
logger.log(`Mode: ${row.mode}`);
|
|
96
|
+
logger.log(`Dimensions: ${row.dimensions_covered}/${row.total_dimensions}`);
|
|
97
|
+
logger.log(`Confidence: ${row.confidence}`);
|
|
98
|
+
logger.log(`Report: ${row.report_path || '(none)'}`);
|
|
99
|
+
logger.log(`Linked squad: ${row.linked_squad_slug || '(standalone)'}`);
|
|
100
|
+
logger.log(`Created: ${row.created_at}`);
|
|
101
|
+
|
|
102
|
+
if (row.report_path) {
|
|
103
|
+
const reportFile = path.resolve(projectDir, row.report_path);
|
|
104
|
+
if (await pathExists(reportFile)) {
|
|
105
|
+
const content = await fs.readFile(reportFile, 'utf8');
|
|
106
|
+
logger.log('');
|
|
107
|
+
logger.log(content);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { found: true, investigation: row };
|
|
111
|
+
} finally {
|
|
112
|
+
db.close();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Subcommand: score <slug>
|
|
118
|
+
* Calculates the completeness score for an investigation report.
|
|
119
|
+
*/
|
|
120
|
+
async function handleScore(projectDir, slug, { logger, t }) {
|
|
121
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
122
|
+
if (!handle) {
|
|
123
|
+
logger.error(t('squad_investigate.no_runtime'));
|
|
124
|
+
return { found: false, score: 0 };
|
|
125
|
+
}
|
|
126
|
+
const { db } = handle;
|
|
127
|
+
try {
|
|
128
|
+
const row = getInvestigation(db, slug);
|
|
129
|
+
if (!row) {
|
|
130
|
+
logger.error(t('squad_investigate.not_found', { slug }));
|
|
131
|
+
return { found: false, score: 0 };
|
|
132
|
+
}
|
|
133
|
+
if (!row.report_path) {
|
|
134
|
+
logger.error(t('squad_investigate.no_report', { slug }));
|
|
135
|
+
return { found: true, score: 0 };
|
|
136
|
+
}
|
|
137
|
+
const reportFile = path.resolve(projectDir, row.report_path);
|
|
138
|
+
if (!(await pathExists(reportFile))) {
|
|
139
|
+
logger.error(t('squad_investigate.report_missing', { path: row.report_path }));
|
|
140
|
+
return { found: true, score: 0 };
|
|
141
|
+
}
|
|
142
|
+
const content = await fs.readFile(reportFile, 'utf8');
|
|
143
|
+
const result = scoreCompleteness(content);
|
|
144
|
+
logger.log(`Completeness: ${result.covered}/${result.total} dimensions (${result.score})`);
|
|
145
|
+
return { found: true, ...result };
|
|
146
|
+
} finally {
|
|
147
|
+
db.close();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Subcommand: link <investigation-slug> <squad-slug>
|
|
153
|
+
* Associates an investigation with a squad.
|
|
154
|
+
*/
|
|
155
|
+
async function handleLink(projectDir, invSlug, squadSlug, { logger, t }) {
|
|
156
|
+
if (!invSlug || !squadSlug) {
|
|
157
|
+
logger.error(t('squad_investigate.link_usage'));
|
|
158
|
+
return { linked: false };
|
|
159
|
+
}
|
|
160
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
161
|
+
if (!handle) {
|
|
162
|
+
logger.error(t('squad_investigate.no_runtime'));
|
|
163
|
+
return { linked: false };
|
|
164
|
+
}
|
|
165
|
+
const { db } = handle;
|
|
166
|
+
try {
|
|
167
|
+
const success = linkInvestigation(db, invSlug, squadSlug);
|
|
168
|
+
if (success) {
|
|
169
|
+
logger.log(t('squad_investigate.linked', { investigation: invSlug, squad: squadSlug }));
|
|
170
|
+
} else {
|
|
171
|
+
logger.error(t('squad_investigate.not_found', { slug: invSlug }));
|
|
172
|
+
}
|
|
173
|
+
return { linked: success };
|
|
174
|
+
} finally {
|
|
175
|
+
db.close();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Subcommand: register
|
|
181
|
+
* Registers an existing investigation report file into the runtime SQLite.
|
|
182
|
+
*/
|
|
183
|
+
async function handleRegister(projectDir, reportPath, options, { logger, t }) {
|
|
184
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
185
|
+
if (!handle) {
|
|
186
|
+
logger.error(t('squad_investigate.no_runtime'));
|
|
187
|
+
return { registered: false };
|
|
188
|
+
}
|
|
189
|
+
const { db } = handle;
|
|
190
|
+
try {
|
|
191
|
+
const absPath = path.resolve(projectDir, reportPath);
|
|
192
|
+
if (!(await pathExists(absPath))) {
|
|
193
|
+
logger.error(t('squad_investigate.report_missing', { path: reportPath }));
|
|
194
|
+
return { registered: false };
|
|
195
|
+
}
|
|
196
|
+
const content = await fs.readFile(absPath, 'utf8');
|
|
197
|
+
const { covered, total, score } = scoreCompleteness(content);
|
|
198
|
+
const relPath = path.relative(projectDir, absPath);
|
|
199
|
+
const slug = insertInvestigation(db, {
|
|
200
|
+
investigationSlug: options.slug || undefined,
|
|
201
|
+
domain: options.domain || path.basename(reportPath, '.md'),
|
|
202
|
+
mode: options.mode || 'full',
|
|
203
|
+
dimensionsCovered: covered,
|
|
204
|
+
totalDimensions: total,
|
|
205
|
+
confidence: options.confidence ? Number(options.confidence) : score,
|
|
206
|
+
reportPath: relPath,
|
|
207
|
+
linkedSquadSlug: options.squad || null
|
|
208
|
+
});
|
|
209
|
+
logger.log(t('squad_investigate.registered', { slug, path: relPath }));
|
|
210
|
+
return { registered: true, slug };
|
|
211
|
+
} finally {
|
|
212
|
+
db.close();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function runSquadInvestigate({ args = [], options = {}, logger = console, t = (k) => k } = {}) {
|
|
217
|
+
const projectDir = path.resolve(process.cwd(), args[0] || '.');
|
|
218
|
+
const sub = options.sub || args[1] || 'list';
|
|
219
|
+
|
|
220
|
+
if (sub === 'list') {
|
|
221
|
+
return handleList(projectDir, { logger, t });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (sub === 'show') {
|
|
225
|
+
const slug = options.investigation || args[2];
|
|
226
|
+
if (!slug) {
|
|
227
|
+
logger.error(t('squad_investigate.show_usage'));
|
|
228
|
+
return { found: false };
|
|
229
|
+
}
|
|
230
|
+
return handleShow(projectDir, slug, { logger, t });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (sub === 'score') {
|
|
234
|
+
const slug = options.investigation || args[2];
|
|
235
|
+
if (!slug) {
|
|
236
|
+
logger.error(t('squad_investigate.score_usage'));
|
|
237
|
+
return { found: false, score: 0 };
|
|
238
|
+
}
|
|
239
|
+
return handleScore(projectDir, slug, { logger, t });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (sub === 'link') {
|
|
243
|
+
const invSlug = options.investigation || args[2];
|
|
244
|
+
const squadSlug = options.squad || args[3];
|
|
245
|
+
return handleLink(projectDir, invSlug, squadSlug, { logger, t });
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (sub === 'register') {
|
|
249
|
+
const reportPath = options.report || args[2];
|
|
250
|
+
if (!reportPath) {
|
|
251
|
+
logger.error(t('squad_investigate.register_usage'));
|
|
252
|
+
return { registered: false };
|
|
253
|
+
}
|
|
254
|
+
return handleRegister(projectDir, reportPath, options, { logger, t });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
logger.error(t('squad_investigate.unknown_sub', { sub }));
|
|
258
|
+
return { error: `Unknown subcommand: ${sub}` };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
module.exports = { runSquadInvestigate, scoreCompleteness, countDimensions };
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const {
|
|
5
|
+
openRuntimeDb,
|
|
6
|
+
listSquadLearnings,
|
|
7
|
+
getSquadLearning,
|
|
8
|
+
updateSquadLearningStatus,
|
|
9
|
+
promoteSquadLearning,
|
|
10
|
+
archiveStaleSquadLearnings,
|
|
11
|
+
getSquadLearningStats
|
|
12
|
+
} = require('../runtime-store');
|
|
13
|
+
|
|
14
|
+
const SQUADS_DIR = path.join('.aioson', 'squads');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Subcommand: list <slug> [--status=active|stale|archived|promoted]
|
|
18
|
+
* Lists learnings for a squad.
|
|
19
|
+
*/
|
|
20
|
+
async function handleList(projectDir, squadSlug, statusFilter, { logger, t }) {
|
|
21
|
+
if (!squadSlug) {
|
|
22
|
+
logger.error(t('squad_learning.slug_required'));
|
|
23
|
+
return { found: false };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
27
|
+
if (!handle) {
|
|
28
|
+
logger.error(t('squad_learning.no_runtime'));
|
|
29
|
+
return { found: false };
|
|
30
|
+
}
|
|
31
|
+
const { db } = handle;
|
|
32
|
+
try {
|
|
33
|
+
const rows = listSquadLearnings(db, squadSlug, statusFilter || null);
|
|
34
|
+
if (rows.length === 0) {
|
|
35
|
+
logger.log(t('squad_learning.no_learnings', { slug: squadSlug }));
|
|
36
|
+
return { found: true, learnings: [] };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
logger.log(`Learnings for squad: ${squadSlug} (${rows.length})`);
|
|
40
|
+
logger.log('');
|
|
41
|
+
for (const row of rows) {
|
|
42
|
+
const icon = row.status === 'active' ? '●' : row.status === 'promoted' ? '★' : row.status === 'stale' ? '○' : '▪';
|
|
43
|
+
logger.log(` ${icon} [${row.type}] ${row.title} (freq: ${row.frequency}, ${row.confidence}) [${row.status}]`);
|
|
44
|
+
logger.log(` id: ${row.learning_id}`);
|
|
45
|
+
}
|
|
46
|
+
return { found: true, learnings: rows };
|
|
47
|
+
} finally {
|
|
48
|
+
db.close();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Subcommand: stats <slug>
|
|
54
|
+
* Shows statistics for a squad's learnings.
|
|
55
|
+
*/
|
|
56
|
+
async function handleStats(projectDir, squadSlug, { logger, t }) {
|
|
57
|
+
if (!squadSlug) {
|
|
58
|
+
logger.error(t('squad_learning.slug_required'));
|
|
59
|
+
return { found: false };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
63
|
+
if (!handle) {
|
|
64
|
+
logger.error(t('squad_learning.no_runtime'));
|
|
65
|
+
return { found: false };
|
|
66
|
+
}
|
|
67
|
+
const { db } = handle;
|
|
68
|
+
try {
|
|
69
|
+
const stats = getSquadLearningStats(db, squadSlug);
|
|
70
|
+
if (stats.length === 0) {
|
|
71
|
+
logger.log(t('squad_learning.no_learnings', { slug: squadSlug }));
|
|
72
|
+
return { found: true, stats: [] };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
logger.log(`Learning stats for squad: ${squadSlug}`);
|
|
76
|
+
logger.log('');
|
|
77
|
+
let total = 0;
|
|
78
|
+
for (const row of stats) {
|
|
79
|
+
logger.log(` ${row.type} / ${row.status}: ${row.count}`);
|
|
80
|
+
total += row.count;
|
|
81
|
+
}
|
|
82
|
+
logger.log('');
|
|
83
|
+
logger.log(` Total: ${total}`);
|
|
84
|
+
return { found: true, stats, total };
|
|
85
|
+
} finally {
|
|
86
|
+
db.close();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Subcommand: archive <slug> [--days=90]
|
|
92
|
+
* Moves stale learnings to archived status.
|
|
93
|
+
*/
|
|
94
|
+
async function handleArchive(projectDir, squadSlug, days, { logger, t }) {
|
|
95
|
+
if (!squadSlug) {
|
|
96
|
+
logger.error(t('squad_learning.slug_required'));
|
|
97
|
+
return { archived: 0 };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
101
|
+
if (!handle) {
|
|
102
|
+
logger.error(t('squad_learning.no_runtime'));
|
|
103
|
+
return { archived: 0 };
|
|
104
|
+
}
|
|
105
|
+
const { db } = handle;
|
|
106
|
+
try {
|
|
107
|
+
const count = archiveStaleSquadLearnings(db, squadSlug, days);
|
|
108
|
+
logger.log(t('squad_learning.archived_count', { count, slug: squadSlug }));
|
|
109
|
+
return { archived: count };
|
|
110
|
+
} finally {
|
|
111
|
+
db.close();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Subcommand: promote <slug> <learning-id> --to=<rule-path>
|
|
117
|
+
* Promotes a learning to a rule.
|
|
118
|
+
*/
|
|
119
|
+
async function handlePromote(projectDir, squadSlug, learningId, promotedTo, { logger, t }) {
|
|
120
|
+
if (!squadSlug || !learningId) {
|
|
121
|
+
logger.error(t('squad_learning.promote_usage'));
|
|
122
|
+
return { promoted: false };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
126
|
+
if (!handle) {
|
|
127
|
+
logger.error(t('squad_learning.no_runtime'));
|
|
128
|
+
return { promoted: false };
|
|
129
|
+
}
|
|
130
|
+
const { db } = handle;
|
|
131
|
+
try {
|
|
132
|
+
const learning = getSquadLearning(db, learningId);
|
|
133
|
+
if (!learning || learning.squad_slug !== squadSlug) {
|
|
134
|
+
logger.error(t('squad_learning.not_found', { id: learningId }));
|
|
135
|
+
return { promoted: false };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const rulePath = promotedTo || path.join(SQUADS_DIR, squadSlug, 'rules', `${learning.type}-${Date.now()}.md`);
|
|
139
|
+
const updated = promoteSquadLearning(db, learningId, rulePath);
|
|
140
|
+
if (updated) {
|
|
141
|
+
logger.log(t('squad_learning.promoted', { id: learningId, path: rulePath }));
|
|
142
|
+
}
|
|
143
|
+
return { promoted: updated, rulePath };
|
|
144
|
+
} finally {
|
|
145
|
+
db.close();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Subcommand: export <slug>
|
|
151
|
+
* Exports learnings as JSON.
|
|
152
|
+
*/
|
|
153
|
+
async function handleExport(projectDir, squadSlug, { logger, t }) {
|
|
154
|
+
if (!squadSlug) {
|
|
155
|
+
logger.error(t('squad_learning.slug_required'));
|
|
156
|
+
return { exported: false };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const handle = await openRuntimeDb(projectDir, { mustExist: true });
|
|
160
|
+
if (!handle) {
|
|
161
|
+
logger.error(t('squad_learning.no_runtime'));
|
|
162
|
+
return { exported: false };
|
|
163
|
+
}
|
|
164
|
+
const { db } = handle;
|
|
165
|
+
try {
|
|
166
|
+
const rows = listSquadLearnings(db, squadSlug);
|
|
167
|
+
const output = {
|
|
168
|
+
squad: squadSlug,
|
|
169
|
+
exported_at: new Date().toISOString(),
|
|
170
|
+
learnings: rows
|
|
171
|
+
};
|
|
172
|
+
logger.log(JSON.stringify(output, null, 2));
|
|
173
|
+
return { exported: true, count: rows.length };
|
|
174
|
+
} finally {
|
|
175
|
+
db.close();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Entry point for CLI integration.
|
|
181
|
+
*/
|
|
182
|
+
async function runSquadLearning({ args = [], options = {}, logger = console, t = (k) => k } = {}) {
|
|
183
|
+
const projectDir = path.resolve(process.cwd(), args[0] || '.');
|
|
184
|
+
const sub = options.sub || args[1] || 'list';
|
|
185
|
+
const slug = options.squad || args[2] || null;
|
|
186
|
+
const context = { logger, t };
|
|
187
|
+
|
|
188
|
+
if (sub === 'list') {
|
|
189
|
+
return handleList(projectDir, slug, options.status || null, context);
|
|
190
|
+
}
|
|
191
|
+
if (sub === 'stats') {
|
|
192
|
+
return handleStats(projectDir, slug, context);
|
|
193
|
+
}
|
|
194
|
+
if (sub === 'archive') {
|
|
195
|
+
return handleArchive(projectDir, slug, options.days || 90, context);
|
|
196
|
+
}
|
|
197
|
+
if (sub === 'promote') {
|
|
198
|
+
const learningId = args[3] || options.id;
|
|
199
|
+
return handlePromote(projectDir, slug, learningId, options.to || null, context);
|
|
200
|
+
}
|
|
201
|
+
if (sub === 'export') {
|
|
202
|
+
return handleExport(projectDir, slug, context);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
logger.error(`Unknown subcommand: ${sub}. Available: list, stats, archive, promote, export`);
|
|
206
|
+
return { error: true };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
module.exports = { runSquadLearning, handleList, handleStats, handleArchive, handlePromote, handleExport };
|