@jaimevalasek/aioson 1.4.0 → 1.5.1

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 (199) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/LICENSE +661 -21
  3. package/README.md +3 -1
  4. package/docs/en/squad-dashboard.md +372 -0
  5. package/docs/openclaw-bridge.md +308 -0
  6. package/docs/pt/agentes.md +124 -10
  7. package/docs/pt/cenarios.md +46 -2
  8. package/docs/pt/comandos-cli.md +60 -1
  9. package/docs/pt/inicio-rapido.md +18 -2
  10. package/docs/pt/squad-dashboard.md +373 -0
  11. package/docs/testing/genome-2.0-matrix.md +5 -5
  12. package/docs/testing/genome-2.0-rollout.md +9 -9
  13. package/package.json +2 -2
  14. package/src/backup-local.js +74 -0
  15. package/src/cli.js +98 -0
  16. package/src/commands/backup-local-cmd.js +25 -0
  17. package/src/commands/runtime.js +242 -0
  18. package/src/commands/setup-context.js +7 -2
  19. package/src/commands/squad-daemon.js +209 -0
  20. package/src/commands/squad-dashboard.js +39 -0
  21. package/src/commands/squad-deploy.js +64 -0
  22. package/src/commands/squad-doctor.js +52 -0
  23. package/src/commands/squad-mcp.js +270 -0
  24. package/src/commands/squad-processes.js +56 -0
  25. package/src/commands/squad-recovery.js +42 -0
  26. package/src/commands/squad-roi.js +291 -0
  27. package/src/commands/squad-score.js +250 -0
  28. package/src/commands/squad-status.js +37 -1
  29. package/src/commands/squad-validate.js +62 -1
  30. package/src/commands/squad-webhook.js +160 -0
  31. package/src/commands/squad-worker.js +191 -0
  32. package/src/commands/squad-worktrees.js +75 -0
  33. package/src/commands/web-map.js +70 -0
  34. package/src/commands/web-scrape.js +71 -0
  35. package/src/constants.js +8 -0
  36. package/src/context-writer.js +45 -1
  37. package/src/i18n/messages/en.js +127 -1
  38. package/src/i18n/messages/es.js +117 -0
  39. package/src/i18n/messages/fr.js +117 -0
  40. package/src/i18n/messages/pt-BR.js +126 -1
  41. package/src/lib/webhook-server.js +328 -0
  42. package/src/mcp-connectors/registry.js +602 -0
  43. package/src/runtime-store.js +259 -2
  44. package/src/squad/external-session.js +180 -0
  45. package/src/squad/inter-squad.js +74 -0
  46. package/src/squad/recovery-context.js +201 -0
  47. package/src/squad/worktree-manager.js +114 -0
  48. package/src/squad-daemon.js +490 -0
  49. package/src/squad-dashboard/api.js +223 -0
  50. package/src/squad-dashboard/attachment-handler.js +93 -0
  51. package/src/squad-dashboard/context-monitor.js +157 -0
  52. package/src/squad-dashboard/execution-logs.js +115 -0
  53. package/src/squad-dashboard/hunk-review.js +209 -0
  54. package/src/squad-dashboard/metrics.js +133 -0
  55. package/src/squad-dashboard/process-monitor.js +125 -0
  56. package/src/squad-dashboard/renderer.js +858 -0
  57. package/src/squad-dashboard/server.js +232 -0
  58. package/src/squad-dashboard/styles.js +525 -0
  59. package/src/squad-dashboard/token-tracker.js +99 -0
  60. package/src/web.js +284 -0
  61. package/src/worker-runner.js +339 -0
  62. package/template/.aioson/agents/analyst.md +4 -0
  63. package/template/.aioson/agents/architect.md +4 -0
  64. package/template/.aioson/agents/dev.md +120 -11
  65. package/template/.aioson/agents/deyvin.md +8 -0
  66. package/template/.aioson/agents/neo.md +152 -0
  67. package/template/.aioson/agents/orache.md +17 -0
  68. package/template/.aioson/agents/orchestrator.md +26 -0
  69. package/template/.aioson/agents/product.md +60 -12
  70. package/template/.aioson/agents/qa.md +1 -0
  71. package/template/.aioson/agents/setup.md +63 -19
  72. package/template/.aioson/agents/sheldon.md +603 -0
  73. package/template/.aioson/agents/squad.md +191 -0
  74. package/template/.aioson/agents/tester.md +254 -0
  75. package/template/.aioson/agents/ux-ui.md +12 -0
  76. package/template/.aioson/config.md +6 -0
  77. package/template/.aioson/locales/en/agents/analyst.md +8 -0
  78. package/template/.aioson/locales/en/agents/architect.md +8 -0
  79. package/template/.aioson/locales/en/agents/dev.md +66 -7
  80. package/template/.aioson/locales/en/agents/deyvin.md +8 -0
  81. package/template/.aioson/locales/en/agents/neo.md +8 -0
  82. package/template/.aioson/locales/en/agents/orchestrator.md +26 -0
  83. package/template/.aioson/locales/en/agents/qa.md +49 -0
  84. package/template/.aioson/locales/en/agents/setup.md +2 -1
  85. package/template/.aioson/locales/en/agents/sheldon.md +340 -0
  86. package/template/.aioson/locales/en/agents/ux-ui.md +8 -0
  87. package/template/.aioson/locales/es/agents/analyst.md +8 -0
  88. package/template/.aioson/locales/es/agents/architect.md +8 -0
  89. package/template/.aioson/locales/es/agents/dev.md +66 -7
  90. package/template/.aioson/locales/es/agents/deyvin.md +8 -0
  91. package/template/.aioson/locales/es/agents/neo.md +48 -0
  92. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  93. package/template/.aioson/locales/es/agents/qa.md +26 -0
  94. package/template/.aioson/locales/es/agents/setup.md +2 -1
  95. package/template/.aioson/locales/es/agents/sheldon.md +192 -0
  96. package/template/.aioson/locales/es/agents/squad.md +63 -0
  97. package/template/.aioson/locales/es/agents/ux-ui.md +8 -0
  98. package/template/.aioson/locales/fr/agents/analyst.md +8 -0
  99. package/template/.aioson/locales/fr/agents/architect.md +8 -0
  100. package/template/.aioson/locales/fr/agents/dev.md +66 -7
  101. package/template/.aioson/locales/fr/agents/deyvin.md +8 -0
  102. package/template/.aioson/locales/fr/agents/neo.md +48 -0
  103. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  104. package/template/.aioson/locales/fr/agents/qa.md +26 -0
  105. package/template/.aioson/locales/fr/agents/setup.md +2 -1
  106. package/template/.aioson/locales/fr/agents/sheldon.md +192 -0
  107. package/template/.aioson/locales/fr/agents/squad.md +63 -0
  108. package/template/.aioson/locales/fr/agents/ux-ui.md +8 -0
  109. package/template/.aioson/locales/pt-BR/agents/analyst.md +19 -0
  110. package/template/.aioson/locales/pt-BR/agents/architect.md +19 -0
  111. package/template/.aioson/locales/pt-BR/agents/dev.md +75 -12
  112. package/template/.aioson/locales/pt-BR/agents/deyvin.md +8 -0
  113. package/template/.aioson/locales/pt-BR/agents/neo.md +147 -0
  114. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +26 -0
  115. package/template/.aioson/locales/pt-BR/agents/product.md +8 -3
  116. package/template/.aioson/locales/pt-BR/agents/qa.md +60 -0
  117. package/template/.aioson/locales/pt-BR/agents/setup.md +2 -1
  118. package/template/.aioson/locales/pt-BR/agents/sheldon.md +192 -0
  119. package/template/.aioson/locales/pt-BR/agents/squad.md +105 -0
  120. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +8 -0
  121. package/template/.aioson/schemas/squad-blueprint.schema.json +21 -0
  122. package/template/.aioson/schemas/squad-manifest.schema.json +178 -1
  123. package/template/.aioson/skills/design/bold-editorial-ui/SKILL.md +205 -0
  124. package/template/.aioson/skills/design/bold-editorial-ui/references/art-direction.md +338 -0
  125. package/template/.aioson/skills/design/bold-editorial-ui/references/components.md +977 -0
  126. package/template/.aioson/skills/design/bold-editorial-ui/references/dashboards.md +218 -0
  127. package/template/.aioson/skills/design/bold-editorial-ui/references/design-tokens.md +326 -0
  128. package/template/.aioson/skills/design/bold-editorial-ui/references/motion.md +461 -0
  129. package/template/.aioson/skills/design/bold-editorial-ui/references/patterns.md +293 -0
  130. package/template/.aioson/skills/design/bold-editorial-ui/references/websites.md +352 -0
  131. package/template/.aioson/skills/design/clean-saas-ui/SKILL.md +210 -0
  132. package/template/.aioson/skills/design/clean-saas-ui/references/art-direction.md +319 -0
  133. package/template/.aioson/skills/design/clean-saas-ui/references/components.md +365 -0
  134. package/template/.aioson/skills/design/clean-saas-ui/references/dashboards.md +196 -0
  135. package/template/.aioson/skills/design/clean-saas-ui/references/design-tokens.md +244 -0
  136. package/template/.aioson/skills/design/clean-saas-ui/references/motion.md +235 -0
  137. package/template/.aioson/skills/design/clean-saas-ui/references/patterns.md +215 -0
  138. package/template/.aioson/skills/design/clean-saas-ui/references/websites.md +295 -0
  139. package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +55 -9
  140. package/template/.aioson/skills/design/cognitive-core-ui/references/art-direction.md +339 -0
  141. package/template/.aioson/skills/design/cognitive-core-ui/references/components.md +1 -1
  142. package/template/.aioson/skills/design/cognitive-core-ui/references/dashboards.md +100 -0
  143. package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +43 -9
  144. package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +40 -0
  145. package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +1 -1
  146. package/template/.aioson/skills/design/cognitive-core-ui/references/websites.md +99 -12
  147. package/template/.aioson/skills/design/warm-craft-ui/SKILL.md +209 -0
  148. package/template/.aioson/skills/design/warm-craft-ui/references/art-direction.md +324 -0
  149. package/template/.aioson/skills/design/warm-craft-ui/references/components.md +508 -0
  150. package/template/.aioson/skills/design/warm-craft-ui/references/dashboards.md +223 -0
  151. package/template/.aioson/skills/design/warm-craft-ui/references/design-tokens.md +374 -0
  152. package/template/.aioson/skills/design/warm-craft-ui/references/motion.md +356 -0
  153. package/template/.aioson/skills/design/warm-craft-ui/references/patterns.md +288 -0
  154. package/template/.aioson/skills/design/warm-craft-ui/references/websites.md +289 -0
  155. package/template/.aioson/skills/premium-visual-design/SKILL.md +83 -0
  156. package/template/.aioson/skills/premium-visual-design/components/agent-badge.md +92 -0
  157. package/template/.aioson/skills/premium-visual-design/components/dependency-node.md +102 -0
  158. package/template/.aioson/skills/premium-visual-design/components/mention-autocomplete.md +136 -0
  159. package/template/.aioson/skills/premium-visual-design/components/notification-center.md +136 -0
  160. package/template/.aioson/skills/premium-visual-design/components/review-action-bar.md +188 -0
  161. package/template/.aioson/skills/premium-visual-design/components/team-switcher.md +131 -0
  162. package/template/.aioson/skills/premium-visual-design/patterns/agent-message-thread.md +198 -0
  163. package/template/.aioson/skills/premium-visual-design/patterns/notification-panel.md +275 -0
  164. package/template/.aioson/skills/premium-visual-design/patterns/review-workflow-ui.md +234 -0
  165. package/template/.aioson/skills/premium-visual-design/patterns/task-dependency-graph.md +147 -0
  166. package/template/.aioson/skills/premium-visual-design/tokens/status-extended.md +142 -0
  167. package/template/.aioson/skills/squad/formats/catalog.json +15 -0
  168. package/template/.aioson/skills/squad/formats/content/blog-post.md +47 -0
  169. package/template/.aioson/skills/squad/formats/content/newsletter.md +47 -0
  170. package/template/.aioson/skills/squad/formats/creative/podcast-script.md +43 -0
  171. package/template/.aioson/skills/squad/formats/creative/video-script.md +41 -0
  172. package/template/.aioson/skills/squad/formats/social/instagram-feed.md +42 -0
  173. package/template/.aioson/skills/squad/formats/social/linkedin-post.md +42 -0
  174. package/template/.aioson/skills/squad/formats/social/tiktok.md +39 -0
  175. package/template/.aioson/skills/squad/formats/social/twitter-thread.md +39 -0
  176. package/template/.aioson/skills/squad/formats/social/youtube-long.md +47 -0
  177. package/template/.aioson/skills/squad/formats/social/youtube-shorts.md +39 -0
  178. package/template/.aioson/skills/squad/patterns/multi-platform-pattern.md +108 -0
  179. package/template/.aioson/skills/squad/patterns/persona-based-pattern.md +98 -0
  180. package/template/.aioson/skills/squad/patterns/pipeline-pattern.md +106 -0
  181. package/template/.aioson/skills/squad/patterns/review-loop-pattern.md +81 -0
  182. package/template/.aioson/skills/squad/references/checklist-templates.md +122 -0
  183. package/template/.aioson/skills/squad/references/executor-archetypes.md +123 -0
  184. package/template/.aioson/skills/squad/references/workflow-templates.md +169 -0
  185. package/template/.aioson/skills/static/debugging-protocol.md +42 -0
  186. package/template/.aioson/skills/static/git-worktrees.md +36 -0
  187. package/template/.aioson/tasks/implementation-plan.md +19 -0
  188. package/template/.aioson/tasks/squad-design.md +28 -0
  189. package/template/.aioson/tasks/squad-profile.md +48 -0
  190. package/template/.aioson/tasks/squad-review.md +61 -0
  191. package/template/.aioson/tasks/squad-task-decompose.md +66 -0
  192. package/template/.claude/commands/aioson/agent/neo.md +5 -0
  193. package/template/.claude/commands/aioson/agent/tester.md +5 -0
  194. package/template/.gemini/GEMINI.md +1 -0
  195. package/template/.gemini/commands/aios-neo.toml +4 -0
  196. package/template/.gemini/commands/aios-tester.toml +6 -0
  197. package/template/AGENTS.md +3 -0
  198. package/template/CLAUDE.md +5 -2
  199. package/template/OPENCODE.md +2 -0
package/src/cli.js CHANGED
@@ -32,6 +32,8 @@ const { runQaInit } = require('./commands/qa-init');
32
32
  const { runQaRun } = require('./commands/qa-run');
33
33
  const { runQaScan } = require('./commands/qa-scan');
34
34
  const { runQaReport } = require('./commands/qa-report');
35
+ const { runWebMap } = require('./commands/web-map');
36
+ const { runWebScrape } = require('./commands/web-scrape');
35
37
  const { runScanProject } = require('./commands/scan-project');
36
38
  const { runConfig } = require('./commands/config');
37
39
  const { runGenomeDoctor } = require('./commands/genome-doctor');
@@ -48,6 +50,17 @@ const { runImplementationPlan } = require('./commands/implementation-plan');
48
50
  const { runSquadPlan } = require('./commands/squad-plan');
49
51
  const { runSquadLearning } = require('./commands/squad-learning');
50
52
  const { runLearning } = require('./commands/learning');
53
+ const { runSquadDashboard } = require('./commands/squad-dashboard');
54
+ const { runSquadWorker } = require('./commands/squad-worker');
55
+ const { runSquadDaemon } = require('./commands/squad-daemon');
56
+ const { runSquadMcp } = require('./commands/squad-mcp');
57
+ const { runSquadRoi } = require('./commands/squad-roi');
58
+ const { runSquadScore } = require('./commands/squad-score');
59
+ const { runSquadProcesses } = require('./commands/squad-processes');
60
+ const { runSquadWorktrees, runSquadMerge } = require('./commands/squad-worktrees');
61
+ const { runSquadRecovery } = require('./commands/squad-recovery');
62
+ const { runSquadDeploy } = require('./commands/squad-deploy');
63
+ const { runSquadWebhook } = require('./commands/squad-webhook');
51
64
  const {
52
65
  runRuntimeInit,
53
66
  runRuntimeIngest,
@@ -60,6 +73,8 @@ const {
60
73
  runRuntimeFail,
61
74
  runRuntimeStatus,
62
75
  runRuntimeLog,
76
+ runAgentDone,
77
+ runAgentRecover,
63
78
  runRuntimeSessionStart,
64
79
  runRuntimeSessionLog,
65
80
  runRuntimeSessionFinish,
@@ -93,6 +108,7 @@ const {
93
108
  runSkillList,
94
109
  runSkillRemove
95
110
  } = require('./commands/skill');
111
+ const { runBackupLocal } = require('./commands/backup-local-cmd');
96
112
 
97
113
  const JSON_SUPPORTED_COMMANDS = new Set([
98
114
  'init',
@@ -159,6 +175,10 @@ const JSON_SUPPORTED_COMMANDS = new Set([
159
175
  'qa-scan',
160
176
  'qa:report',
161
177
  'qa-report',
178
+ 'web:map',
179
+ 'web-map',
180
+ 'web:scrape',
181
+ 'web-scrape',
162
182
  'scan:project',
163
183
  'scan-project',
164
184
  'config',
@@ -182,6 +202,31 @@ const JSON_SUPPORTED_COMMANDS = new Set([
182
202
  'squad-agent-create',
183
203
  'squad:investigate',
184
204
  'squad-investigate',
205
+ 'squad:dashboard',
206
+ 'squad-dashboard',
207
+ 'squad:worker',
208
+ 'squad-worker',
209
+ 'squad:daemon',
210
+ 'squad-daemon',
211
+ 'squad:mcp',
212
+ 'squad-mcp',
213
+ 'squad:mcp:call',
214
+ 'squad:roi',
215
+ 'squad-roi',
216
+ 'squad:score',
217
+ 'squad-score',
218
+ 'squad:processes',
219
+ 'squad-processes',
220
+ 'squad:worktrees',
221
+ 'squad-worktrees',
222
+ 'squad:merge',
223
+ 'squad-merge',
224
+ 'squad:recovery',
225
+ 'squad-recovery',
226
+ 'squad:deploy',
227
+ 'squad-deploy',
228
+ 'squad:webhook',
229
+ 'squad-webhook',
185
230
  'plan:show',
186
231
  'plan:status',
187
232
  'plan:checkpoint',
@@ -218,6 +263,10 @@ const JSON_SUPPORTED_COMMANDS = new Set([
218
263
  'runtime-status',
219
264
  'runtime:log',
220
265
  'runtime-log',
266
+ 'agent:done',
267
+ 'agent-done',
268
+ 'agent:recover',
269
+ 'agent-recover',
221
270
  'runtime:session:start',
222
271
  'runtime-session-start',
223
272
  'runtime:session:log',
@@ -333,6 +382,8 @@ function printHelp(t, logger) {
333
382
  logHelpLine(t, logger, 'cli.help_qa_run');
334
383
  logHelpLine(t, logger, 'cli.help_qa_scan');
335
384
  logHelpLine(t, logger, 'cli.help_qa_report');
385
+ logHelpLine(t, logger, 'cli.help_web_map');
386
+ logHelpLine(t, logger, 'cli.help_web_scrape');
336
387
  logHelpLine(t, logger, 'cli.help_scan_project');
337
388
  logHelpLine(t, logger, 'cli.help_config');
338
389
  logHelpLine(t, logger, 'cli.help_genome_doctor');
@@ -345,6 +396,12 @@ function printHelp(t, logger) {
345
396
  logHelpLine(t, logger, 'cli.help_squad_pipeline');
346
397
  logHelpLine(t, logger, 'cli.help_squad_agent_create');
347
398
  logHelpLine(t, logger, 'cli.help_squad_investigate');
399
+ logHelpLine(t, logger, 'cli.help_squad_dashboard');
400
+ logHelpLine(t, logger, 'cli.help_squad_worker');
401
+ logHelpLine(t, logger, 'cli.help_squad_daemon');
402
+ logHelpLine(t, logger, 'cli.help_squad_mcp');
403
+ logHelpLine(t, logger, 'cli.help_squad_roi');
404
+ logHelpLine(t, logger, 'cli.help_squad_score');
348
405
  logHelpLine(t, logger, 'cli.help_squad_learning');
349
406
  logHelpLine(t, logger, 'cli.help_learning');
350
407
  logHelpLine(t, logger, 'cli.help_runtime_init');
@@ -523,6 +580,10 @@ async function main() {
523
580
  result = await runQaScan({ args, options, logger: commandLogger, t });
524
581
  } else if (command === 'qa:report' || command === 'qa-report') {
525
582
  result = await runQaReport({ args, options, logger: commandLogger, t });
583
+ } else if (command === 'web:map' || command === 'web-map') {
584
+ result = await runWebMap({ args, options, logger: commandLogger, t });
585
+ } else if (command === 'web:scrape' || command === 'web-scrape') {
586
+ result = await runWebScrape({ args, options, logger: commandLogger, t });
526
587
  } else if (command === 'scan:project' || command === 'scan-project') {
527
588
  result = await runScanProject({ args, options, logger: commandLogger, t });
528
589
  } else if (command === 'config') {
@@ -547,6 +608,37 @@ async function main() {
547
608
  result = await runSquadAgentCreate({ args, options, logger: commandLogger, t });
548
609
  } else if (command === 'squad:investigate' || command === 'squad-investigate') {
549
610
  result = await runSquadInvestigate({ args, options, logger: commandLogger, t });
611
+ } else if (command === 'squad:dashboard' || command === 'squad-dashboard') {
612
+ result = await runSquadDashboard({ args, options, logger: commandLogger, t });
613
+ } else if (command === 'squad:worker' || command === 'squad-worker') {
614
+ const sub = options.sub || 'list';
615
+ result = await runSquadWorker({ args, options: { ...options, sub }, logger: commandLogger, t });
616
+ } else if (command === 'squad:daemon' || command === 'squad-daemon') {
617
+ const sub = options.sub || 'status';
618
+ result = await runSquadDaemon({ args, options: { ...options, sub }, logger: commandLogger, t });
619
+ } else if (command === 'squad:mcp:call') {
620
+ result = await runSquadMcp({ args, options: { ...options, sub: 'call' }, logger: commandLogger, t });
621
+ } else if (command === 'squad:mcp' || command === 'squad-mcp') {
622
+ const sub = options.sub || 'status';
623
+ result = await runSquadMcp({ args, options: { ...options, sub }, logger: commandLogger, t });
624
+ } else if (command === 'squad:roi' || command === 'squad-roi') {
625
+ const sub = options.sub || 'report';
626
+ result = await runSquadRoi({ args, options: { ...options, sub }, logger: commandLogger, t });
627
+ } else if (command === 'squad:score' || command === 'squad-score') {
628
+ result = await runSquadScore({ args, options, logger: commandLogger, translator: t });
629
+ } else if (command === 'squad:processes' || command === 'squad-processes') {
630
+ result = await runSquadProcesses({ args, options, logger: commandLogger, t });
631
+ } else if (command === 'squad:worktrees' || command === 'squad-worktrees') {
632
+ result = await runSquadWorktrees({ args, options, logger: commandLogger, t });
633
+ } else if (command === 'squad:merge' || command === 'squad-merge') {
634
+ result = await runSquadMerge({ args, options, logger: commandLogger, t });
635
+ } else if (command === 'squad:recovery' || command === 'squad-recovery') {
636
+ result = await runSquadRecovery({ args, options, logger: commandLogger, t });
637
+ } else if (command === 'squad:deploy' || command === 'squad-deploy') {
638
+ result = await runSquadDeploy({ args, options, logger: commandLogger, t });
639
+ } else if (command === 'squad:webhook' || command === 'squad-webhook') {
640
+ const sub = options.sub || 'start';
641
+ result = await runSquadWebhook({ args, options: { ...options, sub }, logger: commandLogger, t });
550
642
  } else if (command === 'squad:plan' || command === 'squad-plan') {
551
643
  result = await runSquadPlan({ args, options, logger: commandLogger, t });
552
644
  } else if (command === 'squad:learning' || command === 'squad-learning') {
@@ -580,6 +672,10 @@ async function main() {
580
672
  result = await runRuntimeStatus({ args, options, logger: commandLogger, t });
581
673
  } else if (command === 'runtime:log' || command === 'runtime-log') {
582
674
  result = await runRuntimeLog({ args, options, logger: commandLogger, t });
675
+ } else if (command === 'agent:done' || command === 'agent-done') {
676
+ result = await runAgentDone({ args, options, logger: commandLogger, t });
677
+ } else if (command === 'agent:recover' || command === 'agent-recover') {
678
+ result = await runAgentRecover({ args, options, logger: commandLogger, t });
583
679
  } else if (command === 'runtime:session:start' || command === 'runtime-session-start') {
584
680
  result = await runRuntimeSessionStart({ args, options, logger: commandLogger, t });
585
681
  } else if (command === 'runtime:session:log' || command === 'runtime-session-log') {
@@ -614,6 +710,8 @@ async function main() {
614
710
  result = await runRuntimeBackup({ args, options, logger: commandLogger, t });
615
711
  } else if (command === 'runtime:restore' || command === 'runtime-restore') {
616
712
  result = await runRuntimeRestore({ args, options, logger: commandLogger, t });
713
+ } else if (command === 'backup:local' || command === 'backup-local') {
714
+ result = await runBackupLocal({ args, options, logger: commandLogger, t });
617
715
  } else if (command === 'skill:install' || command === 'skill-install') {
618
716
  result = await runSkillInstall({ args, options, logger: commandLogger, t });
619
717
  } else if (command === 'skill:list' || command === 'skill-list') {
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { backupAiosonDocs } = require('../backup-local');
5
+
6
+ /**
7
+ * aioson backup:local [.]
8
+ *
9
+ * Manually backs up all .md files from .aioson/context/ and .aioson/plans/
10
+ * to ~/.aioson/backups/{project}/{timestamp}/
11
+ */
12
+ async function runBackupLocal({ args, options, logger }) {
13
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
14
+ const result = await backupAiosonDocs(targetDir);
15
+
16
+ if (result.count === 0) {
17
+ logger.log('backup:local — nothing to back up (no .md files found in .aioson/context/ or .aioson/plans/)');
18
+ return { ok: true, count: 0, backupPath: null };
19
+ }
20
+
21
+ logger.log(`backup:local — ${result.count} file(s) backed up → ${result.backupPath}`);
22
+ return { ok: true, count: result.count, backupPath: result.backupPath };
23
+ }
24
+
25
+ module.exports = { runBackupLocal };
@@ -20,6 +20,7 @@ const {
20
20
  } = require('../runtime-store');
21
21
  const { runAutoDelivery } = require('../delivery-runner');
22
22
  const { writeHandoff, buildRuntimeLogHandoff } = require('../session-handoff');
23
+ const { backupAiosonDocs, isDocCreatingAgent } = require('../backup-local');
23
24
 
24
25
  const ALLOWED_LAYOUTS = new Set(['document', 'tabs', 'accordion', 'stack', 'mixed']);
25
26
  const DEFAULT_TEXT_FIELDS = ['content', 'text', 'body', 'lyrics', 'markdown'];
@@ -1159,6 +1160,80 @@ async function runRuntimeLog({ args, options = {}, logger, t }) {
1159
1160
  }
1160
1161
 
1161
1162
 
1163
+ /**
1164
+ * aioson agent:done . --agent=<name> --summary="..." [--title="..."] [--status=completed|failed]
1165
+ *
1166
+ * Safe self-registration for official agents invoked directly (not via workflow:next or live:start).
1167
+ * - If an active live session exists for the agent: appends a completion event without closing the session.
1168
+ * - If no session exists: creates a standalone task+run and immediately marks it completed.
1169
+ *
1170
+ * Intended to be called ONCE at the very end of an agent session, after delivering the main artifact.
1171
+ */
1172
+ async function runAgentDone({ args, options = {}, logger, t }) {
1173
+ const targetDir = resolveTargetDir(args);
1174
+ const agentName = String(options.agent || '').trim();
1175
+ if (!agentName) {
1176
+ throw new Error('--agent is required');
1177
+ }
1178
+ const normalizedAgent = agentName.startsWith('@') ? agentName : `@${agentName}`;
1179
+ const summary = String(options.summary || options.message || `${normalizedAgent} session completed`).trim();
1180
+ const title = options.title ? String(options.title).trim() : null;
1181
+ const status = options.status || 'completed';
1182
+
1183
+ const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
1184
+
1185
+ try {
1186
+ const session = await readAgentSession(runtimeDir, normalizedAgent);
1187
+ const hasActiveSession = session && !session.finished && session.runKey;
1188
+
1189
+ if (hasActiveSession) {
1190
+ // Live or tracked session is already open — only append a completion note.
1191
+ // Do NOT close the session: live:handoff or live:close owns the lifecycle.
1192
+ appendRunEvent(db, {
1193
+ runKey: session.runKey,
1194
+ eventType: 'agent_done',
1195
+ phase: 'live',
1196
+ status: 'running',
1197
+ message: summary
1198
+ });
1199
+
1200
+ if (!options.json) {
1201
+ logger.log(`agent:done — ${normalizedAgent} | live session active, event logged | run: ${session.runKey} (${dbPath})`);
1202
+ }
1203
+
1204
+ if (isDocCreatingAgent(normalizedAgent)) {
1205
+ backupAiosonDocs(targetDir).catch(() => {});
1206
+ }
1207
+
1208
+ return { ok: true, targetDir, dbPath, agent: normalizedAgent, mode: 'live_event', runKey: session.runKey };
1209
+ }
1210
+
1211
+ // No active session — create a standalone task+run and immediately complete it.
1212
+ const { runKey, taskKey } = await logAgentEvent(db, runtimeDir, {
1213
+ agentName: normalizedAgent,
1214
+ message: summary,
1215
+ type: 'completed',
1216
+ taskTitle: title || normalizedAgent,
1217
+ finish: true,
1218
+ status,
1219
+ summary
1220
+ });
1221
+
1222
+ if (!options.json) {
1223
+ logger.log(`agent:done — ${normalizedAgent} | task: ${taskKey} | run: ${runKey} (${dbPath})`);
1224
+ }
1225
+
1226
+ if (isDocCreatingAgent(normalizedAgent)) {
1227
+ backupAiosonDocs(targetDir).catch(() => {});
1228
+ }
1229
+
1230
+ return { ok: true, targetDir, dbPath, agent: normalizedAgent, mode: 'standalone', runKey, taskKey };
1231
+ } finally {
1232
+ db.close();
1233
+ }
1234
+ }
1235
+
1236
+
1162
1237
  async function runRuntimeSessionStart({ args, options = {}, logger, t }) {
1163
1238
  const targetDir = resolveTargetDir(args);
1164
1239
  const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
@@ -1683,6 +1758,171 @@ function parseFrontmatter(content) {
1683
1758
  return result;
1684
1759
  }
1685
1760
 
1761
+ /**
1762
+ * Parses a duration string like "24h", "30m", "7d" into milliseconds.
1763
+ * Falls back to treating the raw value as hours.
1764
+ */
1765
+ function parseDurationMs(value, defaultHours = 24) {
1766
+ const text = String(value || '').trim().toLowerCase();
1767
+ if (!text) return defaultHours * 60 * 60 * 1000;
1768
+
1769
+ const match = text.match(/^(\d+(?:\.\d+)?)\s*([hmd]?)$/);
1770
+ if (!match) return defaultHours * 60 * 60 * 1000;
1771
+
1772
+ const n = parseFloat(match[1]);
1773
+ const unit = match[2] || 'h';
1774
+ if (unit === 'd') return n * 24 * 60 * 60 * 1000;
1775
+ if (unit === 'm') return n * 60 * 1000;
1776
+ return n * 60 * 60 * 1000; // hours (default)
1777
+ }
1778
+
1779
+ /**
1780
+ * aioson agent:recover [targetDir] [--older-than=<duration>] [--dry-run]
1781
+ *
1782
+ * Detects and closes agent sessions that were abandoned (Claude Code closed before
1783
+ * agent:done was called, or live:start session was never closed).
1784
+ *
1785
+ * Sources checked:
1786
+ * 1. Session files in .aioson/.sessions/ with finished=false older than threshold.
1787
+ * 2. agent_runs rows with status='running'/'queued' and started_at older than threshold
1788
+ * that have no corresponding live session file (orphaned DB records).
1789
+ *
1790
+ * --older-than Duration threshold. Accepts: 24h (default), 8h, 30m, 7d.
1791
+ * --dry-run Report what would be recovered without making any changes.
1792
+ * --json Output JSON result.
1793
+ */
1794
+ async function runAgentRecover({ args, options = {}, logger }) {
1795
+ const targetDir = resolveTargetDir(args);
1796
+ const dryRun = Boolean(options['dry-run'] || options.dryRun);
1797
+ const olderThanMs = parseDurationMs(options['older-than'] || options.olderThan, 24);
1798
+ const cutoffMs = Date.now() - olderThanMs;
1799
+ const cutoffIso = new Date(cutoffMs).toISOString();
1800
+ const now = new Date().toISOString();
1801
+
1802
+ const { db, dbPath, runtimeDir } = await openRuntimeDb(targetDir);
1803
+
1804
+ const recovered = [];
1805
+ const skipped = [];
1806
+
1807
+ try {
1808
+ // ── 1. Scan session files ─────────────────────────────────────────────────
1809
+ const sessionsDir = path.join(runtimeDir, '.sessions');
1810
+ let sessionFiles = [];
1811
+ try {
1812
+ const entries = await fs.readdir(sessionsDir);
1813
+ sessionFiles = entries.filter((f) => f.endsWith('.json'));
1814
+ } catch {
1815
+ // .sessions dir may not exist — that's fine
1816
+ }
1817
+
1818
+ for (const file of sessionFiles) {
1819
+ const filePath = path.join(sessionsDir, file);
1820
+ let session;
1821
+ try {
1822
+ session = JSON.parse(await fs.readFile(filePath, 'utf8'));
1823
+ } catch {
1824
+ continue;
1825
+ }
1826
+
1827
+ if (session.finished) continue;
1828
+
1829
+ const startedAt = session.startedAt ? new Date(session.startedAt).getTime() : 0;
1830
+ if (startedAt > cutoffMs) {
1831
+ skipped.push({ source: 'session_file', file, reason: 'within_threshold', startedAt: session.startedAt });
1832
+ continue;
1833
+ }
1834
+
1835
+ const agentName = file.replace(/\.json$/, '');
1836
+ const runKey = session.runKey || null;
1837
+ const taskKey = session.taskKey || null;
1838
+
1839
+ if (!dryRun) {
1840
+ // Mark run as abandoned
1841
+ if (runKey) {
1842
+ const runRow = db.prepare('SELECT run_key, status FROM agent_runs WHERE run_key = ?').get(runKey);
1843
+ if (runRow && (runRow.status === 'running' || runRow.status === 'queued')) {
1844
+ db.prepare(`
1845
+ UPDATE agent_runs
1846
+ SET status = 'abandoned', summary = 'Recovered: session abandoned without close', updated_at = ?, finished_at = ?
1847
+ WHERE run_key = ?
1848
+ `).run(now, now, runKey);
1849
+ }
1850
+ }
1851
+ // Mark task as abandoned
1852
+ if (taskKey) {
1853
+ const taskRow = db.prepare('SELECT task_key, status FROM tasks WHERE task_key = ?').get(taskKey);
1854
+ if (taskRow && (taskRow.status === 'running' || taskRow.status === 'queued')) {
1855
+ db.prepare(`
1856
+ UPDATE tasks
1857
+ SET status = 'abandoned', updated_at = ?, finished_at = ?
1858
+ WHERE task_key = ?
1859
+ `).run(now, now, taskKey);
1860
+ }
1861
+ }
1862
+ // Remove session file
1863
+ try { await fs.unlink(filePath); } catch { /* noop */ }
1864
+ }
1865
+
1866
+ recovered.push({ source: 'session_file', agent: agentName, runKey, taskKey, startedAt: session.startedAt });
1867
+ }
1868
+
1869
+ // ── 2. Scan DB for orphaned running runs (no session file) ────────────────
1870
+ const orphanedRuns = db.prepare(`
1871
+ SELECT run_key, task_key, agent_name, started_at
1872
+ FROM agent_runs
1873
+ WHERE status IN ('running', 'queued')
1874
+ AND source = 'direct'
1875
+ AND started_at < ?
1876
+ `).all(cutoffIso);
1877
+
1878
+ for (const run of orphanedRuns) {
1879
+ // Skip if already recovered via session file
1880
+ if (recovered.some((r) => r.runKey === run.run_key)) continue;
1881
+
1882
+ if (!dryRun) {
1883
+ db.prepare(`
1884
+ UPDATE agent_runs
1885
+ SET status = 'abandoned', summary = 'Recovered: orphaned run with no session file', updated_at = ?, finished_at = ?
1886
+ WHERE run_key = ?
1887
+ `).run(now, now, run.run_key);
1888
+
1889
+ if (run.task_key) {
1890
+ const taskRow = db.prepare('SELECT task_key, status FROM tasks WHERE task_key = ?').get(run.task_key);
1891
+ if (taskRow && (taskRow.status === 'running' || taskRow.status === 'queued')) {
1892
+ db.prepare(`
1893
+ UPDATE tasks
1894
+ SET status = 'abandoned', updated_at = ?, finished_at = ?
1895
+ WHERE task_key = ?
1896
+ `).run(now, now, run.task_key);
1897
+ }
1898
+ }
1899
+ }
1900
+
1901
+ recovered.push({ source: 'orphaned_run', agent: run.agent_name, runKey: run.run_key, taskKey: run.task_key, startedAt: run.started_at });
1902
+ }
1903
+
1904
+ // ── Output ────────────────────────────────────────────────────────────────
1905
+ const olderThanLabel = options['older-than'] || options.olderThan || '24h';
1906
+ if (recovered.length === 0) {
1907
+ logger.log(`agent:recover — no abandoned sessions found older than ${olderThanLabel} (${dbPath})`);
1908
+ } else {
1909
+ const verb = dryRun ? '[dry-run] would recover' : 'recovered';
1910
+ logger.log(`agent:recover — ${verb} ${recovered.length} abandoned session(s) older than ${olderThanLabel} (${dbPath})`);
1911
+ for (const r of recovered) {
1912
+ logger.log(` ${r.agent} started: ${r.startedAt || '?'} run: ${r.runKey || '—'} [${r.source}]`);
1913
+ }
1914
+ }
1915
+ if (skipped.length > 0) {
1916
+ logger.log(` skipped ${skipped.length} session(s) within threshold.`);
1917
+ }
1918
+
1919
+ return { ok: true, targetDir, dbPath, dryRun, cutoff: cutoffIso, recovered, skipped };
1920
+ } finally {
1921
+ db.close();
1922
+ }
1923
+ }
1924
+
1925
+
1686
1926
  /**
1687
1927
  * aioson runtime:prune [targetDir] --older-than=<days>
1688
1928
  *
@@ -1767,6 +2007,8 @@ module.exports = {
1767
2007
  runRuntimeFail,
1768
2008
  runRuntimeStatus,
1769
2009
  runRuntimeLog,
2010
+ runAgentDone,
2011
+ runAgentRecover,
1770
2012
  runRuntimeSessionStart,
1771
2013
  runRuntimeSessionLog,
1772
2014
  runRuntimeSessionFinish,
@@ -8,7 +8,8 @@ const {
8
8
  calculateClassification,
9
9
  normalizeBoolean,
10
10
  renderProjectContext,
11
- writeProjectContext
11
+ writeProjectContext,
12
+ renderSquadApiSection
12
13
  } = require('../context-writer');
13
14
  const { applyAgentLocale } = require('../locales');
14
15
  const { openRuntimeDb, logAgentEvent } = require('../runtime-store');
@@ -208,6 +209,7 @@ function applyExplicitOverrides(data, options, detectedInstalled) {
208
209
  const langValue = options.language ?? options.lang;
209
210
  if (langValue !== undefined) output.conversationLanguage = String(langValue);
210
211
  if (hasOption(options, 'design-skill')) output.designSkill = String(options['design-skill']);
212
+ if (hasOption(options, 'test-runner')) output.testRunner = String(options['test-runner']);
211
213
  if (hasOption(options, 'web3-enabled')) {
212
214
  output.web3Enabled = normalizeBoolean(options['web3-enabled'], output.web3Enabled);
213
215
  }
@@ -469,6 +471,7 @@ async function runSetupContext({ args, options, logger, t }) {
469
471
  frameworkInstalled: detectedInstalled,
470
472
  conversationLanguage: 'en',
471
473
  designSkill: '',
474
+ testRunner: '',
472
475
  web3Enabled: inferredWeb3Enabled,
473
476
  web3Networks: inferredWeb3Enabled ? inferWeb3Network(detectedFramework) : '',
474
477
  contractFramework: inferredWeb3Enabled ? detectedFramework : '',
@@ -616,7 +619,9 @@ async function runSetupContext({ args, options, logger, t }) {
616
619
  }
617
620
 
618
621
  const content = renderProjectContext(data);
619
- const filePath = await writeProjectContext(targetDir, content);
622
+ const squadApiSection = await renderSquadApiSection(targetDir);
623
+ const fullContent = squadApiSection ? content + '\n' + squadApiSection + '\n' : content;
624
+ const filePath = await writeProjectContext(targetDir, fullContent);
620
625
  const localeApplyResult = await applyAgentLocale(targetDir, data.conversationLanguage, {
621
626
  dryRun: false
622
627
  });