@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
@@ -12,6 +12,7 @@ const {
12
12
 
13
13
  const RUNTIME_DIR = path.join('.aioson', 'runtime');
14
14
  const DB_FILE = 'aios.sqlite';
15
+ const LOGS_DIR = 'aioson-logs';
15
16
  const SESSIONS_DIR = '.sessions';
16
17
  const VALID_STATUSES = new Set(['queued', 'running', 'completed', 'failed']);
17
18
  const VALID_TASK_STATUSES = new Set(['queued', 'running', 'completed', 'failed']);
@@ -32,7 +33,8 @@ function resolveRuntimePaths(targetDir) {
32
33
  const runtimeDir = path.join(targetDir, RUNTIME_DIR);
33
34
  return {
34
35
  runtimeDir,
35
- dbPath: path.join(runtimeDir, DB_FILE)
36
+ dbPath: path.join(runtimeDir, DB_FILE),
37
+ logsDir: path.join(targetDir, LOGS_DIR)
36
38
  };
37
39
  }
38
40
 
@@ -42,7 +44,7 @@ async function runtimeStoreExists(targetDir) {
42
44
  }
43
45
 
44
46
  async function openRuntimeDb(targetDir, options = {}) {
45
- const { runtimeDir, dbPath } = resolveRuntimePaths(targetDir);
47
+ const { runtimeDir, dbPath, logsDir } = resolveRuntimePaths(targetDir);
46
48
  const mustExist = Boolean(options.mustExist);
47
49
 
48
50
  if (mustExist && !(await exists(dbPath))) {
@@ -50,6 +52,7 @@ async function openRuntimeDb(targetDir, options = {}) {
50
52
  }
51
53
 
52
54
  await ensureDir(runtimeDir);
55
+ await ensureDir(logsDir);
53
56
 
54
57
  const db = new Database(dbPath);
55
58
  db.pragma('journal_mode = WAL');
@@ -125,8 +128,11 @@ async function openRuntimeDb(targetDir, options = {}) {
125
128
  task_key TEXT PRIMARY KEY,
126
129
  squad_slug TEXT,
127
130
  session_key TEXT,
131
+ task_kind TEXT,
132
+ parent_task_key TEXT,
128
133
  title TEXT NOT NULL,
129
134
  goal TEXT,
135
+ meta_json TEXT,
130
136
  status TEXT NOT NULL,
131
137
  created_by TEXT,
132
138
  created_at TEXT NOT NULL,
@@ -351,6 +357,145 @@ async function openRuntimeDb(targetDir, options = {}) {
351
357
 
352
358
  CREATE INDEX IF NOT EXISTS idx_artisan_squads_status ON artisan_squads(status, updated_at DESC);
353
359
  CREATE INDEX IF NOT EXISTS idx_artisan_messages_artisan ON artisan_messages(artisan_id, created_at ASC);
360
+
361
+ CREATE TABLE IF NOT EXISTS delivery_log (
362
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
363
+ squad_slug TEXT NOT NULL,
364
+ content_key TEXT,
365
+ webhook_slug TEXT,
366
+ trigger_type TEXT NOT NULL,
367
+ url TEXT NOT NULL,
368
+ status_code INTEGER,
369
+ response_body TEXT,
370
+ error_message TEXT,
371
+ attempt INTEGER DEFAULT 1,
372
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
373
+ );
374
+
375
+ CREATE INDEX IF NOT EXISTS idx_delivery_log_squad ON delivery_log(squad_slug);
376
+ CREATE INDEX IF NOT EXISTS idx_delivery_log_content ON delivery_log(content_key);
377
+
378
+ CREATE TABLE IF NOT EXISTS backup_manifest (
379
+ record_key TEXT PRIMARY KEY,
380
+ record_type TEXT NOT NULL,
381
+ content_hash TEXT NOT NULL,
382
+ backed_up_at TEXT NOT NULL,
383
+ remote_key TEXT NOT NULL
384
+ );
385
+ CREATE INDEX IF NOT EXISTS idx_backup_manifest_type ON backup_manifest(record_type, backed_up_at DESC);
386
+
387
+ CREATE TABLE IF NOT EXISTS squad_investigations (
388
+ investigation_slug TEXT PRIMARY KEY,
389
+ domain TEXT NOT NULL,
390
+ mode TEXT DEFAULT 'full',
391
+ dimensions_covered INTEGER DEFAULT 0,
392
+ total_dimensions INTEGER DEFAULT 7,
393
+ confidence REAL DEFAULT 0,
394
+ report_path TEXT,
395
+ linked_squad_slug TEXT,
396
+ created_at TEXT DEFAULT (datetime('now')),
397
+ updated_at TEXT DEFAULT (datetime('now'))
398
+ );
399
+
400
+ CREATE INDEX IF NOT EXISTS idx_squad_investigations_domain ON squad_investigations(domain);
401
+ CREATE INDEX IF NOT EXISTS idx_squad_investigations_squad ON squad_investigations(linked_squad_slug);
402
+
403
+ CREATE TABLE IF NOT EXISTS implementation_plans (
404
+ plan_id TEXT PRIMARY KEY,
405
+ project_name TEXT,
406
+ scope TEXT DEFAULT 'project',
407
+ feature_slug TEXT,
408
+ status TEXT DEFAULT 'draft',
409
+ classification TEXT,
410
+ phases_total INTEGER DEFAULT 0,
411
+ phases_completed INTEGER DEFAULT 0,
412
+ source_artifacts TEXT,
413
+ source_hash TEXT,
414
+ created_at TEXT DEFAULT (datetime('now')),
415
+ updated_at TEXT DEFAULT (datetime('now'))
416
+ );
417
+
418
+ CREATE TABLE IF NOT EXISTS plan_phases (
419
+ plan_id TEXT NOT NULL,
420
+ phase_number INTEGER NOT NULL,
421
+ title TEXT NOT NULL,
422
+ status TEXT DEFAULT 'pending',
423
+ completed_at TEXT,
424
+ notes TEXT,
425
+ PRIMARY KEY (plan_id, phase_number),
426
+ FOREIGN KEY (plan_id) REFERENCES implementation_plans(plan_id)
427
+ );
428
+
429
+ CREATE TABLE IF NOT EXISTS squad_execution_plans (
430
+ plan_slug TEXT PRIMARY KEY,
431
+ squad_slug TEXT NOT NULL,
432
+ status TEXT DEFAULT 'draft',
433
+ rounds_total INTEGER DEFAULT 0,
434
+ rounds_completed INTEGER DEFAULT 0,
435
+ based_on_blueprint TEXT,
436
+ based_on_investigation TEXT,
437
+ source_hash TEXT,
438
+ created_at TEXT DEFAULT (datetime('now')),
439
+ updated_at TEXT DEFAULT (datetime('now'))
440
+ );
441
+
442
+ CREATE TABLE IF NOT EXISTS squad_plan_rounds (
443
+ plan_slug TEXT NOT NULL,
444
+ round_number INTEGER NOT NULL,
445
+ executor_slug TEXT NOT NULL,
446
+ title TEXT NOT NULL,
447
+ status TEXT DEFAULT 'pending',
448
+ completed_at TEXT,
449
+ notes TEXT,
450
+ PRIMARY KEY (plan_slug, round_number),
451
+ FOREIGN KEY (plan_slug) REFERENCES squad_execution_plans(plan_slug)
452
+ );
453
+
454
+ CREATE TABLE IF NOT EXISTS squad_learnings (
455
+ learning_id TEXT PRIMARY KEY,
456
+ squad_slug TEXT NOT NULL,
457
+ type TEXT NOT NULL CHECK (type IN ('preference', 'process', 'domain', 'quality')),
458
+ title TEXT NOT NULL,
459
+ signal TEXT DEFAULT 'explicit' CHECK (signal IN ('explicit', 'implicit')),
460
+ confidence TEXT DEFAULT 'medium' CHECK (confidence IN ('high', 'medium', 'low')),
461
+ frequency INTEGER DEFAULT 1,
462
+ last_reinforced TEXT,
463
+ applies_to TEXT DEFAULT 'squad',
464
+ file_path TEXT,
465
+ promoted_to TEXT,
466
+ status TEXT DEFAULT 'active' CHECK (status IN ('active', 'stale', 'archived', 'promoted')),
467
+ source_session TEXT,
468
+ evidence TEXT,
469
+ created_at TEXT DEFAULT (datetime('now')),
470
+ updated_at TEXT DEFAULT (datetime('now'))
471
+ );
472
+
473
+ CREATE TABLE IF NOT EXISTS project_learnings (
474
+ learning_id TEXT PRIMARY KEY,
475
+ project_name TEXT,
476
+ feature_slug TEXT,
477
+ type TEXT NOT NULL CHECK (type IN ('preference', 'process', 'domain', 'quality')),
478
+ title TEXT NOT NULL,
479
+ confidence TEXT DEFAULT 'medium',
480
+ frequency INTEGER DEFAULT 1,
481
+ last_reinforced TEXT,
482
+ applies_to TEXT DEFAULT 'project',
483
+ promoted_to TEXT,
484
+ status TEXT DEFAULT 'active' CHECK (status IN ('active', 'stale', 'archived', 'promoted')),
485
+ source_session TEXT,
486
+ evidence TEXT,
487
+ created_at TEXT DEFAULT (datetime('now')),
488
+ updated_at TEXT DEFAULT (datetime('now'))
489
+ );
490
+
491
+ CREATE INDEX IF NOT EXISTS idx_impl_plans_status ON implementation_plans(status);
492
+ CREATE INDEX IF NOT EXISTS idx_squad_exec_plans_squad ON squad_execution_plans(squad_slug);
493
+ CREATE INDEX IF NOT EXISTS idx_squad_exec_plans_status ON squad_execution_plans(status);
494
+ CREATE INDEX IF NOT EXISTS idx_squad_learnings_squad ON squad_learnings(squad_slug);
495
+ CREATE INDEX IF NOT EXISTS idx_squad_learnings_type ON squad_learnings(type);
496
+ CREATE INDEX IF NOT EXISTS idx_squad_learnings_status ON squad_learnings(status);
497
+ CREATE INDEX IF NOT EXISTS idx_project_learnings_type ON project_learnings(type);
498
+ CREATE INDEX IF NOT EXISTS idx_project_learnings_status ON project_learnings(status);
354
499
  `);
355
500
 
356
501
  ensureLegacyColumns(db);
@@ -381,6 +526,25 @@ function normalizeTaskStatus(value, fallback) {
381
526
  }
382
527
 
383
528
  function ensureLegacyColumns(db) {
529
+ const taskColumns = db.prepare('PRAGMA table_info(tasks)').all();
530
+ const taskColumnNames = new Set(taskColumns.map((column) => column.name));
531
+
532
+ if (!taskColumnNames.has('task_kind')) {
533
+ db.exec('ALTER TABLE tasks ADD COLUMN task_kind TEXT');
534
+ }
535
+
536
+ if (!taskColumnNames.has('parent_task_key')) {
537
+ db.exec('ALTER TABLE tasks ADD COLUMN parent_task_key TEXT');
538
+ }
539
+
540
+ if (!taskColumnNames.has('meta_json')) {
541
+ db.exec('ALTER TABLE tasks ADD COLUMN meta_json TEXT');
542
+ }
543
+
544
+ db.exec('CREATE INDEX IF NOT EXISTS idx_tasks_session ON tasks(session_key, updated_at DESC)');
545
+ db.exec('CREATE INDEX IF NOT EXISTS idx_tasks_parent ON tasks(parent_task_key, updated_at DESC)');
546
+ db.exec('CREATE INDEX IF NOT EXISTS idx_tasks_kind ON tasks(task_kind, updated_at DESC)');
547
+
384
548
  const agentRunColumns = db.prepare('PRAGMA table_info(agent_runs)').all();
385
549
  const agentRunColumnNames = new Set(agentRunColumns.map((column) => column.name));
386
550
 
@@ -498,56 +662,66 @@ function appendRunEvent(db, options) {
498
662
  const now = options.createdAt || nowIso();
499
663
  const payloadJson = options.payload ? JSON.stringify(options.payload) : null;
500
664
 
501
- insertEvent(db, {
502
- run_key: run.run_key,
503
- event_type: String(options.eventType || 'update'),
504
- message: String(options.message || ''),
505
- payload_json: payloadJson,
506
- created_at: now
507
- });
665
+ const doInsert = db.transaction(() => {
666
+ insertEvent(db, {
667
+ run_key: run.run_key,
668
+ event_type: String(options.eventType || 'update'),
669
+ message: String(options.message || ''),
670
+ payload_json: payloadJson,
671
+ created_at: now
672
+ });
508
673
 
509
- insertExecutionEvent(db, {
510
- task_key: run.task_key,
511
- run_key: run.run_key,
512
- agent_name: run.agent_name,
513
- agent_kind: run.agent_kind,
514
- squad_slug: run.squad_slug,
515
- session_key: run.session_key,
516
- source: run.source,
517
- workflow_id: run.workflow_id,
518
- workflow_stage: run.workflow_stage,
519
- parent_run_key: run.parent_run_key,
520
- event_type: String(options.eventType || 'update'),
521
- phase: options.phase ? String(options.phase).trim() : null,
522
- status: options.status ? String(options.status).trim() : run.status || null,
523
- tool_name: options.toolName ? String(options.toolName).trim() : null,
524
- message: String(options.message || ''),
525
- payload_json: payloadJson,
526
- sequence_no: nextExecutionSequence(db, run.run_key),
527
- parent_event_id: options.parentEventId || null,
528
- created_at: now
674
+ insertExecutionEvent(db, {
675
+ task_key: run.task_key,
676
+ run_key: run.run_key,
677
+ agent_name: run.agent_name,
678
+ agent_kind: run.agent_kind,
679
+ squad_slug: run.squad_slug,
680
+ session_key: run.session_key,
681
+ source: run.source,
682
+ workflow_id: run.workflow_id,
683
+ workflow_stage: run.workflow_stage,
684
+ parent_run_key: run.parent_run_key,
685
+ event_type: String(options.eventType || 'update'),
686
+ phase: options.phase ? String(options.phase).trim() : null,
687
+ status: options.status ? String(options.status).trim() : run.status || null,
688
+ tool_name: options.toolName ? String(options.toolName).trim() : null,
689
+ message: String(options.message || ''),
690
+ payload_json: payloadJson,
691
+ sequence_no: nextExecutionSequence(db, run.run_key),
692
+ parent_event_id: options.parentEventId || null,
693
+ created_at: now
694
+ });
529
695
  });
696
+
697
+ doInsert();
530
698
  }
531
699
 
532
700
  function startTask(db, options) {
533
701
  const now = nowIso();
534
702
  const taskKey = String(options.taskKey || createTaskKey(options.title));
535
703
  const status = normalizeTaskStatus(options.status, 'running');
704
+ const metaJson = options.metaJson && typeof options.metaJson === 'object'
705
+ ? JSON.stringify(options.metaJson)
706
+ : (typeof options.metaJson === 'string' && options.metaJson.trim() ? options.metaJson.trim() : null);
536
707
 
537
708
  db.prepare(`
538
709
  INSERT INTO tasks (
539
- task_key, squad_slug, session_key, title, goal, status, created_by,
540
- created_at, updated_at, finished_at
710
+ task_key, squad_slug, session_key, task_kind, parent_task_key,
711
+ title, goal, meta_json, status, created_by, created_at, updated_at, finished_at
541
712
  ) VALUES (
542
- @task_key, @squad_slug, @session_key, @title, @goal, @status, @created_by,
543
- @created_at, @updated_at, @finished_at
713
+ @task_key, @squad_slug, @session_key, @task_kind, @parent_task_key,
714
+ @title, @goal, @meta_json, @status, @created_by, @created_at, @updated_at, @finished_at
544
715
  )
545
716
  `).run({
546
717
  task_key: taskKey,
547
718
  squad_slug: options.squadSlug ? String(options.squadSlug).trim() : null,
548
719
  session_key: options.sessionKey ? String(options.sessionKey).trim() : null,
720
+ task_kind: options.taskKind ? String(options.taskKind).trim() : null,
721
+ parent_task_key: options.parentTaskKey ? String(options.parentTaskKey).trim() : null,
549
722
  title: String(options.title).trim(),
550
723
  goal: options.goal ? String(options.goal).trim() : null,
724
+ meta_json: metaJson,
551
725
  status,
552
726
  created_by: options.createdBy ? String(options.createdBy).trim() : null,
553
727
  created_at: now,
@@ -566,12 +740,18 @@ function updateTask(db, options) {
566
740
 
567
741
  const now = nowIso();
568
742
  const nextStatus = normalizeTaskStatus(options.status, existing.status || 'running');
743
+ const metaJson = options.metaJson && typeof options.metaJson === 'object'
744
+ ? JSON.stringify(options.metaJson)
745
+ : (typeof options.metaJson === 'string' && options.metaJson.trim() ? options.metaJson.trim() : null);
569
746
 
570
747
  db.prepare(`
571
748
  UPDATE tasks
572
749
  SET
573
750
  status = @status,
574
751
  goal = COALESCE(@goal, goal),
752
+ task_kind = COALESCE(@task_kind, task_kind),
753
+ parent_task_key = COALESCE(@parent_task_key, parent_task_key),
754
+ meta_json = COALESCE(@meta_json, meta_json),
575
755
  updated_at = @updated_at,
576
756
  finished_at = CASE
577
757
  WHEN @status IN ('completed', 'failed') THEN @updated_at
@@ -582,6 +762,9 @@ function updateTask(db, options) {
582
762
  task_key: String(options.taskKey),
583
763
  status: nextStatus,
584
764
  goal: options.goal ? String(options.goal).trim() : null,
765
+ task_kind: options.taskKind ? String(options.taskKind).trim() : null,
766
+ parent_task_key: options.parentTaskKey ? String(options.parentTaskKey).trim() : null,
767
+ meta_json: metaJson,
585
768
  updated_at: now
586
769
  });
587
770
 
@@ -696,6 +879,52 @@ function parseJsonArray(value) {
696
879
  }
697
880
  }
698
881
 
882
+
883
+ function parseJsonObject(value) {
884
+ if (!value) return null;
885
+ try {
886
+ const parsed = JSON.parse(value);
887
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
888
+ } catch {
889
+ return null;
890
+ }
891
+ }
892
+
893
+ function getTaskPlanProgress(meta) {
894
+ const steps = Array.isArray(meta?.plan_steps) ? meta.plan_steps : [];
895
+ return {
896
+ plan_steps_done: steps.filter((step) => step && step.done).length,
897
+ plan_steps_total: steps.length
898
+ };
899
+ }
900
+
901
+ function decorateTaskSnapshotRow(row) {
902
+ const meta = parseJsonObject(row.meta_json);
903
+ const progress = getTaskPlanProgress(meta);
904
+ row.meta = meta;
905
+ row.plan_steps_done = progress.plan_steps_done;
906
+ row.plan_steps_total = progress.plan_steps_total;
907
+ row.is_live_session = row.task_kind === 'live_session';
908
+ row.is_micro_task = row.task_kind === 'micro_task';
909
+ return row;
910
+ }
911
+
912
+ function decorateRunSnapshotRow(row) {
913
+ row.used_skills = parseJsonArray(row.used_skills_json);
914
+ row.is_live = row.source === 'live';
915
+ row.is_handoff_child = Boolean(row.parent_run_key);
916
+ return row;
917
+ }
918
+
919
+ function decorateExecutionEventSnapshotRow(row) {
920
+ const payload = parseJsonObject(row.payload_json);
921
+ row.payload = payload;
922
+ row.is_handoff = row.event_type === 'handoff';
923
+ row.handoff_from = payload?.from || row.agent_name || null;
924
+ row.handoff_to = payload?.to || null;
925
+ return row;
926
+ }
927
+
699
928
  function upsertSquadManifest(db, options) {
700
929
  const now = nowIso();
701
930
  const slug = String(options.slug).trim();
@@ -1110,7 +1339,7 @@ function startRun(db, options) {
1110
1339
 
1111
1340
  appendRunEvent(db, {
1112
1341
  runKey,
1113
- eventType: 'start',
1342
+ eventType: String(options.eventType || 'start'),
1114
1343
  phase: options.phase || 'run',
1115
1344
  status,
1116
1345
  message: String(options.message || options.title || 'Agent started'),
@@ -1242,7 +1471,7 @@ function getStatusSnapshot(db) {
1242
1471
 
1243
1472
  const activeTasks = db.prepare(`
1244
1473
  SELECT
1245
- task_key, squad_slug, session_key, title, goal, status, created_by, created_at, updated_at,
1474
+ task_key, squad_slug, session_key, task_kind, parent_task_key, title, goal, meta_json, status, created_by, created_at, updated_at,
1246
1475
  (
1247
1476
  SELECT COUNT(*)
1248
1477
  FROM agent_runs
@@ -1252,7 +1481,29 @@ function getStatusSnapshot(db) {
1252
1481
  SELECT COUNT(*)
1253
1482
  FROM artifacts
1254
1483
  WHERE artifacts.task_key = tasks.task_key
1255
- ) AS artifact_count
1484
+ ) AS artifact_count,
1485
+ (
1486
+ SELECT agent_name
1487
+ FROM agent_runs
1488
+ WHERE agent_runs.task_key = tasks.task_key
1489
+ ORDER BY CASE WHEN agent_runs.status IN ('queued', 'running') THEN 0 ELSE 1 END, updated_at DESC, started_at DESC
1490
+ LIMIT 1
1491
+ ) AS latest_agent_name,
1492
+ (
1493
+ SELECT COUNT(*)
1494
+ FROM tasks AS child_tasks
1495
+ WHERE child_tasks.parent_task_key = tasks.task_key
1496
+ ) AS child_task_count,
1497
+ (
1498
+ SELECT COUNT(*)
1499
+ FROM tasks AS child_tasks
1500
+ WHERE child_tasks.parent_task_key = tasks.task_key AND child_tasks.status = 'completed'
1501
+ ) AS completed_child_task_count,
1502
+ (
1503
+ SELECT COUNT(*)
1504
+ FROM agent_runs AS handoff_runs
1505
+ WHERE handoff_runs.task_key = tasks.task_key AND handoff_runs.parent_run_key IS NOT NULL
1506
+ ) AS handoff_count
1256
1507
  FROM tasks
1257
1508
  WHERE status IN ('queued', 'running')
1258
1509
  ORDER BY updated_at DESC, created_at DESC
@@ -1260,7 +1511,7 @@ function getStatusSnapshot(db) {
1260
1511
 
1261
1512
  const recentTasks = db.prepare(`
1262
1513
  SELECT
1263
- task_key, squad_slug, session_key, title, goal, status, created_by, created_at, updated_at, finished_at,
1514
+ task_key, squad_slug, session_key, task_kind, parent_task_key, title, goal, meta_json, status, created_by, created_at, updated_at, finished_at,
1264
1515
  (
1265
1516
  SELECT COUNT(*)
1266
1517
  FROM agent_runs
@@ -1270,7 +1521,29 @@ function getStatusSnapshot(db) {
1270
1521
  SELECT COUNT(*)
1271
1522
  FROM artifacts
1272
1523
  WHERE artifacts.task_key = tasks.task_key
1273
- ) AS artifact_count
1524
+ ) AS artifact_count,
1525
+ (
1526
+ SELECT agent_name
1527
+ FROM agent_runs
1528
+ WHERE agent_runs.task_key = tasks.task_key
1529
+ ORDER BY CASE WHEN agent_runs.status IN ('queued', 'running') THEN 0 ELSE 1 END, updated_at DESC, started_at DESC
1530
+ LIMIT 1
1531
+ ) AS latest_agent_name,
1532
+ (
1533
+ SELECT COUNT(*)
1534
+ FROM tasks AS child_tasks
1535
+ WHERE child_tasks.parent_task_key = tasks.task_key
1536
+ ) AS child_task_count,
1537
+ (
1538
+ SELECT COUNT(*)
1539
+ FROM tasks AS child_tasks
1540
+ WHERE child_tasks.parent_task_key = tasks.task_key AND child_tasks.status = 'completed'
1541
+ ) AS completed_child_task_count,
1542
+ (
1543
+ SELECT COUNT(*)
1544
+ FROM agent_runs AS handoff_runs
1545
+ WHERE handoff_runs.task_key = tasks.task_key AND handoff_runs.parent_run_key IS NOT NULL
1546
+ ) AS handoff_count
1274
1547
  FROM tasks
1275
1548
  ORDER BY updated_at DESC, created_at DESC
1276
1549
  LIMIT 20
@@ -1303,17 +1576,35 @@ function getStatusSnapshot(db) {
1303
1576
  `).all();
1304
1577
 
1305
1578
  for (const row of activeRuns) {
1306
- row.used_skills = parseJsonArray(row.used_skills_json);
1579
+ decorateRunSnapshotRow(row);
1307
1580
  }
1308
1581
 
1309
1582
  for (const row of recentRuns) {
1310
- row.used_skills = parseJsonArray(row.used_skills_json);
1583
+ decorateRunSnapshotRow(row);
1584
+ }
1585
+
1586
+ for (const row of activeTasks) {
1587
+ decorateTaskSnapshotRow(row);
1588
+ }
1589
+
1590
+ for (const row of recentTasks) {
1591
+ decorateTaskSnapshotRow(row);
1311
1592
  }
1312
1593
 
1313
1594
  for (const row of recentContentItems) {
1314
1595
  row.used_skills = parseJsonArray(row.used_skills_json);
1315
1596
  }
1316
1597
 
1598
+ for (const row of recentExecutionEvents) {
1599
+ decorateExecutionEventSnapshotRow(row);
1600
+ }
1601
+
1602
+ const activeLiveSessions = activeTasks.filter((task) => task.task_kind === 'live_session');
1603
+ const activeMicroTasks = activeTasks.filter((task) => task.task_kind === 'micro_task');
1604
+ const recentLiveSessions = recentTasks.filter((task) => task.task_kind === 'live_session');
1605
+ const recentMicroTasks = recentTasks.filter((task) => task.task_kind === 'micro_task');
1606
+ const recentHandoffs = recentExecutionEvents.filter((event) => event.event_type === 'handoff');
1607
+
1317
1608
  return {
1318
1609
  taskCounts,
1319
1610
  counts,
@@ -1321,6 +1612,11 @@ function getStatusSnapshot(db) {
1321
1612
  recentTasks,
1322
1613
  activeRuns,
1323
1614
  recentRuns,
1615
+ activeLiveSessions,
1616
+ activeMicroTasks,
1617
+ recentLiveSessions,
1618
+ recentMicroTasks,
1619
+ recentHandoffs,
1324
1620
  recentArtifacts,
1325
1621
  recentContentItems,
1326
1622
  recentExecutionEvents
@@ -1374,6 +1670,7 @@ async function clearAgentSession(runtimeDir, agentName) {
1374
1670
  async function logAgentEvent(db, runtimeDir, options) {
1375
1671
  const agentName = String(options.agentName || 'unknown').trim();
1376
1672
  const squadSlug = options.squadSlug ? String(options.squadSlug).trim() : null;
1673
+ const sessionKey = options.sessionKey ? String(options.sessionKey).trim() : null;
1377
1674
  const isFinish = Boolean(options.finish);
1378
1675
  const now = nowIso();
1379
1676
 
@@ -1442,6 +1739,7 @@ async function logAgentEvent(db, runtimeDir, options) {
1442
1739
  taskKey = startTask(db, {
1443
1740
  title: taskTitle,
1444
1741
  squadSlug: null,
1742
+ sessionKey,
1445
1743
  status: 'running',
1446
1744
  createdBy: agentName
1447
1745
  });
@@ -1450,10 +1748,11 @@ async function logAgentEvent(db, runtimeDir, options) {
1450
1748
  agentName,
1451
1749
  agentKind: 'official',
1452
1750
  squadSlug: null,
1751
+ sessionKey,
1453
1752
  title: taskTitle,
1454
1753
  message: options.message || 'Iniciando'
1455
1754
  });
1456
- await writeAgentSession(runtimeDir, agentName, { runKey, taskKey, startedAt: now, finished: false });
1755
+ await writeAgentSession(runtimeDir, agentName, { runKey, taskKey, sessionKey, startedAt: now, finished: false });
1457
1756
  } else {
1458
1757
  appendRunEvent(db, {
1459
1758
  runKey,
@@ -1487,6 +1786,405 @@ async function logAgentEvent(db, runtimeDir, options) {
1487
1786
  return { runKey, taskKey };
1488
1787
  }
1489
1788
 
1789
+ // --- Squad Investigations CRUD ---
1790
+
1791
+ function insertInvestigation(db, options = {}) {
1792
+ const slug = options.investigationSlug || `inv-${slugify(options.domain || 'unknown')}-${Date.now()}`;
1793
+ const now = nowIso();
1794
+ db.prepare(`
1795
+ INSERT OR REPLACE INTO squad_investigations
1796
+ (investigation_slug, domain, mode, dimensions_covered, total_dimensions,
1797
+ confidence, report_path, linked_squad_slug, created_at, updated_at)
1798
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1799
+ `).run(
1800
+ slug,
1801
+ String(options.domain || ''),
1802
+ String(options.mode || 'full'),
1803
+ Number(options.dimensionsCovered) || 0,
1804
+ Number(options.totalDimensions) || 7,
1805
+ Number(options.confidence) || 0,
1806
+ options.reportPath || null,
1807
+ options.linkedSquadSlug || null,
1808
+ now,
1809
+ now
1810
+ );
1811
+ return slug;
1812
+ }
1813
+
1814
+ function listInvestigations(db) {
1815
+ return db.prepare(`
1816
+ SELECT * FROM squad_investigations ORDER BY created_at DESC
1817
+ `).all();
1818
+ }
1819
+
1820
+ function getInvestigation(db, slug) {
1821
+ return db.prepare(`
1822
+ SELECT * FROM squad_investigations WHERE investigation_slug = ?
1823
+ `).get(slug) || null;
1824
+ }
1825
+
1826
+ function linkInvestigation(db, investigationSlug, squadSlug) {
1827
+ const now = nowIso();
1828
+ const result = db.prepare(`
1829
+ UPDATE squad_investigations
1830
+ SET linked_squad_slug = ?, updated_at = ?
1831
+ WHERE investigation_slug = ?
1832
+ `).run(squadSlug, now, investigationSlug);
1833
+ return result.changes > 0;
1834
+ }
1835
+
1836
+ // --- Implementation Plans CRUD ---
1837
+
1838
+ function upsertImplementationPlan(db, options = {}) {
1839
+ const planId = options.planId || `plan-${slugify(options.projectName || 'proj')}-${Date.now()}`;
1840
+ const now = nowIso();
1841
+ db.prepare(`
1842
+ INSERT OR REPLACE INTO implementation_plans
1843
+ (plan_id, project_name, scope, feature_slug, status, classification,
1844
+ phases_total, phases_completed, source_artifacts, source_hash, created_at, updated_at)
1845
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1846
+ `).run(
1847
+ planId,
1848
+ options.projectName || null,
1849
+ options.scope || 'project',
1850
+ options.featureSlug || null,
1851
+ options.status || 'draft',
1852
+ options.classification || null,
1853
+ Number(options.phasesTotal) || 0,
1854
+ Number(options.phasesCompleted) || 0,
1855
+ options.sourceArtifacts ? JSON.stringify(options.sourceArtifacts) : null,
1856
+ options.sourceHash || null,
1857
+ now,
1858
+ now
1859
+ );
1860
+ return planId;
1861
+ }
1862
+
1863
+ function getImplementationPlan(db, planId) {
1864
+ return db.prepare(`
1865
+ SELECT * FROM implementation_plans WHERE plan_id = ?
1866
+ `).get(planId) || null;
1867
+ }
1868
+
1869
+ function listImplementationPlans(db) {
1870
+ return db.prepare(`
1871
+ SELECT * FROM implementation_plans ORDER BY created_at DESC
1872
+ `).all();
1873
+ }
1874
+
1875
+ function updateImplementationPlanStatus(db, planId, status) {
1876
+ const now = nowIso();
1877
+ const result = db.prepare(`
1878
+ UPDATE implementation_plans SET status = ?, updated_at = ? WHERE plan_id = ?
1879
+ `).run(status, now, planId);
1880
+ return result.changes > 0;
1881
+ }
1882
+
1883
+ function upsertPlanPhase(db, planId, phaseNumber, title, status) {
1884
+ db.prepare(`
1885
+ INSERT OR REPLACE INTO plan_phases (plan_id, phase_number, title, status, completed_at)
1886
+ VALUES (?, ?, ?, ?, ?)
1887
+ `).run(
1888
+ planId,
1889
+ phaseNumber,
1890
+ title,
1891
+ status || 'pending',
1892
+ status === 'completed' ? nowIso() : null
1893
+ );
1894
+ }
1895
+
1896
+ function updatePlanPhaseStatus(db, planId, phaseNumber, status, notes) {
1897
+ const now = nowIso();
1898
+ const result = db.prepare(`
1899
+ UPDATE plan_phases SET status = ?, completed_at = ?, notes = ?
1900
+ WHERE plan_id = ? AND phase_number = ?
1901
+ `).run(
1902
+ status,
1903
+ status === 'completed' ? now : null,
1904
+ notes || null,
1905
+ planId,
1906
+ phaseNumber
1907
+ );
1908
+ if (result.changes > 0 && status === 'completed') {
1909
+ db.prepare(`
1910
+ UPDATE implementation_plans
1911
+ SET phases_completed = (SELECT COUNT(*) FROM plan_phases WHERE plan_id = ? AND status = 'completed'),
1912
+ updated_at = ?
1913
+ WHERE plan_id = ?
1914
+ `).run(planId, now, planId);
1915
+ }
1916
+ return result.changes > 0;
1917
+ }
1918
+
1919
+ function getPlanPhases(db, planId) {
1920
+ return db.prepare(`
1921
+ SELECT * FROM plan_phases WHERE plan_id = ? ORDER BY phase_number
1922
+ `).all(planId);
1923
+ }
1924
+
1925
+ // --- Squad Execution Plans CRUD ---
1926
+
1927
+ function upsertSquadExecutionPlan(db, options = {}) {
1928
+ const planSlug = options.planSlug || `sqplan-${slugify(options.squadSlug || 'squad')}-${Date.now()}`;
1929
+ const now = nowIso();
1930
+ db.prepare(`
1931
+ INSERT OR REPLACE INTO squad_execution_plans
1932
+ (plan_slug, squad_slug, status, rounds_total, rounds_completed,
1933
+ based_on_blueprint, based_on_investigation, source_hash, created_at, updated_at)
1934
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1935
+ `).run(
1936
+ planSlug,
1937
+ options.squadSlug || '',
1938
+ options.status || 'draft',
1939
+ Number(options.roundsTotal) || 0,
1940
+ Number(options.roundsCompleted) || 0,
1941
+ options.basedOnBlueprint || null,
1942
+ options.basedOnInvestigation || null,
1943
+ options.sourceHash || null,
1944
+ now,
1945
+ now
1946
+ );
1947
+ return planSlug;
1948
+ }
1949
+
1950
+ function getSquadExecutionPlan(db, planSlug) {
1951
+ return db.prepare(`
1952
+ SELECT * FROM squad_execution_plans WHERE plan_slug = ?
1953
+ `).get(planSlug) || null;
1954
+ }
1955
+
1956
+ function getSquadExecutionPlanBySquad(db, squadSlug) {
1957
+ return db.prepare(`
1958
+ SELECT * FROM squad_execution_plans WHERE squad_slug = ? ORDER BY created_at DESC LIMIT 1
1959
+ `).get(squadSlug) || null;
1960
+ }
1961
+
1962
+ function listSquadExecutionPlans(db) {
1963
+ return db.prepare(`
1964
+ SELECT * FROM squad_execution_plans ORDER BY created_at DESC
1965
+ `).all();
1966
+ }
1967
+
1968
+ function updateSquadExecutionPlanStatus(db, planSlug, status) {
1969
+ const now = nowIso();
1970
+ const result = db.prepare(`
1971
+ UPDATE squad_execution_plans SET status = ?, updated_at = ? WHERE plan_slug = ?
1972
+ `).run(status, now, planSlug);
1973
+ return result.changes > 0;
1974
+ }
1975
+
1976
+ function upsertSquadPlanRound(db, planSlug, roundNumber, executorSlug, title, status) {
1977
+ db.prepare(`
1978
+ INSERT OR REPLACE INTO squad_plan_rounds (plan_slug, round_number, executor_slug, title, status, completed_at)
1979
+ VALUES (?, ?, ?, ?, ?, ?)
1980
+ `).run(
1981
+ planSlug,
1982
+ roundNumber,
1983
+ executorSlug,
1984
+ title,
1985
+ status || 'pending',
1986
+ status === 'completed' ? nowIso() : null
1987
+ );
1988
+ }
1989
+
1990
+ function updateSquadPlanRoundStatus(db, planSlug, roundNumber, status, notes) {
1991
+ const now = nowIso();
1992
+ const result = db.prepare(`
1993
+ UPDATE squad_plan_rounds SET status = ?, completed_at = ?, notes = ?
1994
+ WHERE plan_slug = ? AND round_number = ?
1995
+ `).run(
1996
+ status,
1997
+ status === 'completed' ? now : null,
1998
+ notes || null,
1999
+ planSlug,
2000
+ roundNumber
2001
+ );
2002
+ if (result.changes > 0 && status === 'completed') {
2003
+ db.prepare(`
2004
+ UPDATE squad_execution_plans
2005
+ SET rounds_completed = (SELECT COUNT(*) FROM squad_plan_rounds WHERE plan_slug = ? AND status = 'completed'),
2006
+ updated_at = ?
2007
+ WHERE plan_slug = ?
2008
+ `).run(planSlug, now, planSlug);
2009
+ }
2010
+ return result.changes > 0;
2011
+ }
2012
+
2013
+ function getSquadPlanRounds(db, planSlug) {
2014
+ return db.prepare(`
2015
+ SELECT * FROM squad_plan_rounds WHERE plan_slug = ? ORDER BY round_number
2016
+ `).all(planSlug);
2017
+ }
2018
+
2019
+ // --- Squad Learnings CRUD ---
2020
+
2021
+ function insertSquadLearning(db, options = {}) {
2022
+ const learningId = options.learningId || `sl-${slugify(options.squadSlug || 'squad')}-${Date.now()}`;
2023
+ const now = nowIso();
2024
+ db.prepare(`
2025
+ INSERT OR REPLACE INTO squad_learnings
2026
+ (learning_id, squad_slug, type, title, signal, confidence, frequency,
2027
+ last_reinforced, applies_to, file_path, promoted_to, status,
2028
+ source_session, evidence, created_at, updated_at)
2029
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2030
+ `).run(
2031
+ learningId,
2032
+ options.squadSlug || '',
2033
+ options.type || 'preference',
2034
+ options.title || '',
2035
+ options.signal || 'explicit',
2036
+ options.confidence || 'medium',
2037
+ Number(options.frequency) || 1,
2038
+ options.lastReinforced || now,
2039
+ options.appliesTo || 'squad',
2040
+ options.filePath || null,
2041
+ options.promotedTo || null,
2042
+ options.status || 'active',
2043
+ options.sourceSession || null,
2044
+ options.evidence || null,
2045
+ now,
2046
+ now
2047
+ );
2048
+ return learningId;
2049
+ }
2050
+
2051
+ function listSquadLearnings(db, squadSlug, statusFilter) {
2052
+ if (statusFilter) {
2053
+ return db.prepare(`
2054
+ SELECT * FROM squad_learnings WHERE squad_slug = ? AND status = ? ORDER BY created_at DESC
2055
+ `).all(squadSlug, statusFilter);
2056
+ }
2057
+ return db.prepare(`
2058
+ SELECT * FROM squad_learnings WHERE squad_slug = ? ORDER BY created_at DESC
2059
+ `).all(squadSlug);
2060
+ }
2061
+
2062
+ function getSquadLearning(db, learningId) {
2063
+ return db.prepare(`
2064
+ SELECT * FROM squad_learnings WHERE learning_id = ?
2065
+ `).get(learningId) || null;
2066
+ }
2067
+
2068
+ function updateSquadLearningStatus(db, learningId, status) {
2069
+ const now = nowIso();
2070
+ const result = db.prepare(`
2071
+ UPDATE squad_learnings SET status = ?, updated_at = ? WHERE learning_id = ?
2072
+ `).run(status, now, learningId);
2073
+ return result.changes > 0;
2074
+ }
2075
+
2076
+ function reinforceSquadLearning(db, learningId) {
2077
+ const now = nowIso();
2078
+ const result = db.prepare(`
2079
+ UPDATE squad_learnings SET frequency = frequency + 1, last_reinforced = ?, updated_at = ? WHERE learning_id = ?
2080
+ `).run(now, now, learningId);
2081
+ return result.changes > 0;
2082
+ }
2083
+
2084
+ function promoteSquadLearning(db, learningId, promotedTo) {
2085
+ const now = nowIso();
2086
+ const result = db.prepare(`
2087
+ UPDATE squad_learnings SET status = 'promoted', promoted_to = ?, updated_at = ? WHERE learning_id = ?
2088
+ `).run(promotedTo, now, learningId);
2089
+ return result.changes > 0;
2090
+ }
2091
+
2092
+ function archiveStaleSquadLearnings(db, squadSlug, staleDays) {
2093
+ const days = Number(staleDays) || 90;
2094
+ const cutoff = new Date(Date.now() - days * 86400000).toISOString();
2095
+ const result = db.prepare(`
2096
+ UPDATE squad_learnings SET status = 'stale', updated_at = datetime('now')
2097
+ WHERE squad_slug = ? AND status = 'active' AND last_reinforced < ?
2098
+ `).run(squadSlug, cutoff);
2099
+ return result.changes;
2100
+ }
2101
+
2102
+ function getSquadLearningStats(db, squadSlug) {
2103
+ return db.prepare(`
2104
+ SELECT type, status, COUNT(*) as count FROM squad_learnings
2105
+ WHERE squad_slug = ? GROUP BY type, status ORDER BY type, status
2106
+ `).all(squadSlug);
2107
+ }
2108
+
2109
+ // --- Project Learnings CRUD ---
2110
+
2111
+ function insertProjectLearning(db, options = {}) {
2112
+ const learningId = options.learningId || `pl-${slugify(options.projectName || 'proj')}-${Date.now()}`;
2113
+ const now = nowIso();
2114
+ db.prepare(`
2115
+ INSERT OR REPLACE INTO project_learnings
2116
+ (learning_id, project_name, feature_slug, type, title, confidence, frequency,
2117
+ last_reinforced, applies_to, promoted_to, status,
2118
+ source_session, evidence, created_at, updated_at)
2119
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2120
+ `).run(
2121
+ learningId,
2122
+ options.projectName || null,
2123
+ options.featureSlug || null,
2124
+ options.type || 'preference',
2125
+ options.title || '',
2126
+ options.confidence || 'medium',
2127
+ Number(options.frequency) || 1,
2128
+ options.lastReinforced || now,
2129
+ options.appliesTo || 'project',
2130
+ options.promotedTo || null,
2131
+ options.status || 'active',
2132
+ options.sourceSession || null,
2133
+ options.evidence || null,
2134
+ now,
2135
+ now
2136
+ );
2137
+ return learningId;
2138
+ }
2139
+
2140
+ function listProjectLearnings(db, statusFilter) {
2141
+ if (statusFilter) {
2142
+ return db.prepare(`
2143
+ SELECT * FROM project_learnings WHERE status = ? ORDER BY created_at DESC
2144
+ `).all(statusFilter);
2145
+ }
2146
+ return db.prepare(`
2147
+ SELECT * FROM project_learnings ORDER BY created_at DESC
2148
+ `).all();
2149
+ }
2150
+
2151
+ function getProjectLearning(db, learningId) {
2152
+ return db.prepare(`
2153
+ SELECT * FROM project_learnings WHERE learning_id = ?
2154
+ `).get(learningId) || null;
2155
+ }
2156
+
2157
+ function updateProjectLearningStatus(db, learningId, status) {
2158
+ const now = nowIso();
2159
+ const result = db.prepare(`
2160
+ UPDATE project_learnings SET status = ?, updated_at = ? WHERE learning_id = ?
2161
+ `).run(status, now, learningId);
2162
+ return result.changes > 0;
2163
+ }
2164
+
2165
+ function reinforceProjectLearning(db, learningId) {
2166
+ const now = nowIso();
2167
+ const result = db.prepare(`
2168
+ UPDATE project_learnings SET frequency = frequency + 1, last_reinforced = ?, updated_at = ? WHERE learning_id = ?
2169
+ `).run(now, now, learningId);
2170
+ return result.changes > 0;
2171
+ }
2172
+
2173
+ function promoteProjectLearning(db, learningId, promotedTo) {
2174
+ const now = nowIso();
2175
+ const result = db.prepare(`
2176
+ UPDATE project_learnings SET status = 'promoted', promoted_to = ?, updated_at = ? WHERE learning_id = ?
2177
+ `).run(promotedTo, now, learningId);
2178
+ return result.changes > 0;
2179
+ }
2180
+
2181
+ function getProjectLearningStats(db) {
2182
+ return db.prepare(`
2183
+ SELECT type, status, COUNT(*) as count FROM project_learnings
2184
+ GROUP BY type, status ORDER BY type, status
2185
+ `).all();
2186
+ }
2187
+
1490
2188
  module.exports = {
1491
2189
  resolveRuntimePaths,
1492
2190
  runtimeStoreExists,
@@ -1505,6 +2203,7 @@ module.exports = {
1505
2203
  appendRunEvent,
1506
2204
  logAgentEvent,
1507
2205
  readAgentSession,
2206
+ writeAgentSession,
1508
2207
  clearAgentSession,
1509
2208
  // Pipeline CRUD
1510
2209
  upsertPipeline,
@@ -1523,5 +2222,44 @@ module.exports = {
1523
2222
  listArtisanSquads,
1524
2223
  deleteArtisanSquad,
1525
2224
  addArtisanMessage,
1526
- getArtisanMessages
2225
+ getArtisanMessages,
2226
+ // Investigation CRUD
2227
+ insertInvestigation,
2228
+ listInvestigations,
2229
+ getInvestigation,
2230
+ linkInvestigation,
2231
+ // Implementation Plans CRUD
2232
+ upsertImplementationPlan,
2233
+ getImplementationPlan,
2234
+ listImplementationPlans,
2235
+ updateImplementationPlanStatus,
2236
+ upsertPlanPhase,
2237
+ updatePlanPhaseStatus,
2238
+ getPlanPhases,
2239
+ // Squad Execution Plans CRUD
2240
+ upsertSquadExecutionPlan,
2241
+ getSquadExecutionPlan,
2242
+ getSquadExecutionPlanBySquad,
2243
+ listSquadExecutionPlans,
2244
+ updateSquadExecutionPlanStatus,
2245
+ upsertSquadPlanRound,
2246
+ updateSquadPlanRoundStatus,
2247
+ getSquadPlanRounds,
2248
+ // Squad Learnings CRUD
2249
+ insertSquadLearning,
2250
+ listSquadLearnings,
2251
+ getSquadLearning,
2252
+ updateSquadLearningStatus,
2253
+ reinforceSquadLearning,
2254
+ promoteSquadLearning,
2255
+ archiveStaleSquadLearnings,
2256
+ getSquadLearningStats,
2257
+ // Project Learnings CRUD
2258
+ insertProjectLearning,
2259
+ listProjectLearnings,
2260
+ getProjectLearning,
2261
+ updateProjectLearningStatus,
2262
+ reinforceProjectLearning,
2263
+ promoteProjectLearning,
2264
+ getProjectLearningStats
1527
2265
  };