@jaimevalasek/aioson 1.17.3 → 1.19.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 (90) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +85 -51
  3. package/docs/en/3-recipes/full-feature-with-sheldon.md +1 -1
  4. package/docs/en/5-reference/cli-reference.md +4 -4
  5. package/docs/en/5-reference/qa-browser.md +2 -2
  6. package/docs/en/README.md +1 -1
  7. package/docs/en/deyvin-subtask-scout/how-to-use.md +2 -2
  8. package/docs/en/deyvin-subtask-scout/sub-task-scout.md +3 -3
  9. package/docs/en/deyvin-subtask-scout/troubleshooting.md +1 -1
  10. package/docs/pt/3-receitas/publicar-no-aioson-com.md +17 -0
  11. package/docs/pt/5-referencia/comandos-cli.md +2 -2
  12. package/docs/pt/5-referencia/inteligencia-adaptativa.md +3 -3
  13. package/docs/pt/5-referencia/skills.md +1 -1
  14. package/docs/pt/5-referencia/web3.md +3 -3
  15. package/docs/pt/README.md +1 -1
  16. package/docs/pt/_arquivo/README.md +1 -1
  17. package/docs/pt/_arquivo/cenarios.md +31 -31
  18. package/docs/pt/_arquivo/design-hybrid-forge.md +5 -5
  19. package/docs/pt/_arquivo/guia-engineer.md +1 -1
  20. package/docs/pt/_arquivo/profiler-system.md +1 -1
  21. package/docs/pt/_arquivo/site-forge.md +16 -16
  22. package/docs/pt/_arquivo/squad-genome.md +2 -2
  23. package/docs/pt/agentes.md +37 -37
  24. package/docs/pt/deyvin-subtask-scout/como-usar.md +2 -2
  25. package/docs/pt/deyvin-subtask-scout/sub-task-scout.md +1 -1
  26. package/docs/pt/deyvin-subtask-scout/troubleshooting.md +1 -1
  27. package/docs/pt/living-memory/README.md +1 -1
  28. package/docs/pt/living-memory/memoria-viva.md +2 -2
  29. package/docs/pt/living-memory/reflexao-in-harness.md +1 -1
  30. package/docs/pt/living-memory/troubleshooting.md +6 -6
  31. package/package.json +1 -1
  32. package/src/cli.js +111 -7
  33. package/src/commands/gate-approve.js +56 -1
  34. package/src/commands/live.js +81 -54
  35. package/src/commands/op-capture.js +27 -2
  36. package/src/commands/op-list.js +33 -1
  37. package/src/commands/store-system.js +4 -0
  38. package/src/commands/tool-capabilities.js +14 -10
  39. package/src/commands/workflow-heal.js +47 -1
  40. package/src/constants.js +73 -0
  41. package/src/i18n/messages/en.js +20 -2
  42. package/src/i18n/messages/es.js +18 -1
  43. package/src/i18n/messages/fr.js +18 -1
  44. package/src/i18n/messages/pt-BR.js +20 -2
  45. package/src/lib/dev-resume.js +6 -1
  46. package/src/lib/tool-capabilities.js +64 -37
  47. package/src/operator-memory/decision.js +11 -4
  48. package/src/operator-memory/proposal.js +11 -7
  49. package/src/session-handoff.js +52 -1
  50. package/template/.aioson/agents/analyst.md +33 -1
  51. package/template/.aioson/agents/architect.md +33 -1
  52. package/template/.aioson/agents/briefing.md +23 -0
  53. package/template/.aioson/agents/orchestrator.md +26 -0
  54. package/template/.aioson/agents/pentester.md +66 -14
  55. package/template/.aioson/agents/pm.md +18 -1
  56. package/template/.aioson/agents/product.md +11 -0
  57. package/template/.aioson/agents/sheldon.md +21 -1
  58. package/template/.aioson/agents/tester.md +114 -1
  59. package/template/.aioson/docs/pentester/browser-dast-playbook.md +398 -0
  60. package/template/.aioson/rules/agent-structural-contract.md +139 -0
  61. package/template/.aioson/skills/process/decision-presentation/SKILL.md +2 -2
  62. package/template/.claude/commands/aioson/agent/analyst.md +16 -5
  63. package/template/.claude/commands/aioson/agent/architect.md +17 -5
  64. package/template/.claude/commands/aioson/agent/briefing.md +16 -5
  65. package/template/.claude/commands/aioson/agent/committer.md +16 -5
  66. package/template/.claude/commands/aioson/agent/copywriter.md +16 -5
  67. package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +16 -5
  68. package/template/.claude/commands/aioson/agent/dev.md +18 -5
  69. package/template/.claude/commands/aioson/agent/deyvin.md +16 -5
  70. package/template/.claude/commands/aioson/agent/discover.md +16 -5
  71. package/template/.claude/commands/aioson/agent/discovery-design-doc.md +16 -5
  72. package/template/.claude/commands/aioson/agent/genome.md +16 -5
  73. package/template/.claude/commands/aioson/agent/neo.md +16 -5
  74. package/template/.claude/commands/aioson/agent/orache.md +16 -5
  75. package/template/.claude/commands/aioson/agent/orchestrator.md +21 -5
  76. package/template/.claude/commands/aioson/agent/pair.md +16 -5
  77. package/template/.claude/commands/aioson/agent/pentester.md +22 -5
  78. package/template/.claude/commands/aioson/agent/pm.md +20 -5
  79. package/template/.claude/commands/aioson/agent/product.md +16 -5
  80. package/template/.claude/commands/aioson/agent/profiler-enricher.md +16 -5
  81. package/template/.claude/commands/aioson/agent/profiler-forge.md +16 -5
  82. package/template/.claude/commands/aioson/agent/profiler-researcher.md +16 -5
  83. package/template/.claude/commands/aioson/agent/qa.md +16 -5
  84. package/template/.claude/commands/aioson/agent/setup.md +16 -5
  85. package/template/.claude/commands/aioson/agent/sheldon.md +16 -5
  86. package/template/.claude/commands/aioson/agent/site-forge.md +16 -5
  87. package/template/.claude/commands/aioson/agent/squad.md +16 -5
  88. package/template/.claude/commands/aioson/agent/tester.md +16 -5
  89. package/template/.claude/commands/aioson/agent/ux-ui.md +19 -5
  90. package/template/.claude/commands/aioson/agent/validator.md +17 -5
@@ -19,6 +19,8 @@ module.exports = {
19
19
  help_agents: 'aioson agents [path] [--lang=<bcp47-tag>] [--locale=en]',
20
20
  help_agent_prompt:
21
21
  'aioson agent:prompt <agent> [path] [--tool=codex|claude|gemini|opencode] [--lang=<bcp47-tag>] [--locale=en]',
22
+ help_agent_help:
23
+ 'aioson agent:help [agent] [--json]',
22
24
  help_agent_invoke:
23
25
  'aioson agent:invoke <agent> [path] [--tool=codex|claude|gemini|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=en]',
24
26
  help_context_validate: 'aioson context:validate [path] [--json] [--locale=en]',
@@ -219,7 +221,7 @@ module.exports = {
219
221
  help_runtime_emit:
220
222
  'aioson runtime:emit [path] --agent=<name> [--type=<event>] [--summary=<text>] [--title=<text>] [--refs=<file[,file2]>] [--plan-step=<id>] [--meta=<json>] [--json] [--locale=en]',
221
223
  help_live_start:
222
- 'aioson live:start [path] --tool=codex|claude|gemini|opencode --agent=<name> [--tool-bin=<binary>] [--tool-args=<args>] [--title=<text>] [--goal=<text>] [--plan=<file>] [--session=<key>] [--message=<text>] [--attach] [--no-launch] [--tmux] [--json] [--locale=en]',
224
+ 'aioson live:start [path] --tool=codex|claude|gemini|opencode --agent=<name> [--tool-bin=<binary>] [--permission-mode=default|yolo] [--tool-args=<args>] [--title=<text>] [--goal=<text>] [--plan=<file>] [--session=<key>] [--message=<text>] [--attach] [--no-launch] [--tmux] [--json] [--locale=en]',
223
225
  help_live_status:
224
226
  'aioson live:status [path] [--agent=<name>] [--limit=8] [--watch=2] [--format=compact|tmux-bar] [--json] [--locale=en]',
225
227
  help_live_handoff:
@@ -505,7 +507,22 @@ module.exports = {
505
507
  'Pentester app_target requires --feature=<slug> (or --slug=<slug>).',
506
508
  prompt_missing_scope_for_app_target:
507
509
  'Pentester app_target requires --scope=<area>.',
508
- prompt_title: 'Prompt for agent "{agent}" on tool "{tool}" (locale: {locale}):'
510
+ prompt_title: 'Prompt for agent "{agent}" on tool "{tool}" (locale: {locale}):',
511
+ help_available: 'Available agents:',
512
+ help_run_detail: 'Run "aioson agent:help <name>" for details on a specific agent.',
513
+ help_usage: 'Usage:',
514
+ help_claude_code: '(Claude Code)',
515
+ help_common_options: 'Common options:',
516
+ help_opt_tool: 'Target tool (codex|claude|gemini|opencode)',
517
+ help_opt_language: 'Interaction language (e.g., pt-BR, en)',
518
+ help_opt_headless: 'Output prompt only, no runtime tracking',
519
+ help_opt_output: 'Save headless prompt to file',
520
+ help_opt_json: 'JSON output mode',
521
+ help_agent_options: 'Agent-specific options ({command}):',
522
+ help_requires: 'Requires:',
523
+ help_produces: 'Produces:',
524
+ help_instruction_file: 'Instruction file:',
525
+ help_unknown_agent: 'Unknown agent: {agent}'
509
526
  },
510
527
  context_validate: {
511
528
  missing_file: 'Context file not found: {path}',
@@ -1093,6 +1110,7 @@ module.exports = {
1093
1110
  json_requires_no_launch: '--json requires --no-launch for live:start because foreground launch is interactive.',
1094
1111
  tool_binary_not_found: 'Tool binary not found in PATH: {binary}',
1095
1112
  tool_mismatch: 'Active session uses tool "{existing}" but --tool={requested} was given. Close the session first or use the same tool.',
1113
+ tool_mismatch_auto_closed: 'Previous live session used "{existing}" and was auto-closed. Starting a new session with "{requested}".',
1096
1114
  micro_task_already_open: 'A live micro-task is already open for {agent}. Emit task_completed before task_started again.',
1097
1115
  handoff_same_agent: 'live:handoff requires different --agent and --to values.',
1098
1116
  handoff_agent_mismatch: 'No active live session found for {agent}.',
@@ -20,6 +20,8 @@ module.exports = {
20
20
  help_agents: 'aioson agents [path] [--lang=<bcp47-tag>] [--locale=es]',
21
21
  help_agent_prompt:
22
22
  'aioson agent:prompt <agent> [path] [--tool=codex|claude|gemini|opencode] [--lang=<bcp47-tag>] [--locale=es]',
23
+ help_agent_help:
24
+ 'aioson agent:help [agent] [--json]',
23
25
  help_agent_invoke:
24
26
  'aioson agent:invoke <agent> [path] [--tool=codex|claude|gemini|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=es]',
25
27
  help_context_validate: 'aioson context:validate [path] [--json] [--locale=es]',
@@ -383,7 +385,22 @@ module.exports = {
383
385
  'Pentester app_target requiere --feature=<slug> (o --slug=<slug>).',
384
386
  prompt_missing_scope_for_app_target:
385
387
  'Pentester app_target requiere --scope=<area>.',
386
- prompt_title: 'Prompt para el agente "{agent}" en la herramienta "{tool}" (locale: {locale}):'
388
+ prompt_title: 'Prompt para el agente "{agent}" en la herramienta "{tool}" (locale: {locale}):',
389
+ help_available: 'Agentes disponibles:',
390
+ help_run_detail: 'Ejecute "aioson agent:help <nombre>" para detalles de un agente específico.',
391
+ help_usage: 'Uso:',
392
+ help_claude_code: '(Claude Code)',
393
+ help_common_options: 'Opciones comunes:',
394
+ help_opt_tool: 'Herramienta destino (codex|claude|gemini|opencode)',
395
+ help_opt_language: 'Idioma de interacción (ej: pt-BR, en)',
396
+ help_opt_headless: 'Solo salida del prompt, sin rastreo de runtime',
397
+ help_opt_output: 'Guardar prompt headless en archivo',
398
+ help_opt_json: 'Modo de salida JSON',
399
+ help_agent_options: 'Opciones específicas del agente ({command}):',
400
+ help_requires: 'Requiere:',
401
+ help_produces: 'Produce:',
402
+ help_instruction_file: 'Archivo de instrucción:',
403
+ help_unknown_agent: 'Agente desconocido: {agent}'
387
404
  },
388
405
  context_validate: {
389
406
  missing_file: 'Archivo de contexto no encontrado: {path}',
@@ -20,6 +20,8 @@ module.exports = {
20
20
  help_agents: 'aioson agents [path] [--lang=<bcp47-tag>] [--locale=fr]',
21
21
  help_agent_prompt:
22
22
  'aioson agent:prompt <agent> [path] [--tool=codex|claude|gemini|opencode] [--lang=<bcp47-tag>] [--locale=fr]',
23
+ help_agent_help:
24
+ 'aioson agent:help [agent] [--json]',
23
25
  help_agent_invoke:
24
26
  'aioson agent:invoke <agent> [path] [--tool=codex|claude|gemini|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=fr]',
25
27
  help_context_validate: 'aioson context:validate [path] [--json] [--locale=fr]',
@@ -382,7 +384,22 @@ module.exports = {
382
384
  'Pentester app_target exige --feature=<slug> (ou --slug=<slug>).',
383
385
  prompt_missing_scope_for_app_target:
384
386
  'Pentester app_target exige --scope=<area>.',
385
- prompt_title: 'Prompt pour l agent "{agent}" sur l outil "{tool}" (locale : {locale}) :'
387
+ prompt_title: 'Prompt pour l agent "{agent}" sur l outil "{tool}" (locale : {locale}) :',
388
+ help_available: 'Agents disponibles :',
389
+ help_run_detail: 'Exécutez "aioson agent:help <nom>" pour les détails d un agent spécifique.',
390
+ help_usage: 'Utilisation :',
391
+ help_claude_code: '(Claude Code)',
392
+ help_common_options: 'Options communes :',
393
+ help_opt_tool: 'Outil cible (codex|claude|gemini|opencode)',
394
+ help_opt_language: 'Langue d interaction (ex : pt-BR, en)',
395
+ help_opt_headless: 'Sortie du prompt uniquement, sans suivi runtime',
396
+ help_opt_output: 'Enregistrer le prompt headless dans un fichier',
397
+ help_opt_json: 'Mode de sortie JSON',
398
+ help_agent_options: 'Options spécifiques de l agent ({command}) :',
399
+ help_requires: 'Requiert :',
400
+ help_produces: 'Produit :',
401
+ help_instruction_file: 'Fichier d instruction :',
402
+ help_unknown_agent: 'Agent inconnu : {agent}'
386
403
  },
387
404
  context_validate: {
388
405
  missing_file: 'Fichier de contexte introuvable : {path}',
@@ -20,6 +20,8 @@ module.exports = {
20
20
  help_agents: 'aioson agents [path] [--lang=<bcp47-tag>] [--locale=pt-BR]',
21
21
  help_agent_prompt:
22
22
  'aioson agent:prompt <agent> [path] [--tool=codex|claude|gemini|opencode] [--lang=<bcp47-tag>] [--locale=pt-BR]',
23
+ help_agent_help:
24
+ 'aioson agent:help [agent] [--json]',
23
25
  help_agent_invoke:
24
26
  'aioson agent:invoke <agent> [path] [--tool=codex|claude|gemini|opencode] [--mode=framework_target|app_target] [--feature=<slug>] [--scope=<area>] [--lang=<bcp47-tag>] [--locale=pt-BR]',
25
27
  help_context_validate: 'aioson context:validate [path] [--json] [--locale=pt-BR]',
@@ -217,7 +219,7 @@ module.exports = {
217
219
  help_runtime_emit:
218
220
  'aioson runtime:emit [path] --agent=<nome> [--type=<evento>] [--summary=<texto>] [--title=<texto>] [--refs=<arquivo[,arquivo2]>] [--plan-step=<id>] [--meta=<json>] [--json] [--locale=pt-BR]',
219
221
  help_live_start:
220
- 'aioson live:start [path] --tool=codex|claude|gemini|opencode --agent=<nome> [--tool-bin=<binario>] [--tool-args=<args>] [--title=<texto>] [--goal=<texto>] [--plan=<arquivo>] [--session=<chave>] [--message=<texto>] [--attach] [--no-launch] [--tmux] [--json] [--locale=pt-BR]',
222
+ 'aioson live:start [path] --tool=codex|claude|gemini|opencode --agent=<nome> [--tool-bin=<binario>] [--permission-mode=default|yolo] [--tool-args=<args>] [--title=<texto>] [--goal=<texto>] [--plan=<arquivo>] [--session=<chave>] [--message=<texto>] [--attach] [--no-launch] [--tmux] [--json] [--locale=pt-BR]',
221
223
  help_live_status:
222
224
  'aioson live:status [path] [--agent=<nome>] [--limit=8] [--watch=2] [--format=compact|tmux-bar] [--json] [--locale=pt-BR]',
223
225
  help_live_handoff:
@@ -477,7 +479,22 @@ module.exports = {
477
479
  'Pentester app_target exige --feature=<slug> (ou --slug=<slug>).',
478
480
  prompt_missing_scope_for_app_target:
479
481
  'Pentester app_target exige --scope=<area>.',
480
- prompt_title: 'Prompt para o agente "{agent}" na ferramenta "{tool}" (locale: {locale}):'
482
+ prompt_title: 'Prompt para o agente "{agent}" na ferramenta "{tool}" (locale: {locale}):',
483
+ help_available: 'Agentes disponíveis:',
484
+ help_run_detail: 'Execute "aioson agent:help <nome>" para detalhes de um agente específico.',
485
+ help_usage: 'Uso:',
486
+ help_claude_code: '(Claude Code)',
487
+ help_common_options: 'Opções comuns:',
488
+ help_opt_tool: 'Ferramenta alvo (codex|claude|gemini|opencode)',
489
+ help_opt_language: 'Idioma de interação (ex: pt-BR, en)',
490
+ help_opt_headless: 'Saída apenas do prompt, sem rastreamento de runtime',
491
+ help_opt_output: 'Salvar prompt headless em arquivo',
492
+ help_opt_json: 'Modo de saída JSON',
493
+ help_agent_options: 'Opções específicas do agente ({command}):',
494
+ help_requires: 'Requer:',
495
+ help_produces: 'Produz:',
496
+ help_instruction_file: 'Arquivo de instrução:',
497
+ help_unknown_agent: 'Agente desconhecido: {agent}'
481
498
  },
482
499
  context_validate: {
483
500
  missing_file: 'Arquivo de contexto nao encontrado: {path}',
@@ -1119,6 +1136,7 @@ module.exports = {
1119
1136
  json_requires_no_launch: '--json requer --no-launch para live:start porque o lancamento em primeiro plano e interativo.',
1120
1137
  tool_binary_not_found: 'Binario da ferramenta nao encontrado no PATH: {binary}',
1121
1138
  tool_mismatch: 'A sessao ativa usa a ferramenta "{existing}" mas --tool={requested} foi informado. Encerre a sessao primeiro ou use a mesma ferramenta.',
1139
+ tool_mismatch_auto_closed: 'A sessao live anterior usava "{existing}" e foi fechada automaticamente. Iniciando nova sessao com "{requested}".',
1122
1140
  micro_task_already_open: 'Uma micro-tarefa live ja esta aberta para {agent}. Emita task_completed antes de task_started novamente.',
1123
1141
  handoff_same_agent: 'live:handoff requer valores diferentes para --agent e --to.',
1124
1142
  handoff_agent_mismatch: 'Nenhuma sessao live ativa encontrada para {agent}.',
@@ -121,6 +121,10 @@ async function buildDevResumeData(projectPath) {
121
121
  ? lastHandoff.artifact_uris
122
122
  : [];
123
123
 
124
+ const decisionRationale = Array.isArray(lastHandoff && lastHandoff.decision_rationale)
125
+ ? lastHandoff.decision_rationale
126
+ : [];
127
+
124
128
  return {
125
129
  feature_slug: featureSlug,
126
130
  classification,
@@ -128,7 +132,8 @@ async function buildDevResumeData(projectPath) {
128
132
  artifacts_consumed: artifactsConsumed,
129
133
  code_map_paths: extractCodeMapPaths(dossierRaw),
130
134
  sheldon_plan: sheldonPlan,
131
- next_step: devStateNext || deriveNextStepFromPlan(planRaw)
135
+ next_step: devStateNext || deriveNextStepFromPlan(planRaw),
136
+ decision_rationale: decisionRationale.length > 0 ? decisionRationale : undefined
132
137
  };
133
138
  }
134
139
 
@@ -5,10 +5,11 @@
5
5
  // "continue last conversation" is achieved by passing the right resume flag
6
6
  // at spawn time — AIOSON never has to track an internal session ID.
7
7
  //
8
- // Used by:
9
- // - `aioson live:start --resume[=last|<id>]` to map to the correct argv
10
- // - `aioson tool:capabilities` to expose this map as JSON to UI clients
11
- // (e.g. AIOSON Play) so they don't duplicate the lookup.
8
+ // Used by:
9
+ // - `aioson live:start --resume[=last|<id>]` to map to the correct argv
10
+ // - `aioson live:start --permission-mode=yolo` to map to the correct argv
11
+ // - `aioson tool:capabilities` to expose this map as JSON to UI clients
12
+ // (e.g. AIOSON Play) so they don't duplicate the lookup.
12
13
  //
13
14
  // Keep entries minimal and source-of-truth here. Adding a new CLI = one entry.
14
15
  const TOOL_CAPS = {
@@ -17,42 +18,50 @@ const TOOL_CAPS = {
17
18
  binary: 'claude',
18
19
  supports_resume: true,
19
20
  resume_last: ['--continue'],
20
- supports_session_id: true,
21
- resume_session_id: ['--resume', '<id>'],
22
- supports_session_picker: true,
23
- session_picker: ['--resume'],
24
- },
25
- codex: {
21
+ supports_session_id: true,
22
+ resume_session_id: ['--resume', '<id>'],
23
+ supports_session_picker: true,
24
+ session_picker: ['--resume'],
25
+ supports_yolo: true,
26
+ yolo_args: ['--dangerously-skip-permissions'],
27
+ },
28
+ codex: {
26
29
  install_command: 'npm install -g @openai/codex',
27
30
  binary: 'codex',
28
31
  supports_resume: true,
29
32
  resume_last: ['resume', '--last'],
30
- supports_session_id: true,
31
- resume_session_id: ['resume', '<id>'],
32
- supports_session_picker: true,
33
- session_picker: ['resume'],
34
- },
35
- opencode: {
33
+ supports_session_id: true,
34
+ resume_session_id: ['resume', '<id>'],
35
+ supports_session_picker: true,
36
+ session_picker: ['resume'],
37
+ supports_yolo: true,
38
+ yolo_args: ['--dangerously-bypass-approvals-and-sandbox'],
39
+ },
40
+ opencode: {
36
41
  install_command: 'npm install -g opencode-ai',
37
42
  binary: 'opencode',
38
43
  supports_resume: true,
39
44
  resume_last: ['--continue'],
40
- supports_session_id: true,
41
- resume_session_id: ['--session', '<id>'],
42
- supports_session_picker: false,
43
- session_picker: null,
44
- },
45
- gemini: {
45
+ supports_session_id: true,
46
+ resume_session_id: ['--session', '<id>'],
47
+ supports_session_picker: false,
48
+ session_picker: null,
49
+ supports_yolo: false,
50
+ yolo_args: null,
51
+ },
52
+ gemini: {
46
53
  install_command: 'npm install -g @google/gemini-cli',
47
54
  binary: 'gemini',
48
55
  supports_resume: false,
49
56
  resume_last: null,
50
- supports_session_id: false,
51
- resume_session_id: null,
52
- supports_session_picker: false,
53
- session_picker: null,
54
- },
55
- };
57
+ supports_session_id: false,
58
+ resume_session_id: null,
59
+ supports_session_picker: false,
60
+ session_picker: null,
61
+ supports_yolo: false,
62
+ yolo_args: null,
63
+ },
64
+ };
56
65
 
57
66
  function getToolCapabilities(tool) {
58
67
  const key = String(tool || '').trim().toLowerCase();
@@ -71,7 +80,7 @@ function listSupportedTools() {
71
80
  // - '' / undefined / null / false → no resume
72
81
  // - any other string → treat as session id
73
82
  // Returns [] when the tool doesn't support resume or resumeOpt is falsy.
74
- function resolveResumeArgs(tool, resumeOpt) {
83
+ function resolveResumeArgs(tool, resumeOpt) {
75
84
  if (resumeOpt === undefined || resumeOpt === null || resumeOpt === '' || resumeOpt === false) {
76
85
  return [];
77
86
  }
@@ -92,11 +101,29 @@ function resolveResumeArgs(tool, resumeOpt) {
92
101
  }
93
102
 
94
103
  return Array.isArray(caps.resume_last) ? [...caps.resume_last] : [];
95
- }
96
-
97
- module.exports = {
98
- TOOL_CAPS,
99
- getToolCapabilities,
100
- listSupportedTools,
101
- resolveResumeArgs,
102
- };
104
+ }
105
+
106
+ function resolvePermissionModeArgs(tool, permissionMode) {
107
+ const mode = String(permissionMode || '').trim().toLowerCase();
108
+ if (!mode || mode === 'default') return [];
109
+ if (mode !== 'yolo') {
110
+ throw new Error(`permission_mode_unknown:${permissionMode}`);
111
+ }
112
+
113
+ const caps = getToolCapabilities(tool);
114
+ if (!caps) {
115
+ throw new Error(`tool_unknown:${tool}`);
116
+ }
117
+ if (!caps.supports_yolo || !Array.isArray(caps.yolo_args)) {
118
+ throw new Error(`permission_mode_unsupported:${tool}:yolo`);
119
+ }
120
+ return [...caps.yolo_args];
121
+ }
122
+
123
+ module.exports = {
124
+ TOOL_CAPS,
125
+ getToolCapabilities,
126
+ listSupportedTools,
127
+ resolveResumeArgs,
128
+ resolvePermissionModeArgs,
129
+ };
@@ -81,7 +81,7 @@ function deriveTitle(proposal) {
81
81
  function serializeDecision(data) {
82
82
  const body = String(data.body || data.proposal || '').slice(0, MAX_BODY_CHARS);
83
83
  const title = deriveTitle(data.title || data.proposal);
84
- return [
84
+ const lines = [
85
85
  '---',
86
86
  `slug: ${data.slug}`,
87
87
  `signal_type: ${data.signal_type}`,
@@ -93,8 +93,13 @@ function serializeDecision(data) {
93
93
  `source_agent: ${data.source_agent}`,
94
94
  `quotes:${quotesToYaml(data.quotes)}`,
95
95
  `version_schema: "${SCHEMA_VERSION}"`,
96
- `deprecated_by: ${data.deprecated_by ?? 'null'}`,
97
- '---',
96
+ `deprecated_by: ${data.deprecated_by ?? 'null'}`
97
+ ];
98
+ if (data.feature_slug) lines.push(`feature_slug: ${escapeYamlString(data.feature_slug)}`);
99
+ if (data.session_id) lines.push(`session_id: ${escapeYamlString(data.session_id)}`);
100
+ lines.push('---');
101
+ return [
102
+ ...lines,
98
103
  '',
99
104
  `# ${title}`,
100
105
  '',
@@ -178,7 +183,9 @@ function promoteProposal({ identity, proposal: proposalData }) {
178
183
  quotes: proposalData.quotes || [],
179
184
  body,
180
185
  title: proposalData.proposal,
181
- deprecated_by: null
186
+ deprecated_by: null,
187
+ feature_slug: proposalData.feature_slug || null,
188
+ session_id: proposalData.session_id || null
182
189
  };
183
190
 
184
191
  const decFilePath = decisionPath(identity, decision.slug);
@@ -46,7 +46,7 @@ function quotesToYaml(quotes) {
46
46
  }
47
47
 
48
48
  function serializeProposal(data) {
49
- return [
49
+ const lines = [
50
50
  '---',
51
51
  `slug: ${data.slug}`,
52
52
  `signal_type: ${data.signal_type}`,
@@ -56,10 +56,12 @@ function serializeProposal(data) {
56
56
  `quotes:${quotesToYaml(data.quotes)}`,
57
57
  `proposal: ${escapeYamlString(data.proposal)}`,
58
58
  `source_agent: ${data.source_agent}`,
59
- `proposal_fingerprint: ${data.proposal_fingerprint}`,
60
- '---',
61
- ''
62
- ].join('\n');
59
+ `proposal_fingerprint: ${data.proposal_fingerprint}`
60
+ ];
61
+ if (data.feature_slug) lines.push(`feature_slug: ${escapeYamlString(data.feature_slug)}`);
62
+ if (data.session_id) lines.push(`session_id: ${escapeYamlString(data.session_id)}`);
63
+ lines.push('---', '');
64
+ return lines.join('\n');
63
65
  }
64
66
 
65
67
  function parseProposalFrontmatter(content) {
@@ -130,7 +132,7 @@ function deleteProposal(identity, slug) {
130
132
  return false;
131
133
  }
132
134
 
133
- function captureSignal({ identity, slug, signal_type, quote, proposal, source_agent }) {
135
+ function captureSignal({ identity, slug, signal_type, quote, proposal, source_agent, feature_slug, session_id }) {
134
136
  if (!VALID_SIGNAL_TYPES.includes(signal_type)) {
135
137
  throw new Error(`Invalid signal_type '${signal_type}'. Must be one of: ${VALID_SIGNAL_TYPES.join(', ')}`);
136
138
  }
@@ -160,7 +162,9 @@ function captureSignal({ identity, slug, signal_type, quote, proposal, source_ag
160
162
  quotes: quote ? [String(quote).trim()].filter(Boolean) : [],
161
163
  proposal,
162
164
  source_agent: source_agent || 'unknown',
163
- proposal_fingerprint: fingerprintProposal(proposal)
165
+ proposal_fingerprint: fingerprintProposal(proposal),
166
+ feature_slug: feature_slug || null,
167
+ session_id: session_id || null
164
168
  };
165
169
  writeProposal(identity, fresh);
166
170
  return { proposal: fresh, isNew: true };
@@ -6,6 +6,8 @@ const { exists, ensureDir } = require('./utils');
6
6
 
7
7
  const HANDOFF_RELATIVE_PATH = '.aioson/context/last-handoff.json';
8
8
  const HANDOFF_PROTOCOL_RELATIVE_PATH = '.aioson/context/handoff-protocol.json';
9
+ const CONFIRMATIONS_JSONL = '.aioson/runtime/session-confirmations.jsonl';
10
+ const DECISION_RATIONALE_MAX = 5;
9
11
 
10
12
  // handoff-protocol.json schema_version for `artifact_uris`:
11
13
  // v1 (legacy) — array of strings; v2 — array of {path, kind, agent, added_at}.
@@ -63,10 +65,52 @@ function coerceArtifactUris(uris, fallbackAgent) {
63
65
  .filter(Boolean);
64
66
  }
65
67
 
68
+ async function collectDecisionRationale(targetDir) {
69
+ const accPath = path.join(targetDir, CONFIRMATIONS_JSONL);
70
+ try {
71
+ const raw = await fs.readFile(accPath, 'utf8');
72
+ const lines = raw.trim().split('\n').filter(Boolean);
73
+ const entries = [];
74
+ for (const line of lines) {
75
+ try {
76
+ const obj = JSON.parse(line);
77
+ entries.push({
78
+ agent: obj.agent || 'unknown',
79
+ decision: obj.decision || '',
80
+ alternatives_considered: null,
81
+ rationale: obj.quote || null,
82
+ confidence: 'confirmed'
83
+ });
84
+ } catch {
85
+ // skip malformed lines
86
+ }
87
+ }
88
+ // BR-AO-04: FIFO — keep only the last N entries
89
+ return entries.slice(-DECISION_RATIONALE_MAX);
90
+ } catch {
91
+ return [];
92
+ }
93
+ }
94
+
95
+ async function clearConfirmationsAccumulator(targetDir) {
96
+ const accPath = path.join(targetDir, CONFIRMATIONS_JSONL);
97
+ try {
98
+ await fs.unlink(accPath);
99
+ } catch {
100
+ // file may not exist
101
+ }
102
+ }
103
+
66
104
  async function writeHandoff(targetDir, payload) {
67
105
  const handoffPath = path.join(targetDir, HANDOFF_RELATIVE_PATH);
68
106
  const protocolPath = path.join(targetDir, HANDOFF_PROTOCOL_RELATIVE_PATH);
69
107
  await ensureDir(path.dirname(handoffPath));
108
+
109
+ // M2: collect decision rationale from session confirmations
110
+ const sessionRationale = await collectDecisionRationale(targetDir);
111
+ const existingRationale = Array.isArray(payload.decisionRationale) ? payload.decisionRationale : [];
112
+ const mergedRationale = [...existingRationale, ...sessionRationale].slice(-DECISION_RATIONALE_MAX);
113
+
70
114
  const handoff = {
71
115
  version: 1,
72
116
  session_ended_at: new Date().toISOString(),
@@ -79,10 +123,14 @@ async function writeHandoff(targetDir, payload) {
79
123
  context_files_updated: Array.isArray(payload.contextFilesUpdated) ? payload.contextFilesUpdated : [],
80
124
  workflow_mode: payload.workflowMode || null,
81
125
  classification: payload.classification || null,
82
- feature_slug: payload.featureSlug || null
126
+ feature_slug: payload.featureSlug || null,
127
+ decision_rationale: mergedRationale.length > 0 ? mergedRationale : undefined
83
128
  };
84
129
  await fs.writeFile(handoffPath, `${JSON.stringify(handoff, null, 2)}\n`, 'utf8');
85
130
 
131
+ // Clear accumulator after writing to handoff
132
+ await clearConfirmationsAccumulator(targetDir);
133
+
86
134
  const protocol = payload.protocol || buildBasicHandoffProtocol(payload);
87
135
  await fs.writeFile(protocolPath, `${JSON.stringify(protocol, null, 2)}\n`, 'utf8');
88
136
 
@@ -283,9 +331,12 @@ function buildRuntimeLogHandoff(agentName, message, summary) {
283
331
  module.exports = {
284
332
  HANDOFF_RELATIVE_PATH,
285
333
  HANDOFF_PROTOCOL_RELATIVE_PATH,
334
+ CONFIRMATIONS_JSONL,
335
+ DECISION_RATIONALE_MAX,
286
336
  ARTIFACT_KINDS,
287
337
  coerceArtifactUri,
288
338
  coerceArtifactUris,
339
+ collectDecisionRationale,
289
340
  writeHandoff,
290
341
  readHandoff,
291
342
  readHandoffProtocol,
@@ -34,6 +34,8 @@ Before any manual checks, run these commands if the `aioson` CLI is available:
34
34
  ```bash
35
35
  aioson workflow:status . # confirm current stage and what is expected
36
36
  aioson context:validate . # validate project.context.md; detects brownfield state
37
+ aioson preflight . --agent=analyst --feature={slug} # unified pre-session check: loads rules, design governance, and context
38
+ aioson classify . # auto-detect project classification (MICRO/SMALL/MEDIUM) for cross-reference
37
39
  ```
38
40
 
39
41
  For feature mode with existing requirements, run before the synchronization gate:
@@ -341,5 +343,35 @@ aioson dev:state:write . --feature={slug} --phase=1 \
341
343
 
342
344
  `--context` accepts canonical tokens (`prd`, `requirements`, `spec`, `architecture`, `impl-plan`, `sheldon`, `design-doc`, `dossier`), max 4 entries total; missing files emit a warning and are skipped. Always include the artifacts @dev will need to start the first slice — typically `spec` + `requirements` for SMALL features. Idempotent: re-running with the same args does not duplicate state.
343
345
 
346
+ **Handoff message:**
347
+ ```
348
+ Requirements written: .aioson/context/requirements-{slug}.md
349
+ Spec skeleton: .aioson/context/spec-{slug}.md
350
+ Gate A: approved
351
+ Next agent: @architect (MEDIUM) or @dev (SMALL — skip architecture)
352
+ Why: Requirements and spec ready — @architect defines system design, or @dev starts implementation for SMALL features.
353
+ Action: /architect or /dev
354
+ ```
355
+ > Recommended: `/clear` before activating — fresh context window.
356
+
357
+ ## Strategic commands (use during session)
358
+
359
+ - Search memory before web research: `aioson memory:search . --query="<topic>" 2>/dev/null || true`
360
+ - Search context files: `aioson context:search . --query="<term>" 2>/dev/null || true`
361
+ - Compress context before handoff: `aioson context:pack . 2>/dev/null || true`
362
+ - Create spec checkpoint before changes: `aioson spec:checkpoint . --feature={slug} 2>/dev/null || true`
363
+
344
364
  ## Observability
345
- At session end, register: `aioson agent:done . --agent=analyst --summary="Discovery <slug>: <N> entities, <N> rules" 2>/dev/null || true`
365
+
366
+ At strategic milestones during execution, emit progress signals:
367
+ ```bash
368
+ aioson runtime:emit . --agent=analyst --type=milestone --summary="Requirements written: {slug}, {N} BRs, {N} ECs" 2>/dev/null || true
369
+ aioson runtime:emit . --agent=analyst --type=milestone --summary="Spec skeleton created: {slug}" 2>/dev/null || true
370
+ ```
371
+
372
+ At session end, register:
373
+ ```bash
374
+ aioson gate:approve . --feature={slug} --gate=A 2>/dev/null || true
375
+ aioson pulse:update . --agent=analyst --feature={slug} --action="Discovery completed: {N} entities, {N} rules" --next="<next agent recommendation>" 2>/dev/null || true
376
+ aioson agent:done . --agent=analyst --summary="Discovery <slug>: <N> entities, <N> rules" 2>/dev/null || true
377
+ ```
@@ -124,6 +124,20 @@ Before handing off to `@dev`:
124
124
  - If a relevant spec file exists and design is still pending, do not claim Gate B passed.
125
125
  - Tell the user explicitly whether Gate B passed or is blocked before handoff.
126
126
 
127
+ When Gate B passes, register it via CLI:
128
+ ```bash
129
+ aioson gate:approve . --feature={slug} --gate=B 2>/dev/null || true
130
+ ```
131
+
132
+ **Handoff message:**
133
+ ```
134
+ Architecture defined: .aioson/context/architecture.md
135
+ Gate B: {approved|blocked}
136
+ Next agent: @pm (MEDIUM — implementation planning) or @dev (SMALL — direct implementation)
137
+ Action: /pm or /dev
138
+ ```
139
+ > Recommended: `/clear` before activating — fresh context window.
140
+
127
141
  ## Rules
128
142
  - Do not redesign entities produced by `@analyst`. Consume the data design as-is.
129
143
  - Keep architecture proportional to classification. Never apply MEDIUM patterns to a MICRO project.
@@ -321,5 +335,23 @@ Keep architecture.md proportional — verbose output costs tokens without adding
321
335
  - Do not introduce patterns that do not exist in the chosen stack's conventions.
322
336
  - Do not copy content from discovery.md into architecture.md. Reference sections by name: "see discovery.md § Entities". The document chain is already in context.
323
337
 
338
+ ## Strategic commands (use during session)
339
+
340
+ - Search context for existing decisions: `aioson context:search . --query="<architectural term>" 2>/dev/null || true`
341
+ - Validate artifacts against spec: `aioson artifact:validate . --feature={slug} 2>/dev/null || true`
342
+ - Compress context before handoff: `aioson context:pack . 2>/dev/null || true`
343
+ - Audit dossier completeness: `aioson dossier:audit . --check=coverage 2>/dev/null || true`
344
+
324
345
  ## Observability
325
- At session end, register: `aioson agent:done . --agent=architect --summary="Architecture <slug>: <stack>, <N> modules" 2>/dev/null || true`
346
+
347
+ At strategic milestones during execution, emit progress signals:
348
+ ```bash
349
+ aioson runtime:emit . --agent=architect --type=milestone --summary="Architecture decided: {slug}, {stack}" 2>/dev/null || true
350
+ aioson runtime:emit . --agent=architect --type=gate_check --summary="Gate B: {approved|blocked} for {slug}" 2>/dev/null || true
351
+ ```
352
+
353
+ At session end, register:
354
+ ```bash
355
+ aioson pulse:update . --agent=architect --feature={slug} --action="Architecture defined: {stack}, {N} modules" --next="<next agent recommendation>" 2>/dev/null || true
356
+ aioson agent:done . --agent=architect --summary="Architecture <slug>: <stack>, <N> modules" 2>/dev/null || true
357
+ ```
@@ -92,6 +92,16 @@ Wait for confirmation.
92
92
  Write `.aioson/briefings/{slug}/briefings.md` and update `.aioson/briefings/config.md`.
93
93
  See **Output contract** below for exact formats.
94
94
 
95
+ After writing the briefing draft, emit a milestone:
96
+ ```bash
97
+ aioson runtime:emit . --agent=briefing --type=milestone --summary="Briefing draft written: {slug}" 2>/dev/null || true
98
+ ```
99
+
100
+ If a feature dossier exists for the target slug, record the briefing:
101
+ ```bash
102
+ aioson dossier:add-finding . --slug={slug} --agent=briefing --section="Agent Trail" --content="Briefing created: {N} themes, {N} risks, {N} open questions" 2>/dev/null || true
103
+ ```
104
+
95
105
  ## Mode: Conversational (no plans)
96
106
 
97
107
  When `plans/` is empty or the user wants to plan via conversation:
@@ -222,12 +232,24 @@ Always register additional files with a note at the bottom of `briefings.md`:
222
232
  - `{specific-theme}.md` — {one line description}
223
233
  ```
224
234
 
235
+ ## Feature dossier
236
+
237
+ Check `.aioson/context/features/{slug}/dossier.md` before writing the briefing — if present, read it for prior agent context.
238
+
239
+ **After writing the briefing**, record in the dossier:
240
+ ```
241
+ aioson dossier:add-finding . --slug={slug} --agent=briefing --section="Agent Trail" --content="Briefing created: {N} themes, {N} risks, {N} open questions" 2>/dev/null || true
242
+ ```
243
+
244
+ Skip silently when the dossier is absent.
245
+
225
246
  ## Rules
226
247
 
227
248
  - **Never modify `plans/`** — they are read-only. Plans belong to the user.
228
249
  - **Never access `.aioson/briefings/` from @dev** — briefings are pre-production. @dev receives the PRD already built.
229
250
  - **Never create a PRD** — that is `@product`'s responsibility.
230
251
  - **Never approve a briefing automatically** — approval requires explicit user action via CLI.
252
+ - When a briefing is approved (via CLI), emit: `aioson runtime:emit . --agent=briefing --type=milestone --summary="Briefing approved: {slug}" 2>/dev/null || true`
231
253
  - **Never overwrite an existing briefing** without confirming with the user first.
232
254
  - **Slug must be confirmed** by the user before any file is written.
233
255
  - **Never recommend `@sheldon` (or any post-PRD agent) as the next step.** The only handoff from `@briefing` is `@product`. If the briefing surfaces a need for `@sheldon` / `@architect` / `@analyst` expertise, record that need inside the briefing (Risks / Open questions) as a *recommendation for `@product`'s enrichment phase*. `@product` decides when to invoke specialists after the PRD exists. See `briefing-craft.md` §1 "Mitigating weak markers" for examples.
@@ -252,6 +274,7 @@ Always register additional files with a note at the bottom of `briefings.md`:
252
274
  - `config.md` frontmatter must be valid YAML — verify after writing.
253
275
  - All 8 sections must appear in `briefings.md` even when empty (`TBD`).
254
276
  - At session end, update `.aioson/context/project-pulse.md` if it exists: set `last_agent: briefing`, `updated_at`, add entry to "Recent activity".
277
+ - At session end, update pulse: `aioson pulse:update . --agent=briefing --feature={slug} --action="<summary>" --next="<next agent recommendation>" 2>/dev/null || true`
255
278
  - At session end, register: `aioson agent:done . --agent=briefing --summary="<one-line summary>" 2>/dev/null || true`
256
279
  - If `aioson` CLI is not available, write a devlog following the "Devlog" section in `.aioson/config.md`.
257
280