@jaimevalasek/aioson 1.21.3 → 1.21.4

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 (134) hide show
  1. package/CHANGELOG.md +1 -1
  2. package/package.json +1 -1
  3. package/src/agents.js +23 -22
  4. package/src/cli.js +43 -20
  5. package/src/commands/agent-audit.js +189 -119
  6. package/src/commands/artifact-validate.js +31 -14
  7. package/src/commands/context-health.js +170 -34
  8. package/src/commands/devlog-process.js +35 -13
  9. package/src/commands/learning.js +98 -19
  10. package/src/commands/live.js +48 -22
  11. package/src/commands/preflight.js +16 -7
  12. package/src/commands/quality-audit.js +119 -0
  13. package/src/commands/skill-audit.js +200 -0
  14. package/src/commands/squad-playbook.js +100 -0
  15. package/src/commands/squad-role-scan.js +188 -0
  16. package/src/commands/state-save.js +9 -7
  17. package/src/commands/workflow-execute.js +172 -32
  18. package/src/commands/workflow-next.js +148 -40
  19. package/src/commands/workflow-status.js +54 -22
  20. package/src/handoff-contract.js +11 -6
  21. package/src/i18n/messages/en.js +13 -7
  22. package/src/i18n/messages/es.js +7 -5
  23. package/src/i18n/messages/fr.js +7 -5
  24. package/src/i18n/messages/pt-BR.js +13 -7
  25. package/src/learning-import-claude.js +218 -0
  26. package/src/learning-loop-engine.js +268 -254
  27. package/src/learning-loop-migration.js +177 -163
  28. package/src/learning-materialize.js +192 -0
  29. package/src/lib/quality/provider.js +132 -0
  30. package/src/lib/quality/report.js +82 -0
  31. package/src/lib/quality/result.js +185 -0
  32. package/src/parser.js +5 -4
  33. package/src/preflight-engine.js +49 -22
  34. package/src/runtime-store.js +2 -1
  35. package/template/.aioson/agents/analyst.md +18 -6
  36. package/template/.aioson/agents/committer.md +5 -5
  37. package/template/.aioson/agents/copywriter.md +27 -27
  38. package/template/.aioson/agents/dev.md +58 -39
  39. package/template/.aioson/agents/deyvin.md +43 -32
  40. package/template/.aioson/agents/discovery-design-doc.md +27 -13
  41. package/template/.aioson/agents/genome.md +81 -82
  42. package/template/.aioson/agents/manifests/dev.manifest.json +5 -4
  43. package/template/.aioson/agents/manifests/deyvin.manifest.json +4 -3
  44. package/template/.aioson/agents/neo.md +1 -1
  45. package/template/.aioson/agents/orchestrator.md +1 -1
  46. package/template/.aioson/agents/pentester.md +2 -2
  47. package/template/.aioson/agents/product.md +27 -19
  48. package/template/.aioson/agents/qa.md +4 -4
  49. package/template/.aioson/agents/setup.md +1 -1
  50. package/template/.aioson/agents/site-forge.md +17 -19
  51. package/template/.aioson/agents/squad.md +4 -0
  52. package/template/.aioson/agents/tester.md +178 -153
  53. package/template/.aioson/agents/ux-ui.md +1 -1
  54. package/template/.aioson/config.md +12 -12
  55. package/template/.aioson/context/design-doc.md +136 -136
  56. package/template/.aioson/context/project-map.md +7 -5
  57. package/template/.aioson/context/seeds/seed-example.md +27 -27
  58. package/template/.aioson/context/user-profile.md +42 -42
  59. package/template/.aioson/design-docs/agent-loading-contract.md +117 -138
  60. package/template/.aioson/docs/dev/simple-plan-lane.md +92 -0
  61. package/template/.aioson/docs/product/conversation-playbook.md +15 -17
  62. package/template/.aioson/docs/site-forge-build.md +2 -2
  63. package/template/.aioson/docs/site-forge-recon.md +5 -5
  64. package/template/.aioson/docs/squad/creation-flow.md +55 -0
  65. package/template/.aioson/docs/squad/eval-gate.md +79 -0
  66. package/template/.aioson/docs/squad/package-contract.md +39 -6
  67. package/template/.aioson/docs/squad/persona-grounding.md +62 -0
  68. package/template/.aioson/docs/squad/quality-lens.md +12 -1
  69. package/template/.aioson/genomes/INDEX.md +37 -37
  70. package/template/.aioson/genomes/copywriting/references/application-notes.md +2 -2
  71. package/template/.aioson/genomes/copywriting/references/frameworks/pms-research.md +1 -1
  72. package/template/.aioson/genomes/copywriting-brunson/references/application-notes.md +2 -2
  73. package/template/.aioson/learnings/gotchas/.gitkeep +1 -0
  74. package/template/.aioson/learnings/recipes/.gitkeep +1 -0
  75. package/template/.aioson/rules/agent-language-policy.md +21 -21
  76. package/template/.aioson/rules/agent-structural-contract.md +2 -2
  77. package/template/.aioson/rules/aioson-context-boundary.md +8 -6
  78. package/template/.aioson/rules/canonical-path-contract.md +10 -5
  79. package/template/.aioson/rules/data-format-convention.md +11 -11
  80. package/template/.aioson/rules/disk-first-artifacts.md +5 -4
  81. package/template/.aioson/rules/prd-section-ownership.md +12 -12
  82. package/template/.aioson/rules/simple-plan-lane.md +48 -0
  83. package/template/.aioson/rules/spec-level-ownership.md +5 -4
  84. package/template/.aioson/schemas/squad-blueprint.schema.json +32 -11
  85. package/template/.aioson/schemas/squad-manifest.schema.json +29 -8
  86. package/template/.aioson/skills/design/clean-saas-ui/SKILL.md +4 -4
  87. package/template/.aioson/skills/design/clean-saas-ui/references/art-direction.md +30 -30
  88. package/template/.aioson/skills/design/clean-saas-ui/references/motion.md +4 -4
  89. package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +2 -2
  90. package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +1 -1
  91. package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +1 -1
  92. package/template/.aioson/skills/design/neo-brutalist-ui/SKILL.md +5 -5
  93. package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +2 -2
  94. package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +4 -4
  95. package/template/.aioson/skills/design-system/dashboards/SKILL.md +5 -5
  96. package/template/.aioson/skills/design-system/patterns/SKILL.md +1 -1
  97. package/template/.aioson/skills/marketing/references/cta-matrix.md +43 -43
  98. package/template/.aioson/skills/marketing/references/headline-matrix.md +33 -33
  99. package/template/.aioson/skills/marketing/references/market-intelligence.md +2 -2
  100. package/template/.aioson/skills/marketing/references/platform-constraints.md +2 -2
  101. package/template/.aioson/skills/marketing/references/pms-research.md +3 -3
  102. package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +7 -7
  103. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +13 -11
  104. package/template/.aioson/skills/process/aioson-spec-driven/references/ui-language.md +85 -75
  105. package/template/.aioson/skills/process/decision-presentation/SKILL.md +11 -11
  106. package/template/.aioson/skills/process/decision-presentation/references/jargon-map.pt-BR.yaml +4 -4
  107. package/template/.aioson/skills/squad/references/executor-archetypes.md +77 -2
  108. package/template/.aioson/skills/static/harness-validate/SKILL.md +55 -46
  109. package/template/.aioson/skills/static/react-motion-patterns.md +1 -1
  110. package/template/.aioson/skills/static/static-html-patterns.md +2 -2
  111. package/template/.aioson/skills/static/threejs-patterns.md +2 -2
  112. package/template/.aioson/tasks/implementation-plan.md +325 -327
  113. package/template/.aioson/tasks/squad-analyze.md +93 -83
  114. package/template/.aioson/tasks/squad-create.md +156 -148
  115. package/template/.aioson/tasks/squad-design.md +223 -206
  116. package/template/.aioson/tasks/squad-eval.md +72 -0
  117. package/template/.aioson/tasks/squad-execution-plan.md +279 -279
  118. package/template/.aioson/tasks/squad-export.md +20 -20
  119. package/template/.aioson/tasks/squad-extend.md +73 -68
  120. package/template/.aioson/tasks/squad-investigate.md +57 -57
  121. package/template/.aioson/tasks/squad-pipeline.md +122 -122
  122. package/template/.aioson/tasks/squad-profile.md +48 -48
  123. package/template/.aioson/tasks/squad-refresh.md +242 -236
  124. package/template/.aioson/tasks/squad-repair.md +85 -85
  125. package/template/.aioson/tasks/squad-review.md +61 -61
  126. package/template/.aioson/tasks/squad-task-decompose.md +66 -66
  127. package/template/.aioson/tasks/squad-validate.md +65 -58
  128. package/template/.aioson/templates/squads/content-basic/template.json +1 -1
  129. package/template/.aioson/templates/squads/media-channel/template.json +1 -1
  130. package/template/.aioson/templates/squads/research-analysis/template.json +1 -1
  131. package/template/AGENTS.md +10 -6
  132. package/template/CLAUDE.md +10 -6
  133. package/template/OPENCODE.md +9 -5
  134. package/template/agents/_shared/learning-capture-directive.md +88 -0
package/CHANGELOG.md CHANGED
@@ -29,7 +29,7 @@ All notable changes to this project will be documented in this file.
29
29
  ### Fixed
30
30
  - **`memory:reflect-commit --dry-run` is now non-destructive.** The command never read the `--dry-run` flag, so a "dry run" silently performed the full destructive commit — it wrote the bootstrap files **and** unlinked the single-use manifest, leaving the flow unrecoverable (`missing_manifest`) on the next call. `--dry-run` now runs validation + path containment exactly like a real commit, then returns `{ ok: true, dryRun: true, would_write: [...] }` without writing any file or consuming the manifest, so a real commit can still follow. Regression coverage in `tests/memory-reflect-commit-dry-run.test.js`. Note: the reflect manifest remains single-use — a successful real commit consumes it (re-run by re-running `memory:reflect-prepare`).
31
31
 
32
- ## [1.21.0] - 2026-06-XX
32
+ ## [1.21.0] - 2026-05-28
33
33
 
34
34
  ### Added
35
35
  - **Gemini CLI deprecation warning (gemini-phaseout Phase 1).** Google announced (2026-05-20) that the Gemini CLI free/personal tier ends 2026-06-18.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaimevalasek/aioson",
3
- "version": "1.21.3",
3
+ "version": "1.21.4",
4
4
  "description": "AI operating framework for hyper-personalized software.",
5
5
  "keywords": [
6
6
  "ai",
package/src/agents.js CHANGED
@@ -29,14 +29,15 @@ function buildAgentPrompt(agent, tool, options = {}) {
29
29
  const safeTool = String(tool || 'codex').toLowerCase();
30
30
  const instructionPath = options.instructionPath || agent.path;
31
31
  const targetDir = options.targetDir ? String(options.targetDir) : '.';
32
- const interactionLanguage = String(options.interactionLanguage || 'en');
33
- const autonomyMode = String(options.autonomyMode || '').trim();
34
- const capabilitySummary = String(options.capabilitySummary || '').trim();
35
- const activationContext = String(options.activationContext || '').trim();
36
- const dependencyText =
37
- agent.dependsOn.length > 0
38
- ? `Check required context files first: ${agent.dependsOn.join(', ')}.`
39
- : 'No prerequisite context files are required.';
32
+ const interactionLanguage = String(options.interactionLanguage || 'en');
33
+ const autonomyMode = String(options.autonomyMode || '').trim();
34
+ const capabilitySummary = String(options.capabilitySummary || '').trim();
35
+ const activationContext = String(options.activationContext || '').trim();
36
+ const dependsOn = Array.isArray(options.dependsOn) ? options.dependsOn : agent.dependsOn;
37
+ const dependencyText =
38
+ dependsOn.length > 0
39
+ ? `Check required context files first: ${dependsOn.join(', ')}.`
40
+ : 'No prerequisite context files are required.';
40
41
  const activationBlock = activationContext
41
42
  ? [
42
43
  '',
@@ -68,20 +69,20 @@ function buildAgentPrompt(agent, tool, options = {}) {
68
69
  `**Scope boundary:** You operate exclusively as ${agent.command}. Do not perform work that belongs to another agent. When your work is complete, output only the handoff — which agent is next and why. Do not continue into that agent\'s territory.`,
69
70
  ].join('\n');
70
71
 
71
- if (safeTool === 'claude') {
72
- return `Read ${instructionPath} and execute ${agent.command}. ${dependencyText}${activationBlock} Write output to ${agent.output}.${autonomyBlock}${lifecycleBlock}`;
73
- }
74
-
75
- if (safeTool === 'gemini') {
76
- return `Run the Gemini command mapped to ${instructionPath} and execute ${agent.command}. ${dependencyText}${activationBlock} Save result to ${agent.output}.${autonomyBlock}${lifecycleBlock}`;
77
- }
78
-
79
- if (safeTool === 'opencode') {
80
- return `Use agent "${agent.id}" from ${instructionPath}. ${dependencyText}${activationBlock} Save output to ${agent.output}.${autonomyBlock}${lifecycleBlock}`;
81
- }
82
-
83
- return `Read AGENTS.md and execute ${agent.command} using ${instructionPath}. ${dependencyText}${activationBlock} Save output to ${agent.output}.${autonomyBlock}${lifecycleBlock}`;
84
- }
72
+ if (safeTool === 'claude') {
73
+ return `Read ${instructionPath} and execute ${agent.command}. ${dependencyText}${activationBlock}\n\nWrite output to ${agent.output}.${autonomyBlock}${lifecycleBlock}`;
74
+ }
75
+
76
+ if (safeTool === 'gemini') {
77
+ return `Run the Gemini command mapped to ${instructionPath} and execute ${agent.command}. ${dependencyText}${activationBlock}\n\nSave result to ${agent.output}.${autonomyBlock}${lifecycleBlock}`;
78
+ }
79
+
80
+ if (safeTool === 'opencode') {
81
+ return `Use agent "${agent.id}" from ${instructionPath}. ${dependencyText}${activationBlock}\n\nSave output to ${agent.output}.${autonomyBlock}${lifecycleBlock}`;
82
+ }
83
+
84
+ return `Read AGENTS.md and execute ${agent.command} using ${instructionPath}. ${dependencyText}${activationBlock}\n\nSave output to ${agent.output}.${autonomyBlock}${lifecycleBlock}`;
85
+ }
85
86
 
86
87
  module.exports = {
87
88
  normalizeAgentName,
package/src/cli.js CHANGED
@@ -58,6 +58,8 @@ const { runSquadStatus } = require('./commands/squad-status');
58
58
  const { runSquadDoctor } = require('./commands/squad-doctor');
59
59
  const { runSquadRepairGenomes } = require('./commands/squad-repair-genomes');
60
60
  const { runSquadValidate } = require('./commands/squad-validate');
61
+ const { runSquadRoleScan } = require('./commands/squad-role-scan');
62
+ const { runSquadPlaybook } = require('./commands/squad-playbook');
61
63
  const { runSquadExport } = require('./commands/squad-export');
62
64
  const { runSquadPipeline } = require('./commands/squad-pipeline');
63
65
  const { runSquadAgentCreate } = require('./commands/squad-agent-create');
@@ -80,10 +82,12 @@ const { runSquadWebhook } = require('./commands/squad-webhook');
80
82
  const { runSquadBus } = require('./commands/squad-bus');
81
83
  const { runSquadAutorun } = require('./commands/squad-autorun');
82
84
  const { runSquadDependencyGraph } = require('./commands/squad-dependency-graph');
83
- const { runSquadToolRegister } = require('./commands/squad-tool-register');
84
- const { runSquadReview } = require('./commands/squad-review');
85
- const { runAgentAudit } = require('./commands/agent-audit');
86
- const { runBriefGen } = require('./commands/brief-gen');
85
+ const { runSquadToolRegister } = require('./commands/squad-tool-register');
86
+ const { runSquadReview } = require('./commands/squad-review');
87
+ const { runAgentAudit } = require('./commands/agent-audit');
88
+ const { runSkillAudit } = require('./commands/skill-audit');
89
+ const { runQualityAudit } = require('./commands/quality-audit');
90
+ const { runBriefGen } = require('./commands/brief-gen');
87
91
  const { runHarnessInit, runHarnessValidate, runHarnessApplyValidation } = require('./commands/harness');
88
92
  const { runVerifyGate } = require('./commands/verify-gate');
89
93
  const {
@@ -325,6 +329,10 @@ const JSON_SUPPORTED_COMMANDS = new Set([
325
329
  'squad-repair-genomes',
326
330
  'squad:validate',
327
331
  'squad-validate',
332
+ 'squad:role-scan',
333
+ 'squad-role-scan',
334
+ 'squad:playbook',
335
+ 'squad-playbook',
328
336
  'squad:export',
329
337
  'squad-export',
330
338
  'squad:pipeline',
@@ -376,9 +384,13 @@ const JSON_SUPPORTED_COMMANDS = new Set([
376
384
  'squad-tool-register',
377
385
  'squad:review',
378
386
  'squad-review',
379
- 'agent:audit',
380
- 'agent-audit',
381
- 'brief:gen',
387
+ 'agent:audit',
388
+ 'agent-audit',
389
+ 'skill:audit',
390
+ 'skill-audit',
391
+ 'quality:audit',
392
+ 'quality-audit',
393
+ 'brief:gen',
382
394
  'harness:init',
383
395
  'harness-init',
384
396
  'harness:validate',
@@ -812,9 +824,11 @@ function printHelp(t, logger) {
812
824
  logHelpLine(t, logger, 'cli.help_squad_daemon');
813
825
  logHelpLine(t, logger, 'cli.help_squad_mcp');
814
826
  logHelpLine(t, logger, 'cli.help_squad_roi');
815
- logHelpLine(t, logger, 'cli.help_squad_score');
816
- logHelpLine(t, logger, 'cli.help_squad_learning');
817
- logHelpLine(t, logger, 'cli.help_learning');
827
+ logHelpLine(t, logger, 'cli.help_squad_score');
828
+ logHelpLine(t, logger, 'cli.help_squad_learning');
829
+ logHelpLine(t, logger, 'cli.help_agent_audit');
830
+ logHelpLine(t, logger, 'cli.help_quality_audit');
831
+ logHelpLine(t, logger, 'cli.help_learning');
818
832
  logHelpLine(t, logger, 'cli.help_runtime_init');
819
833
  logHelpLine(t, logger, 'cli.help_runtime_ingest');
820
834
  logHelpLine(t, logger, 'cli.help_runtime_task_start');
@@ -837,9 +851,10 @@ function printHelp(t, logger) {
837
851
  logHelpLine(t, logger, 'cli.help_scaffold_complete');
838
852
  logHelpLine(t, logger, 'cli.help_runtime_backup');
839
853
  logHelpLine(t, logger, 'cli.help_runtime_restore');
840
- logHelpLine(t, logger, 'cli.help_skill_install');
841
- logHelpLine(t, logger, 'cli.help_skill_list');
842
- logHelpLine(t, logger, 'cli.help_skill_remove');
854
+ logHelpLine(t, logger, 'cli.help_skill_install');
855
+ logHelpLine(t, logger, 'cli.help_skill_list');
856
+ logHelpLine(t, logger, 'cli.help_skill_remove');
857
+ logHelpLine(t, logger, 'cli.help_skill_audit');
843
858
  logHelpLine(t, logger, 'cli.help_design_hybrid_options');
844
859
  logHelpLine(t, logger, 'cli.help_cloud_import_squad');
845
860
  logHelpLine(t, logger, 'cli.help_cloud_import_genome');
@@ -1164,6 +1179,10 @@ async function main() {
1164
1179
  result = await runSquadRepairGenomes({ args, options, logger: commandLogger, t });
1165
1180
  } else if (command === 'squad:validate' || command === 'squad-validate') {
1166
1181
  result = await runSquadValidate({ args, options, logger: commandLogger, t });
1182
+ } else if (command === 'squad:role-scan' || command === 'squad-role-scan') {
1183
+ result = await runSquadRoleScan({ args, options, logger: commandLogger });
1184
+ } else if (command === 'squad:playbook' || command === 'squad-playbook') {
1185
+ result = await runSquadPlaybook({ args, options, logger: commandLogger });
1167
1186
  } else if (command === 'squad:export' || command === 'squad-export') {
1168
1187
  result = await runSquadExport({ args, options, logger: commandLogger, t });
1169
1188
  } else if (command === 'squad:pipeline' || command === 'squad-pipeline') {
@@ -1216,10 +1235,14 @@ async function main() {
1216
1235
  result = await runSquadToolRegister({ args, options, logger: commandLogger });
1217
1236
  } else if (command === 'squad:review' || command === 'squad-review') {
1218
1237
  result = await runSquadReview({ args, options, logger: commandLogger });
1219
- } else if (command === 'agent:audit' || command === 'agent-audit') {
1220
- result = await runAgentAudit({ args, options, logger: commandLogger });
1221
- } else if (command === 'brief:gen' || command === 'brief-gen') {
1222
- result = await runBriefGen({ args, options, logger: commandLogger, t });
1238
+ } else if (command === 'agent:audit' || command === 'agent-audit') {
1239
+ result = await runAgentAudit({ args, options, logger: commandLogger });
1240
+ } else if (command === 'skill:audit' || command === 'skill-audit') {
1241
+ result = await runSkillAudit({ args, options, logger: commandLogger });
1242
+ } else if (command === 'quality:audit' || command === 'quality-audit') {
1243
+ result = await runQualityAudit({ args, options, logger: commandLogger });
1244
+ } else if (command === 'brief:gen' || command === 'brief-gen') {
1245
+ result = await runBriefGen({ args, options, logger: commandLogger, t });
1223
1246
  } else if (command === 'harness:init' || command === 'harness-init') {
1224
1247
  result = await runHarnessInit({ args, options, logger: commandLogger, t });
1225
1248
  } else if (command === 'harness:validate' || command === 'harness-validate') {
@@ -1268,9 +1291,9 @@ async function main() {
1268
1291
  result = await runSpecCheckpoint({ args, options, logger: commandLogger });
1269
1292
  } else if (command === 'spec:tasks' || command === 'spec-tasks') {
1270
1293
  result = await runSpecTasks({ args, options, logger: commandLogger });
1271
- } else if (command.startsWith('learning:') || command === 'learning') {
1272
- const sub = command === 'learning' ? (args[1] || 'list') : command.split(':')[1];
1273
- result = await runLearning({ args, options: { ...options, sub }, logger: commandLogger, t });
1294
+ } else if (command.startsWith('learning:') || command === 'learning') {
1295
+ const sub = command === 'learning' ? (options.sub || args[1] || 'list') : command.split(':')[1];
1296
+ result = await runLearning({ args, options: { ...options, sub }, logger: commandLogger, t });
1274
1297
  } else if (command.startsWith('plan:') || command === 'plan') {
1275
1298
  const sub = command === 'plan' ? (args[1] || 'show') : command.split(':')[1];
1276
1299
  result = await runImplementationPlan({ args, options: { ...options, sub }, logger: commandLogger, t });
@@ -19,8 +19,11 @@
19
19
  *
20
20
  * Usage:
21
21
  * aioson agent:audit .
22
- * aioson agent:audit . --verbose Show per-section breakdown
23
- * aioson agent:audit . --locales Include locale variant files
22
+ * aioson agent:audit . --verbose Show per-section breakdown
23
+ * aioson agent:audit . --locales Include locale variant files
24
+ * aioson agent:audit . --runtime-only Scan project/runtime surfaces only
25
+ * aioson agent:audit . --template-only Scan template surfaces only
26
+ * aioson agent:audit . --inception Scan project and template surfaces
24
27
  * aioson agent:audit . --fix Write savings report to .aioson/docs/agent-audit.md
25
28
  * aioson agent:audit . --json
26
29
  */
@@ -117,7 +120,7 @@ function parseSections(content) {
117
120
 
118
121
  // ─── File scanner ─────────────────────────────────────────────────────────────
119
122
 
120
- async function scanAgentFile(filePath, relativePath) {
123
+ async function scanAgentFile(filePath, relativePath, category = 'workspace_agent') {
121
124
  let content;
122
125
  try {
123
126
  content = await fs.readFile(filePath, 'utf8');
@@ -140,10 +143,11 @@ async function scanAgentFile(filePath, relativePath) {
140
143
  ? 'over_target'
141
144
  : 'ok';
142
145
 
143
- return {
144
- file: relativePath,
145
- slug,
146
- agent_type: typeDef.type,
146
+ return {
147
+ file: relativePath,
148
+ slug,
149
+ category,
150
+ agent_type: typeDef.type,
147
151
  chars,
148
152
  tokens: estimateTokens(chars),
149
153
  target_chars: typeDef.target,
@@ -156,7 +160,7 @@ async function scanAgentFile(filePath, relativePath) {
156
160
  };
157
161
  }
158
162
 
159
- async function scanDir(dirPath, projectDir, results) {
163
+ async function scanDir(dirPath, projectDir, results, category = 'workspace_agent') {
160
164
  let entries;
161
165
  try {
162
166
  entries = await fs.readdir(dirPath, { withFileTypes: true });
@@ -166,12 +170,70 @@ async function scanDir(dirPath, projectDir, results) {
166
170
 
167
171
  for (const entry of entries) {
168
172
  if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
169
- const filePath = path.join(dirPath, entry.name);
170
- const rel = path.relative(projectDir, filePath).split(path.sep).join('/');
171
- const result = await scanAgentFile(filePath, rel);
172
- if (result) results.push(result);
173
- }
174
- }
173
+ const filePath = path.join(dirPath, entry.name);
174
+ const rel = path.relative(projectDir, filePath).split(path.sep).join('/');
175
+ const result = await scanAgentFile(filePath, rel, category);
176
+ if (result) results.push(result);
177
+ }
178
+ }
179
+
180
+ function normalizeRel(projectDir, filePath) {
181
+ return path.relative(projectDir, filePath).split(path.sep).join('/');
182
+ }
183
+
184
+ function getAuditMode(options) {
185
+ const selected = [
186
+ options['runtime-only'] ? 'runtime' : null,
187
+ options['template-only'] ? 'template' : null,
188
+ options.inception ? 'inception' : null
189
+ ].filter(Boolean);
190
+
191
+ if (selected.length > 1) {
192
+ return { error: 'conflicting_modes', selected };
193
+ }
194
+
195
+ return { mode: selected[0] || 'inception' };
196
+ }
197
+
198
+ function buildAgentRoots(targetDir, mode) {
199
+ const roots = [];
200
+
201
+ if (mode === 'runtime' || mode === 'inception') {
202
+ roots.push({
203
+ type: 'dir',
204
+ path: path.join(targetDir, '.aioson', 'agents'),
205
+ rel: '.aioson/agents',
206
+ category: 'workspace_agent'
207
+ });
208
+ for (const name of ['CLAUDE.md', 'AGENTS.md']) {
209
+ roots.push({
210
+ type: 'file',
211
+ path: path.join(targetDir, name),
212
+ rel: name,
213
+ category: 'auto_loaded'
214
+ });
215
+ }
216
+ }
217
+
218
+ if (mode === 'template' || mode === 'inception') {
219
+ roots.push({
220
+ type: 'dir',
221
+ path: path.join(targetDir, 'template', '.aioson', 'agents'),
222
+ rel: 'template/.aioson/agents',
223
+ category: 'template_agent'
224
+ });
225
+ for (const name of ['CLAUDE.md', 'AGENTS.md']) {
226
+ roots.push({
227
+ type: 'file',
228
+ path: path.join(targetDir, 'template', name),
229
+ rel: `template/${name}`,
230
+ category: 'auto_loaded'
231
+ });
232
+ }
233
+ }
234
+
235
+ return roots;
236
+ }
175
237
 
176
238
  // ─── Report writer ────────────────────────────────────────────────────────────
177
239
 
@@ -238,104 +300,98 @@ function buildMarkdownReport(files, projectDir) {
238
300
 
239
301
  // ─── Main command ─────────────────────────────────────────────────────────────
240
302
 
241
- async function runAgentAudit({ args, options = {}, logger }) {
242
- const targetDir = path.resolve(process.cwd(), args[0] || '.');
243
- const verbose = Boolean(options.verbose || options.v);
244
- const includeLocales = Boolean(options.locales);
245
- const writeFix = Boolean(options.fix);
246
-
247
- const agentsDir = path.join(targetDir, 'template', '.aioson', 'agents');
248
- const rootAgentsDir = path.join(targetDir, '.aioson', 'agents');
249
-
250
- const files = [];
251
-
252
- // Scan template agents (this project)
253
- if (await dirExists(agentsDir)) {
254
- await scanDir(agentsDir, targetDir, files);
255
- }
256
- // Scan project agents (when used inside a project)
257
- if (await dirExists(rootAgentsDir)) {
258
- await scanDir(rootAgentsDir, targetDir, files);
259
- }
260
- // Scan auto-loaded files
261
- for (const name of ['CLAUDE.md', 'AGENTS.md']) {
262
- for (const base of [path.join(targetDir, 'template'), targetDir]) {
263
- const fp = path.join(base, name);
264
- const rel = path.relative(targetDir, fp).split(path.sep).join('/');
265
- const r = await scanAgentFile(fp, rel);
266
- if (r) files.push(r);
267
- }
268
- }
269
-
270
- // Optionally include locales
271
- if (includeLocales) {
272
- const localesBase = path.join(targetDir, 'template', '.aioson', 'locales');
273
- try {
274
- const langs = await fs.readdir(localesBase, { withFileTypes: true });
275
- for (const lang of langs) {
276
- if (!lang.isDirectory()) continue;
277
- await scanDir(
278
- path.join(localesBase, lang.name, 'agents'),
279
- targetDir,
280
- files
281
- );
282
- }
283
- } catch { /* locales dir optional */ }
284
- }
285
-
286
- if (files.length === 0) {
287
- if (!options.json) logger.log('No agent files found. Run from the aioson project root or a project with .aioson/agents/.');
288
- return { ok: false, reason: 'no_files' };
289
- }
290
-
291
- // Sort by size descending
292
- files.sort((a, b) => b.chars - a.chars);
293
-
294
- if (options.json) return { ok: true, files };
295
-
296
- // ── Console report ─────────────────────────────────────────────────────────
297
- const overHard = files.filter((f) => f.status === 'over_hard');
298
- const overTarget = files.filter((f) => f.status === 'over_target');
299
- const totalTokens = files.reduce((s, f) => s + f.tokens, 0);
300
- const totalSavings = files.reduce((s, f) => s + f.savings_tokens, 0);
301
-
302
- logger.log('Agent Audit');
303
- logger.log('─'.repeat(70));
304
- logger.log(`Files scanned : ${files.length}`);
305
- logger.log(`Total tokens : ~${totalTokens.toLocaleString()} per session`);
306
- logger.log(`Over hard limit: ${overHard.length} Over target: ${overTarget.length}`);
307
- logger.log(`Potential save : ~${totalSavings.toLocaleString()} tokens/session (on-demand split)`);
308
- logger.log('');
309
-
310
- // File table
311
- const COL = { file: 45, type: 14, size: 9, tokens: 12, status: 8 };
312
- logger.log(
313
- 'File'.padEnd(COL.file) +
314
- 'Type'.padEnd(COL.type) +
315
- 'Size'.padEnd(COL.size) +
316
- 'Tokens'.padEnd(COL.tokens) +
317
- 'Status'
318
- );
319
- logger.log('─'.repeat(70));
320
-
321
- for (const f of files) {
322
- const statusLabel = { ok: '✓ ok', over_target: '⚠ target', over_hard: '✗ hard' }[f.status];
323
- logger.log(
324
- f.file.slice(0, COL.file - 1).padEnd(COL.file) +
325
- f.agent_type.padEnd(COL.type) +
326
- formatKb(f.chars).padEnd(COL.size) +
327
- formatTokens(f.chars).padEnd(COL.tokens) +
328
- statusLabel
329
- );
330
-
331
- if (verbose && f.sections.length > 0) {
332
- const topSections = [...f.sections].sort((a, b) => b.chars - a.chars).slice(0, 5);
333
- for (const s of topSections) {
334
- const flag = s.onDemandCandidate ? ' [on-demand candidate]' : '';
335
- logger.log(` ${'§ ' + s.title.slice(0, 40)} ${formatKb(s.chars)}${flag}`);
336
- }
337
- }
338
- }
303
+ async function runAgentAudit({ args, options = {}, logger }) {
304
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
305
+ const verbose = Boolean(options.verbose || options.v);
306
+ const includeLocales = Boolean(options.locales);
307
+ const writeFix = Boolean(options.fix);
308
+ const modeResult = getAuditMode(options);
309
+
310
+ if (modeResult.error) {
311
+ if (!options.json) logger.error('Choose only one audit mode: --runtime-only, --template-only, or --inception.');
312
+ return { ok: false, reason: modeResult.error, selected: modeResult.selected };
313
+ }
314
+
315
+ const mode = modeResult.mode;
316
+ const scanRoots = buildAgentRoots(targetDir, mode);
317
+
318
+ const files = [];
319
+
320
+ for (const root of scanRoots) {
321
+ if (root.type === 'dir') {
322
+ if (await dirExists(root.path)) await scanDir(root.path, targetDir, files, root.category);
323
+ continue;
324
+ }
325
+
326
+ const r = await scanAgentFile(root.path, normalizeRel(targetDir, root.path), root.category);
327
+ if (r) files.push(r);
328
+ }
329
+
330
+ // Optionally include locales
331
+ if (includeLocales && (mode === 'runtime' || mode === 'inception')) {
332
+ await scanLocaleAgents(path.join(targetDir, '.aioson', 'locales'), targetDir, files, 'workspace_agent');
333
+ }
334
+ if (includeLocales && (mode === 'template' || mode === 'inception')) {
335
+ await scanLocaleAgents(path.join(targetDir, 'template', '.aioson', 'locales'), targetDir, files, 'template_agent');
336
+ }
337
+
338
+ const roots = scanRoots.map((r) => r.rel);
339
+
340
+ if (files.length === 0) {
341
+ if (!options.json) logger.log('No agent files found. Run from the aioson project root or a project with .aioson/agents/.');
342
+ return { ok: false, reason: 'no_files', mode, roots };
343
+ }
344
+
345
+ // Sort by size descending
346
+ files.sort((a, b) => b.chars - a.chars);
347
+
348
+ if (options.json) return { ok: true, mode, roots, files };
349
+
350
+ // ── Console report ─────────────────────────────────────────────────────────
351
+ const overHard = files.filter((f) => f.status === 'over_hard');
352
+ const overTarget = files.filter((f) => f.status === 'over_target');
353
+ const totalTokens = files.reduce((s, f) => s + f.tokens, 0);
354
+ const totalSavings = files.reduce((s, f) => s + f.savings_tokens, 0);
355
+
356
+ logger.log('Agent Audit');
357
+ logger.log('─'.repeat(70));
358
+ logger.log(`Mode : ${mode}`);
359
+ logger.log(`Roots : ${roots.join(', ')}`);
360
+ logger.log(`Files scanned : ${files.length}`);
361
+ logger.log(`Total tokens : ~${totalTokens.toLocaleString()} per session`);
362
+ logger.log(`Over hard limit: ${overHard.length} Over target: ${overTarget.length}`);
363
+ logger.log(`Potential save : ~${totalSavings.toLocaleString()} tokens/session (on-demand split)`);
364
+ logger.log('');
365
+
366
+ // File table
367
+ const COL = { file: 45, type: 14, size: 9, tokens: 12, status: 8 };
368
+ logger.log(
369
+ 'File'.padEnd(COL.file) +
370
+ 'Type'.padEnd(COL.type) +
371
+ 'Size'.padEnd(COL.size) +
372
+ 'Tokens'.padEnd(COL.tokens) +
373
+ 'Status'
374
+ );
375
+ logger.log(''.repeat(70));
376
+
377
+ for (const f of files) {
378
+ const statusLabel = { ok: '✓ ok', over_target: '⚠ target', over_hard: '✗ hard' }[f.status];
379
+ logger.log(
380
+ f.file.slice(0, COL.file - 1).padEnd(COL.file) +
381
+ f.agent_type.padEnd(COL.type) +
382
+ formatKb(f.chars).padEnd(COL.size) +
383
+ formatTokens(f.chars).padEnd(COL.tokens) +
384
+ statusLabel
385
+ );
386
+
387
+ if (verbose && f.sections.length > 0) {
388
+ const topSections = [...f.sections].sort((a, b) => b.chars - a.chars).slice(0, 5);
389
+ for (const s of topSections) {
390
+ const flag = s.onDemandCandidate ? ' [on-demand candidate]' : '';
391
+ logger.log(` ${'§ ' + s.title.slice(0, 40)} ${formatKb(s.chars)}${flag}`);
392
+ }
393
+ }
394
+ }
339
395
 
340
396
  logger.log('');
341
397
 
@@ -375,15 +431,29 @@ async function runAgentAudit({ args, options = {}, logger }) {
375
431
  logger.log(' Run with --verbose to see per-section breakdown.');
376
432
  }
377
433
 
378
- return {
379
- ok: true,
380
- files: files.length,
381
- over_hard: overHard.length,
382
- over_target: overTarget.length,
383
- total_tokens: totalTokens,
434
+ return {
435
+ ok: true,
436
+ mode,
437
+ roots,
438
+ files: files.length,
439
+ over_hard: overHard.length,
440
+ over_target: overTarget.length,
441
+ total_tokens: totalTokens,
384
442
  potential_savings_tokens: totalSavings
385
443
  };
386
- }
444
+ }
445
+
446
+ async function scanLocaleAgents(localesBase, targetDir, files, category) {
447
+ try {
448
+ const langs = await fs.readdir(localesBase, { withFileTypes: true });
449
+ for (const lang of langs) {
450
+ if (!lang.isDirectory()) continue;
451
+ await scanDir(path.join(localesBase, lang.name, 'agents'), targetDir, files, category);
452
+ }
453
+ } catch {
454
+ // locales dir optional
455
+ }
456
+ }
387
457
 
388
458
  async function dirExists(dirPath) {
389
459
  try {
@@ -70,8 +70,9 @@ async function runArtifactValidate({ args, options = {}, logger }) {
70
70
  reqCount = `${new Set(reqs).size} REQs, ${new Set(acs).size} ACs`;
71
71
  }
72
72
 
73
- // Conformance required?
74
- const conformanceRequired = classification === 'MEDIUM';
73
+ // Conformance required?
74
+ const conformanceRequired = classification === 'MEDIUM';
75
+ const designDocRequired = classification === 'SMALL' || classification === 'MEDIUM';
75
76
 
76
77
  // Build chain items
77
78
  const chain = [
@@ -110,15 +111,29 @@ async function runArtifactValidate({ args, options = {}, logger }) {
110
111
  required: true,
111
112
  indent: 1
112
113
  },
113
- {
114
- name: 'architecture.md',
115
- exists: artifacts.architecture.exists,
116
- detail: null,
117
- required: true,
118
- indent: 1
119
- },
120
- {
121
- name: `implementation-plan-${slug}.md`,
114
+ {
115
+ name: 'architecture.md',
116
+ exists: artifacts.architecture.exists,
117
+ detail: null,
118
+ required: true,
119
+ indent: 1
120
+ },
121
+ {
122
+ name: 'design-doc.md',
123
+ exists: artifacts.design_doc.exists,
124
+ detail: designDocRequired ? 'pre-dev design governance contract' : `SMALL/MEDIUM only — NOT required for ${classification || 'MICRO'}`,
125
+ required: designDocRequired,
126
+ indent: 1
127
+ },
128
+ {
129
+ name: 'readiness.md',
130
+ exists: artifacts.readiness.exists,
131
+ detail: designDocRequired ? 'pre-dev readiness contract' : `SMALL/MEDIUM only — NOT required for ${classification || 'MICRO'}`,
132
+ required: designDocRequired,
133
+ indent: 1
134
+ },
135
+ {
136
+ name: `implementation-plan-${slug}.md`,
122
137
  exists: artifacts.implementation_plan.exists,
123
138
  detail: planStatus ? `status: ${planStatus}` : null,
124
139
  required: true,
@@ -143,9 +158,11 @@ async function runArtifactValidate({ args, options = {}, logger }) {
143
158
  const ARTIFACT_OWNER_MAP = {
144
159
  'project.context.md': { agent: '@setup', reason: 'setup not complete' },
145
160
  [`prd-${slug}.md`]: { agent: '@product', reason: 'PRD not produced yet' },
146
- [`requirements-${slug}.md`]: { agent: '@analyst', reason: 'requirements not produced yet (Gate A)' },
147
- 'architecture.md': { agent: '@architect', reason: 'architecture not produced yet (Gate B)' },
148
- [`implementation-plan-${slug}.md`]: { agent: '@pm', reason: 'implementation plan not produced yet (Gate C)' },
161
+ [`requirements-${slug}.md`]: { agent: '@analyst', reason: 'requirements not produced yet (Gate A)' },
162
+ 'architecture.md': { agent: '@architect', reason: 'architecture not produced yet (Gate B)' },
163
+ 'design-doc.md': { agent: '@discovery-design-doc', reason: 'design governance contract not produced yet' },
164
+ 'readiness.md': { agent: '@discovery-design-doc', reason: 'readiness contract not produced yet' },
165
+ [`implementation-plan-${slug}.md`]: { agent: '@pm', reason: 'implementation plan not produced yet (Gate C)' },
149
166
  [`spec-${slug}.md`]: { agent: '@analyst', reason: 'spec not produced yet — @analyst seeds the feature memory' },
150
167
  [`conformance-${slug}.yaml`]: { agent: '@analyst', reason: 'conformance contract missing — @analyst creates it for MEDIUM features' }
151
168
  };