@jaimevalasek/aioson 1.21.0 → 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 (148) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/docs/pt/living-memory/reflexao-in-harness.md +2 -0
  3. package/package.json +1 -1
  4. package/src/agents.js +23 -22
  5. package/src/cli.js +48 -20
  6. package/src/commands/agent-audit.js +189 -119
  7. package/src/commands/artifact-validate.js +31 -14
  8. package/src/commands/context-health.js +205 -36
  9. package/src/commands/devlog-process.js +35 -13
  10. package/src/commands/feature-close.js +36 -0
  11. package/src/commands/learning.js +98 -19
  12. package/src/commands/live.js +48 -22
  13. package/src/commands/memory-archive.js +193 -193
  14. package/src/commands/memory-reflect-commit.js +28 -4
  15. package/src/commands/memory-restore.js +177 -177
  16. package/src/commands/memory-search.js +135 -135
  17. package/src/commands/memory-trim.js +191 -0
  18. package/src/commands/preflight.js +16 -7
  19. package/src/commands/quality-audit.js +119 -0
  20. package/src/commands/skill-audit.js +200 -0
  21. package/src/commands/squad-playbook.js +100 -0
  22. package/src/commands/squad-role-scan.js +188 -0
  23. package/src/commands/state-save.js +9 -7
  24. package/src/commands/workflow-execute.js +172 -32
  25. package/src/commands/workflow-next.js +148 -40
  26. package/src/commands/workflow-status.js +54 -22
  27. package/src/constants.js +1 -0
  28. package/src/current-state-trim.js +170 -0
  29. package/src/handoff-contract.js +11 -6
  30. package/src/i18n/messages/en.js +25 -7
  31. package/src/i18n/messages/es.js +19 -5
  32. package/src/i18n/messages/fr.js +19 -5
  33. package/src/i18n/messages/pt-BR.js +25 -7
  34. package/src/learning-import-claude.js +218 -0
  35. package/src/learning-loop-engine.js +268 -254
  36. package/src/learning-loop-migration.js +177 -163
  37. package/src/learning-materialize.js +192 -0
  38. package/src/lib/quality/provider.js +132 -0
  39. package/src/lib/quality/report.js +82 -0
  40. package/src/lib/quality/result.js +185 -0
  41. package/src/memory-reflect-engine.js +10 -4
  42. package/src/parser.js +5 -4
  43. package/src/preflight-engine.js +49 -22
  44. package/src/runtime-store.js +2 -1
  45. package/template/.aioson/agents/analyst.md +18 -6
  46. package/template/.aioson/agents/architect.md +3 -0
  47. package/template/.aioson/agents/committer.md +6 -6
  48. package/template/.aioson/agents/copywriter.md +27 -27
  49. package/template/.aioson/agents/dev.md +60 -41
  50. package/template/.aioson/agents/deyvin.md +44 -32
  51. package/template/.aioson/agents/discovery-design-doc.md +27 -13
  52. package/template/.aioson/agents/genome.md +81 -82
  53. package/template/.aioson/agents/manifests/dev.manifest.json +5 -4
  54. package/template/.aioson/agents/manifests/deyvin.manifest.json +4 -3
  55. package/template/.aioson/agents/neo.md +1 -1
  56. package/template/.aioson/agents/orchestrator.md +1 -1
  57. package/template/.aioson/agents/pentester.md +3 -2
  58. package/template/.aioson/agents/product.md +27 -19
  59. package/template/.aioson/agents/qa.md +8 -4
  60. package/template/.aioson/agents/setup.md +1 -1
  61. package/template/.aioson/agents/sheldon.md +1 -0
  62. package/template/.aioson/agents/site-forge.md +17 -19
  63. package/template/.aioson/agents/squad.md +4 -0
  64. package/template/.aioson/agents/tester.md +180 -153
  65. package/template/.aioson/agents/ux-ui.md +1 -1
  66. package/template/.aioson/config/autonomy-protocol.json +1 -0
  67. package/template/.aioson/config.md +12 -12
  68. package/template/.aioson/context/design-doc.md +136 -136
  69. package/template/.aioson/context/project-map.md +7 -5
  70. package/template/.aioson/context/seeds/seed-example.md +27 -27
  71. package/template/.aioson/context/user-profile.md +42 -42
  72. package/template/.aioson/design-docs/agent-loading-contract.md +117 -0
  73. package/template/.aioson/docs/dev/simple-plan-lane.md +92 -0
  74. package/template/.aioson/docs/product/conversation-playbook.md +15 -17
  75. package/template/.aioson/docs/quality/code-health-analysis.md +79 -0
  76. package/template/.aioson/docs/site-forge-build.md +2 -2
  77. package/template/.aioson/docs/site-forge-recon.md +5 -5
  78. package/template/.aioson/docs/squad/creation-flow.md +55 -0
  79. package/template/.aioson/docs/squad/eval-gate.md +79 -0
  80. package/template/.aioson/docs/squad/package-contract.md +39 -6
  81. package/template/.aioson/docs/squad/persona-grounding.md +62 -0
  82. package/template/.aioson/docs/squad/quality-lens.md +12 -1
  83. package/template/.aioson/genomes/INDEX.md +37 -37
  84. package/template/.aioson/genomes/copywriting/references/application-notes.md +2 -2
  85. package/template/.aioson/genomes/copywriting/references/frameworks/pms-research.md +1 -1
  86. package/template/.aioson/genomes/copywriting-brunson/references/application-notes.md +2 -2
  87. package/template/.aioson/learnings/gotchas/.gitkeep +1 -0
  88. package/template/.aioson/learnings/recipes/.gitkeep +1 -0
  89. package/template/.aioson/rules/agent-language-policy.md +21 -21
  90. package/template/.aioson/rules/agent-structural-contract.md +2 -2
  91. package/template/.aioson/rules/aioson-context-boundary.md +8 -6
  92. package/template/.aioson/rules/canonical-path-contract.md +10 -5
  93. package/template/.aioson/rules/data-format-convention.md +11 -11
  94. package/template/.aioson/rules/disk-first-artifacts.md +5 -4
  95. package/template/.aioson/rules/prd-section-ownership.md +12 -12
  96. package/template/.aioson/rules/simple-plan-lane.md +48 -0
  97. package/template/.aioson/rules/spec-level-ownership.md +5 -4
  98. package/template/.aioson/schemas/squad-blueprint.schema.json +32 -11
  99. package/template/.aioson/schemas/squad-manifest.schema.json +29 -8
  100. package/template/.aioson/skills/design/clean-saas-ui/SKILL.md +4 -4
  101. package/template/.aioson/skills/design/clean-saas-ui/references/art-direction.md +30 -30
  102. package/template/.aioson/skills/design/clean-saas-ui/references/motion.md +4 -4
  103. package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +2 -2
  104. package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +1 -1
  105. package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +1 -1
  106. package/template/.aioson/skills/design/neo-brutalist-ui/SKILL.md +5 -5
  107. package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +2 -2
  108. package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +4 -4
  109. package/template/.aioson/skills/design-system/dashboards/SKILL.md +5 -5
  110. package/template/.aioson/skills/design-system/patterns/SKILL.md +1 -1
  111. package/template/.aioson/skills/marketing/references/cta-matrix.md +43 -43
  112. package/template/.aioson/skills/marketing/references/headline-matrix.md +33 -33
  113. package/template/.aioson/skills/marketing/references/market-intelligence.md +2 -2
  114. package/template/.aioson/skills/marketing/references/platform-constraints.md +2 -2
  115. package/template/.aioson/skills/marketing/references/pms-research.md +3 -3
  116. package/template/.aioson/skills/process/aioson-spec-driven/references/approval-gates.md +7 -7
  117. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +13 -11
  118. package/template/.aioson/skills/process/aioson-spec-driven/references/ui-language.md +85 -75
  119. package/template/.aioson/skills/process/decision-presentation/SKILL.md +11 -11
  120. package/template/.aioson/skills/process/decision-presentation/references/jargon-map.pt-BR.yaml +4 -4
  121. package/template/.aioson/skills/squad/references/executor-archetypes.md +77 -2
  122. package/template/.aioson/skills/static/harness-validate/SKILL.md +55 -46
  123. package/template/.aioson/skills/static/react-motion-patterns.md +1 -1
  124. package/template/.aioson/skills/static/static-html-patterns.md +2 -2
  125. package/template/.aioson/skills/static/threejs-patterns.md +2 -2
  126. package/template/.aioson/tasks/implementation-plan.md +325 -327
  127. package/template/.aioson/tasks/squad-analyze.md +93 -83
  128. package/template/.aioson/tasks/squad-create.md +156 -148
  129. package/template/.aioson/tasks/squad-design.md +223 -206
  130. package/template/.aioson/tasks/squad-eval.md +72 -0
  131. package/template/.aioson/tasks/squad-execution-plan.md +279 -279
  132. package/template/.aioson/tasks/squad-export.md +20 -20
  133. package/template/.aioson/tasks/squad-extend.md +73 -68
  134. package/template/.aioson/tasks/squad-investigate.md +57 -57
  135. package/template/.aioson/tasks/squad-pipeline.md +122 -122
  136. package/template/.aioson/tasks/squad-profile.md +48 -48
  137. package/template/.aioson/tasks/squad-refresh.md +242 -236
  138. package/template/.aioson/tasks/squad-repair.md +85 -85
  139. package/template/.aioson/tasks/squad-review.md +61 -61
  140. package/template/.aioson/tasks/squad-task-decompose.md +66 -66
  141. package/template/.aioson/tasks/squad-validate.md +65 -58
  142. package/template/.aioson/templates/squads/content-basic/template.json +1 -1
  143. package/template/.aioson/templates/squads/media-channel/template.json +1 -1
  144. package/template/.aioson/templates/squads/research-analysis/template.json +1 -1
  145. package/template/AGENTS.md +10 -6
  146. package/template/CLAUDE.md +10 -6
  147. package/template/OPENCODE.md +9 -5
  148. package/template/agents/_shared/learning-capture-directive.md +88 -0
package/CHANGELOG.md CHANGED
@@ -4,7 +4,32 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
- ## [1.21.0] - 2026-06-XX
7
+ ## [1.21.3] - 2026-05-28
8
+
9
+ ### Security
10
+ - **`memory:trim --archive=<path>` is now contained under the project root (TS-LC-01).** It was resolved relative to `cwd` with no containment, so a crafted/typo'd path could write or overwrite a file outside the project. Now resolved under the project root and rejected with `archive_path_escape` on absolute or `..`-traversal escape — mirroring the containment wall in `memory-reflect-commit`. Localized message added in all 4 locales.
11
+ - **`feature:close` auto-trim hook now honors `AIOSON_RUNTIME_HOOK` (TS-LC-02).** The hook called the trim engine directly, bypassing the hook-context guard that `memory:trim` enforces. It now skips when running in a hook/automation context, so a tier-2 memory mutation never fires outside explicit human action.
12
+
13
+ ### Tests
14
+ - Coverage pass over the v1.21.2 agent-loading-contract code (`node --test --experimental-test-coverage`): `current-state-trim.js` 98.8%→100% line, `memory-trim.js` 73.9%→88.6% line / 64.4%→76.7% branch. Adds error/edge-path tests (`no_current_state`, `section_not_found`, custom/escaping `--archive`, headerless archive, hook skip paths) and verifies all `cli.memory_{archive,restore,search}` keys resolve.
15
+
16
+ ## [1.21.2] - 2026-05-28
17
+
18
+ ### Added
19
+ - **Agent loading contract + `memory:trim`.** `bootstrap/current-state.md` is an append-only log that every implementation/review agent read in full at activation (~81KB / ~33k tokens, 84% of the bootstrap). New `aioson memory:trim [--keep=<N>] [--archive=<path>] [--dry-run] [--json]` splits its "## What the system already has" section into a HOT log + a cold `current-state-archive.md` (entries MOVED verbatim, never deleted; active-feature entries exempt). `feature:close` (PASS) auto-rolls aged entries (`--no-trim` to opt out). New governance doc `.aioson/design-docs/agent-loading-contract.md` defines the three loading tiers + retention policy. The repo's own current-state was trimmed 81KB → 21KB.
20
+ - **`context:health` now measures `bootstrap/*.md`** — the per-activation layer it previously ignored — and excludes the cold `*-archive.md`; a heavy `current-state.md` now points to `memory:trim`.
21
+ - **Shared code-health analysis lens** `.aioson/docs/quality/code-health-analysis.md` (plan → investigate → refine → operate → test → adjust over coverage, test sufficiency, regression need, execution-chain, performance, componentization), wired on-demand into `@tester`/`@qa`/`@pentester`/`@architect`/`@sheldon`/`@deyvin`.
22
+ - **Current-state entry tagging** — the reflect engine, `@dev`, and `@committer` now prefix new entries with `[{slug} · {YYYY-MM-DD}]` for precise rollup; `@qa`/`@architect`/`@dev`/`@deyvin` bootstrap sections gained archive-awareness (grep the archive before flagging a capability as missing).
23
+
24
+ ### Fixed
25
+ - **`memory:archive` / `memory:restore` / `memory:search` logged raw i18n keys** in every locale — they called message keys without the required `cli.` namespace prefix, so `t()` missed and echoed the key (e.g. `memory_archive.id_required`). All 25 calls now use the `cli.` prefix and localize correctly.
26
+
27
+ ## [1.21.1] - 2026-06-XX
28
+
29
+ ### Fixed
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
+
32
+ ## [1.21.0] - 2026-05-28
8
33
 
9
34
  ### Added
10
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.
@@ -121,6 +121,8 @@ O agente é livre para usar o LLM como achar melhor para fazer a reescrita. O CL
121
121
 
122
122
  Se tudo passa: escreve, apaga o manifest, emite `memory_reflect_committed` em runtime. Se falha: emite `memory_reflect_failed` com a lista de erros e o agente tem 1 retry.
123
123
 
124
+ > **`--dry-run` (pré-visualização não-destrutiva):** `aioson memory:reflect-commit . --agent=dev --output=<json> --dry-run` roda a validação + containment de path exatamente como o commit real, mas **não escreve nada e não consome o manifest** — retorna `{ ok: true, dryRun: true, would_write: [...] }`. Use pra conferir o output antes de aplicar. Atenção: o manifest é **single-use** — um commit real (sem `--dry-run`) o apaga; pra recomeçar, rode `memory:reflect-prepare` de novo.
125
+
124
126
  ## Exemplo end-to-end
125
127
 
126
128
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaimevalasek/aioson",
3
- "version": "1.21.0",
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
@@ -18,6 +18,7 @@ const { runChainAudit } = require('./commands/chain-audit');
18
18
  const { runMemorySearch } = require('./commands/memory-search');
19
19
  const { runMemoryArchive } = require('./commands/memory-archive');
20
20
  const { runMemoryRestore } = require('./commands/memory-restore');
21
+ const { runMemoryTrim } = require('./commands/memory-trim');
21
22
  const { runSetupContext } = require('./commands/setup-context');
22
23
  const { runLocaleApply } = require('./commands/locale-apply');
23
24
  const { runSmokeTest } = require('./commands/smoke');
@@ -57,6 +58,8 @@ const { runSquadStatus } = require('./commands/squad-status');
57
58
  const { runSquadDoctor } = require('./commands/squad-doctor');
58
59
  const { runSquadRepairGenomes } = require('./commands/squad-repair-genomes');
59
60
  const { runSquadValidate } = require('./commands/squad-validate');
61
+ const { runSquadRoleScan } = require('./commands/squad-role-scan');
62
+ const { runSquadPlaybook } = require('./commands/squad-playbook');
60
63
  const { runSquadExport } = require('./commands/squad-export');
61
64
  const { runSquadPipeline } = require('./commands/squad-pipeline');
62
65
  const { runSquadAgentCreate } = require('./commands/squad-agent-create');
@@ -79,10 +82,12 @@ const { runSquadWebhook } = require('./commands/squad-webhook');
79
82
  const { runSquadBus } = require('./commands/squad-bus');
80
83
  const { runSquadAutorun } = require('./commands/squad-autorun');
81
84
  const { runSquadDependencyGraph } = require('./commands/squad-dependency-graph');
82
- const { runSquadToolRegister } = require('./commands/squad-tool-register');
83
- const { runSquadReview } = require('./commands/squad-review');
84
- const { runAgentAudit } = require('./commands/agent-audit');
85
- 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');
86
91
  const { runHarnessInit, runHarnessValidate, runHarnessApplyValidation } = require('./commands/harness');
87
92
  const { runVerifyGate } = require('./commands/verify-gate');
88
93
  const {
@@ -324,6 +329,10 @@ const JSON_SUPPORTED_COMMANDS = new Set([
324
329
  'squad-repair-genomes',
325
330
  'squad:validate',
326
331
  'squad-validate',
332
+ 'squad:role-scan',
333
+ 'squad-role-scan',
334
+ 'squad:playbook',
335
+ 'squad-playbook',
327
336
  'squad:export',
328
337
  'squad-export',
329
338
  'squad:pipeline',
@@ -375,9 +384,13 @@ const JSON_SUPPORTED_COMMANDS = new Set([
375
384
  'squad-tool-register',
376
385
  'squad:review',
377
386
  'squad-review',
378
- 'agent:audit',
379
- 'agent-audit',
380
- 'brief:gen',
387
+ 'agent:audit',
388
+ 'agent-audit',
389
+ 'skill:audit',
390
+ 'skill-audit',
391
+ 'quality:audit',
392
+ 'quality-audit',
393
+ 'brief:gen',
381
394
  'harness:init',
382
395
  'harness-init',
383
396
  'harness:validate',
@@ -525,6 +538,8 @@ const JSON_SUPPORTED_COMMANDS = new Set([
525
538
  'memory-archive',
526
539
  'memory:restore',
527
540
  'memory-restore',
541
+ 'memory:trim',
542
+ 'memory-trim',
528
543
  'memory:reflect-prepare',
529
544
  'memory-reflect-prepare',
530
545
  'memory:reflect-commit',
@@ -809,9 +824,11 @@ function printHelp(t, logger) {
809
824
  logHelpLine(t, logger, 'cli.help_squad_daemon');
810
825
  logHelpLine(t, logger, 'cli.help_squad_mcp');
811
826
  logHelpLine(t, logger, 'cli.help_squad_roi');
812
- logHelpLine(t, logger, 'cli.help_squad_score');
813
- logHelpLine(t, logger, 'cli.help_squad_learning');
814
- 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');
815
832
  logHelpLine(t, logger, 'cli.help_runtime_init');
816
833
  logHelpLine(t, logger, 'cli.help_runtime_ingest');
817
834
  logHelpLine(t, logger, 'cli.help_runtime_task_start');
@@ -834,9 +851,10 @@ function printHelp(t, logger) {
834
851
  logHelpLine(t, logger, 'cli.help_scaffold_complete');
835
852
  logHelpLine(t, logger, 'cli.help_runtime_backup');
836
853
  logHelpLine(t, logger, 'cli.help_runtime_restore');
837
- logHelpLine(t, logger, 'cli.help_skill_install');
838
- logHelpLine(t, logger, 'cli.help_skill_list');
839
- 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');
840
858
  logHelpLine(t, logger, 'cli.help_design_hybrid_options');
841
859
  logHelpLine(t, logger, 'cli.help_cloud_import_squad');
842
860
  logHelpLine(t, logger, 'cli.help_cloud_import_genome');
@@ -1161,6 +1179,10 @@ async function main() {
1161
1179
  result = await runSquadRepairGenomes({ args, options, logger: commandLogger, t });
1162
1180
  } else if (command === 'squad:validate' || command === 'squad-validate') {
1163
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 });
1164
1186
  } else if (command === 'squad:export' || command === 'squad-export') {
1165
1187
  result = await runSquadExport({ args, options, logger: commandLogger, t });
1166
1188
  } else if (command === 'squad:pipeline' || command === 'squad-pipeline') {
@@ -1213,10 +1235,14 @@ async function main() {
1213
1235
  result = await runSquadToolRegister({ args, options, logger: commandLogger });
1214
1236
  } else if (command === 'squad:review' || command === 'squad-review') {
1215
1237
  result = await runSquadReview({ args, options, logger: commandLogger });
1216
- } else if (command === 'agent:audit' || command === 'agent-audit') {
1217
- result = await runAgentAudit({ args, options, logger: commandLogger });
1218
- } else if (command === 'brief:gen' || command === 'brief-gen') {
1219
- 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 });
1220
1246
  } else if (command === 'harness:init' || command === 'harness-init') {
1221
1247
  result = await runHarnessInit({ args, options, logger: commandLogger, t });
1222
1248
  } else if (command === 'harness:validate' || command === 'harness-validate') {
@@ -1265,9 +1291,9 @@ async function main() {
1265
1291
  result = await runSpecCheckpoint({ args, options, logger: commandLogger });
1266
1292
  } else if (command === 'spec:tasks' || command === 'spec-tasks') {
1267
1293
  result = await runSpecTasks({ args, options, logger: commandLogger });
1268
- } else if (command.startsWith('learning:') || command === 'learning') {
1269
- const sub = command === 'learning' ? (args[1] || 'list') : command.split(':')[1];
1270
- 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 });
1271
1297
  } else if (command.startsWith('plan:') || command === 'plan') {
1272
1298
  const sub = command === 'plan' ? (args[1] || 'show') : command.split(':')[1];
1273
1299
  result = await runImplementationPlan({ args, options: { ...options, sub }, logger: commandLogger, t });
@@ -1355,6 +1381,8 @@ async function main() {
1355
1381
  result = await runMemoryArchive({ args, options, logger: commandLogger, t });
1356
1382
  } else if (command === 'memory:restore' || command === 'memory-restore') {
1357
1383
  result = await runMemoryRestore({ args, options, logger: commandLogger, t });
1384
+ } else if (command === 'memory:trim' || command === 'memory-trim') {
1385
+ result = await runMemoryTrim({ args, options, logger: commandLogger, t });
1358
1386
  } else if (command === 'memory:reflect-prepare' || command === 'memory-reflect-prepare') {
1359
1387
  result = await runMemoryReflectPrepare({ args, options, logger: commandLogger });
1360
1388
  } else if (command === 'memory:reflect-commit' || command === 'memory-reflect-commit') {
@@ -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 {