@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.
Files changed (213) hide show
  1. package/README.md +19 -2
  2. package/docs/pt/README.md +62 -2
  3. package/docs/pt/advisor-spec.md +5 -5
  4. package/docs/pt/agentes-customizados.md +670 -0
  5. package/docs/pt/agentes.md +111 -13
  6. package/docs/pt/automacao-squads.md +407 -0
  7. package/docs/pt/cenarios.md +3 -3
  8. package/docs/pt/clientes-ai.md +62 -0
  9. package/docs/pt/comandos-cli.md +167 -17
  10. package/docs/pt/deyvin.md +115 -0
  11. package/docs/pt/genome-3.0-spec.md +11 -11
  12. package/docs/pt/inicio-rapido.md +45 -0
  13. package/docs/pt/memoria-contexto.md +255 -0
  14. package/docs/pt/output-strategy-delivery.md +655 -0
  15. package/docs/pt/profiler-system.md +17 -17
  16. package/docs/pt/runtime-observability.md +5 -1
  17. package/docs/pt/skills.md +175 -0
  18. package/docs/pt/{squad-genoma.md → squad-genome.md} +81 -75
  19. package/docs/testing/genome-2.0-rollout.md +1 -1
  20. package/package.json +3 -3
  21. package/src/agents.js +21 -5
  22. package/src/backup-provider.js +303 -0
  23. package/src/cli.js +178 -2
  24. package/src/commands/agents.js +22 -4
  25. package/src/commands/backup.js +533 -0
  26. package/src/commands/cloud.js +17 -17
  27. package/src/commands/context-pack.js +45 -0
  28. package/src/commands/implementation-plan.js +340 -0
  29. package/src/commands/learning.js +134 -0
  30. package/src/commands/live.js +1583 -0
  31. package/src/commands/runtime.js +833 -2
  32. package/src/commands/scan-project.js +288 -24
  33. package/src/commands/setup-context.js +23 -0
  34. package/src/commands/skill.js +558 -0
  35. package/src/commands/squad-agent-create.js +788 -0
  36. package/src/commands/squad-doctor.js +51 -1
  37. package/src/commands/squad-investigate.js +261 -0
  38. package/src/commands/squad-learning.js +209 -0
  39. package/src/commands/squad-pipeline.js +247 -1
  40. package/src/commands/squad-plan.js +329 -0
  41. package/src/commands/squad-status.js +1 -1
  42. package/src/commands/squad-validate.js +57 -1
  43. package/src/commands/test-agents.js +6 -1
  44. package/src/commands/workflow-next.js +8 -1
  45. package/src/commands/workflow-status.js +250 -0
  46. package/src/constants.js +80 -16
  47. package/src/context-memory.js +837 -0
  48. package/src/context-writer.js +2 -0
  49. package/src/delivery-runner.js +319 -0
  50. package/src/genome-files.js +1 -1
  51. package/src/genome-format.js +1 -1
  52. package/src/i18n/messages/en.js +206 -7
  53. package/src/i18n/messages/es.js +123 -6
  54. package/src/i18n/messages/fr.js +122 -5
  55. package/src/i18n/messages/pt-BR.js +205 -12
  56. package/src/installer.js +30 -2
  57. package/src/lib/genomes/compat.js +1 -1
  58. package/src/runtime-store.js +780 -42
  59. package/src/session-handoff.js +77 -0
  60. package/template/.aioson/agents/analyst.md +36 -9
  61. package/template/.aioson/agents/architect.md +20 -5
  62. package/template/.aioson/agents/dev.md +135 -15
  63. package/template/.aioson/agents/deyvin.md +166 -0
  64. package/template/.aioson/agents/discovery-design-doc.md +25 -1
  65. package/template/.aioson/agents/{genoma.md → genome.md} +20 -20
  66. package/template/.aioson/agents/orache.md +371 -0
  67. package/template/.aioson/agents/orchestrator.md +37 -2
  68. package/template/.aioson/agents/pair.md +5 -0
  69. package/template/.aioson/agents/pm.md +17 -5
  70. package/template/.aioson/agents/product.md +58 -22
  71. package/template/.aioson/agents/profiler-enricher.md +1 -1
  72. package/template/.aioson/agents/profiler-forge.md +9 -9
  73. package/template/.aioson/agents/profiler-researcher.md +1 -1
  74. package/template/.aioson/agents/qa.md +17 -5
  75. package/template/.aioson/agents/setup.md +81 -5
  76. package/template/.aioson/agents/squad.md +675 -28
  77. package/template/.aioson/agents/ux-ui.md +277 -34
  78. package/template/.aioson/config.md +175 -0
  79. package/template/.aioson/context/spec.md.template +17 -0
  80. package/template/.aioson/genomes/.gitkeep +0 -0
  81. package/template/.aioson/installed-skills/.gitkeep +0 -0
  82. package/template/.aioson/locales/en/agents/analyst.md +26 -4
  83. package/template/.aioson/locales/en/agents/architect.md +10 -0
  84. package/template/.aioson/locales/en/agents/dev.md +89 -4
  85. package/template/.aioson/locales/en/agents/deyvin.md +129 -0
  86. package/template/.aioson/locales/en/agents/{genoma.md → genome.md} +14 -14
  87. package/template/.aioson/locales/en/agents/orchestrator.md +36 -2
  88. package/template/.aioson/locales/en/agents/pair.md +5 -0
  89. package/template/.aioson/locales/en/agents/pm.md +7 -0
  90. package/template/.aioson/locales/en/agents/product.md +35 -17
  91. package/template/.aioson/locales/en/agents/qa.md +7 -0
  92. package/template/.aioson/locales/en/agents/setup.md +51 -5
  93. package/template/.aioson/locales/en/agents/squad.md +203 -15
  94. package/template/.aioson/locales/en/agents/ux-ui.md +375 -35
  95. package/template/.aioson/locales/es/agents/analyst.md +16 -4
  96. package/template/.aioson/locales/es/agents/architect.md +10 -0
  97. package/template/.aioson/locales/es/agents/dev.md +70 -2
  98. package/template/.aioson/locales/es/agents/deyvin.md +89 -0
  99. package/template/.aioson/locales/es/agents/{genoma.md → genome.md} +13 -13
  100. package/template/.aioson/locales/es/agents/orache.md +103 -0
  101. package/template/.aioson/locales/es/agents/orchestrator.md +36 -2
  102. package/template/.aioson/locales/es/agents/pair.md +5 -0
  103. package/template/.aioson/locales/es/agents/pm.md +7 -0
  104. package/template/.aioson/locales/es/agents/product.md +13 -3
  105. package/template/.aioson/locales/es/agents/qa.md +7 -0
  106. package/template/.aioson/locales/es/agents/setup.md +28 -5
  107. package/template/.aioson/locales/es/agents/squad.md +221 -15
  108. package/template/.aioson/locales/es/agents/ux-ui.md +26 -25
  109. package/template/.aioson/locales/fr/agents/analyst.md +16 -4
  110. package/template/.aioson/locales/fr/agents/architect.md +10 -0
  111. package/template/.aioson/locales/fr/agents/dev.md +70 -2
  112. package/template/.aioson/locales/fr/agents/deyvin.md +89 -0
  113. package/template/.aioson/locales/fr/agents/{genoma.md → genome.md} +7 -7
  114. package/template/.aioson/locales/fr/agents/orache.md +104 -0
  115. package/template/.aioson/locales/fr/agents/orchestrator.md +36 -2
  116. package/template/.aioson/locales/fr/agents/pair.md +5 -0
  117. package/template/.aioson/locales/fr/agents/pm.md +7 -0
  118. package/template/.aioson/locales/fr/agents/product.md +13 -3
  119. package/template/.aioson/locales/fr/agents/qa.md +7 -0
  120. package/template/.aioson/locales/fr/agents/setup.md +28 -5
  121. package/template/.aioson/locales/fr/agents/squad.md +216 -10
  122. package/template/.aioson/locales/fr/agents/ux-ui.md +26 -25
  123. package/template/.aioson/locales/pt-BR/agents/analyst.md +26 -4
  124. package/template/.aioson/locales/pt-BR/agents/architect.md +10 -0
  125. package/template/.aioson/locales/pt-BR/agents/dev.md +93 -4
  126. package/template/.aioson/locales/pt-BR/agents/deyvin.md +129 -0
  127. package/template/.aioson/locales/pt-BR/agents/{genoma.md → genome.md} +49 -49
  128. package/template/.aioson/locales/pt-BR/agents/orache.md +137 -0
  129. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +36 -2
  130. package/template/.aioson/locales/pt-BR/agents/pair.md +5 -0
  131. package/template/.aioson/locales/pt-BR/agents/pm.md +7 -0
  132. package/template/.aioson/locales/pt-BR/agents/product.md +35 -17
  133. package/template/.aioson/locales/pt-BR/agents/qa.md +7 -0
  134. package/template/.aioson/locales/pt-BR/agents/setup.md +51 -5
  135. package/template/.aioson/locales/pt-BR/agents/squad.md +486 -47
  136. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +361 -22
  137. package/template/.aioson/my-agents/.gitkeep +0 -0
  138. package/template/.aioson/rules/.gitkeep +0 -0
  139. package/template/.aioson/rules/squad/.gitkeep +0 -0
  140. package/template/.aioson/rules/squad/README.md +50 -0
  141. package/template/.aioson/schemas/genome-meta.schema.json +1 -1
  142. package/template/.aioson/schemas/genome.schema.json +1 -1
  143. package/template/.aioson/schemas/squad-blueprint.schema.json +11 -0
  144. package/template/.aioson/schemas/squad-manifest.schema.json +257 -1
  145. package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +157 -0
  146. package/template/.aioson/skills/design/cognitive-core-ui/references/components.md +407 -0
  147. package/template/.aioson/skills/design/cognitive-core-ui/references/dashboards.md +172 -0
  148. package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +490 -0
  149. package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +237 -0
  150. package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +289 -0
  151. package/template/.aioson/skills/design/cognitive-core-ui/references/websites.md +350 -0
  152. package/template/.aioson/skills/design/interface-design/SKILL.md +47 -0
  153. package/template/.aioson/skills/design/interface-design/references/components-and-states.md +105 -0
  154. package/template/.aioson/skills/design/interface-design/references/design-directions.md +101 -0
  155. package/template/.aioson/skills/design/interface-design/references/handoff-and-quality.md +71 -0
  156. package/template/.aioson/skills/design/interface-design/references/intent-and-domain.md +74 -0
  157. package/template/.aioson/skills/design/interface-design/references/tokens-and-depth.md +173 -0
  158. package/template/.aioson/skills/design/premium-command-center-ui/SKILL.md +62 -0
  159. package/template/.aioson/skills/design/premium-command-center-ui/references/operations.md +74 -0
  160. package/template/.aioson/skills/design/premium-command-center-ui/references/patterns.md +116 -0
  161. package/template/.aioson/skills/design/premium-command-center-ui/references/validation.md +47 -0
  162. package/template/.aioson/skills/design/premium-command-center-ui/references/visual-system.md +215 -0
  163. package/template/.aioson/skills/design-system/SKILL.md +92 -0
  164. package/template/.aioson/skills/design-system/cognitive-core-ui.skill +0 -0
  165. package/template/.aioson/skills/design-system/components/SKILL.md +274 -0
  166. package/template/.aioson/skills/design-system/components/SKILL.md:Zone.Identifier +0 -0
  167. package/template/.aioson/skills/design-system/dashboards/SKILL.md +184 -0
  168. package/template/.aioson/skills/design-system/dashboards/SKILL.md:Zone.Identifier +0 -0
  169. package/template/.aioson/skills/design-system/foundations/SKILL.md +250 -0
  170. package/template/.aioson/skills/design-system/foundations/SKILL.md:Zone.Identifier +0 -0
  171. package/template/.aioson/skills/design-system/motion/SKILL.md +197 -0
  172. package/template/.aioson/skills/design-system/motion/SKILL.md:Zone.Identifier +0 -0
  173. package/template/.aioson/skills/design-system/patterns/SKILL.md +231 -0
  174. package/template/.aioson/skills/design-system/patterns/SKILL.md:Zone.Identifier +0 -0
  175. package/template/.aioson/skills/squad/SKILL.md +58 -0
  176. package/template/.aioson/skills/squad/domains/.gitkeep +0 -0
  177. package/template/.aioson/skills/squad/formats/.gitkeep +0 -0
  178. package/template/.aioson/skills/squad/patterns/.gitkeep +0 -0
  179. package/template/.aioson/skills/squad/references/.gitkeep +0 -0
  180. package/template/.aioson/tasks/implementation-plan.md +288 -0
  181. package/template/.aioson/tasks/squad-create.md +1 -1
  182. package/template/.aioson/tasks/squad-execution-plan.md +279 -0
  183. package/template/.aioson/tasks/squad-export.md +1 -1
  184. package/template/.aioson/tasks/squad-investigate.md +44 -0
  185. package/template/.aioson/tasks/squad-learning-review.md +44 -0
  186. package/template/.aioson/tasks/squad-output-config.md +177 -0
  187. package/template/.aioson/tasks/squad-validate.md +1 -1
  188. package/template/.claude/commands/aioson/agent/deyvin.md +5 -0
  189. package/template/.claude/commands/aioson/agent/discovery-design-doc.md +5 -0
  190. package/template/.claude/commands/aioson/agent/genome.md +5 -0
  191. package/template/.claude/commands/aioson/agent/product.md +5 -0
  192. package/template/.claude/commands/aioson/agent/profiler-enricher.md +5 -0
  193. package/template/.claude/commands/aioson/agent/profiler-forge.md +5 -0
  194. package/template/.claude/commands/aioson/agent/profiler-researcher.md +5 -0
  195. package/template/.claude/commands/aioson/agent/squad.md +5 -0
  196. package/template/.gemini/GEMINI.md +2 -0
  197. package/template/.gemini/commands/aios-deyvin.toml +6 -0
  198. package/template/.gemini/commands/aios-pair.toml +6 -0
  199. package/template/AGENTS.md +34 -6
  200. package/template/CLAUDE.md +31 -4
  201. package/template/OPENCODE.md +6 -2
  202. package/template/squad-searches/.gitkeep +0 -0
  203. package/template/.aioson/skills/static/interface-design.md +0 -372
  204. package/template/.aioson/skills/static/premium-command-center-ui.md +0 -190
  205. /package/template/.aioson/{genomas → docs}/.gitkeep +0 -0
  206. /package/template/.claude/commands/aioson/{analyst.md → agent/analyst.md} +0 -0
  207. /package/template/.claude/commands/aioson/{architect.md → agent/architect.md} +0 -0
  208. /package/template/.claude/commands/aioson/{dev.md → agent/dev.md} +0 -0
  209. /package/template/.claude/commands/aioson/{orchestrator.md → agent/orchestrator.md} +0 -0
  210. /package/template/.claude/commands/aioson/{pm.md → agent/pm.md} +0 -0
  211. /package/template/.claude/commands/aioson/{qa.md → agent/qa.md} +0 -0
  212. /package/template/.claude/commands/aioson/{setup.md → agent/setup.md} +0 -0
  213. /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 || `aios-logs/${slug}`)),
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 };