@jaimevalasek/aioson 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/README.md +19 -2
  2. package/docs/pt/README.md +62 -2
  3. package/docs/pt/advisor-spec.md +5 -5
  4. package/docs/pt/agentes-customizados.md +670 -0
  5. package/docs/pt/agentes.md +111 -13
  6. package/docs/pt/automacao-squads.md +407 -0
  7. package/docs/pt/cenarios.md +3 -3
  8. package/docs/pt/clientes-ai.md +62 -0
  9. package/docs/pt/comandos-cli.md +167 -17
  10. package/docs/pt/deyvin.md +115 -0
  11. package/docs/pt/genome-3.0-spec.md +11 -11
  12. package/docs/pt/inicio-rapido.md +45 -0
  13. package/docs/pt/memoria-contexto.md +255 -0
  14. package/docs/pt/output-strategy-delivery.md +655 -0
  15. package/docs/pt/profiler-system.md +17 -17
  16. package/docs/pt/runtime-observability.md +5 -1
  17. package/docs/pt/skills.md +175 -0
  18. package/docs/pt/{squad-genoma.md → squad-genome.md} +81 -75
  19. package/docs/testing/genome-2.0-rollout.md +1 -1
  20. package/package.json +3 -3
  21. package/src/agents.js +21 -5
  22. package/src/backup-provider.js +303 -0
  23. package/src/cli.js +178 -2
  24. package/src/commands/agents.js +22 -4
  25. package/src/commands/backup.js +533 -0
  26. package/src/commands/cloud.js +17 -17
  27. package/src/commands/context-pack.js +45 -0
  28. package/src/commands/implementation-plan.js +340 -0
  29. package/src/commands/learning.js +134 -0
  30. package/src/commands/live.js +1583 -0
  31. package/src/commands/runtime.js +833 -2
  32. package/src/commands/scan-project.js +288 -24
  33. package/src/commands/setup-context.js +23 -0
  34. package/src/commands/skill.js +558 -0
  35. package/src/commands/squad-agent-create.js +788 -0
  36. package/src/commands/squad-doctor.js +51 -1
  37. package/src/commands/squad-investigate.js +261 -0
  38. package/src/commands/squad-learning.js +209 -0
  39. package/src/commands/squad-pipeline.js +247 -1
  40. package/src/commands/squad-plan.js +329 -0
  41. package/src/commands/squad-status.js +1 -1
  42. package/src/commands/squad-validate.js +57 -1
  43. package/src/commands/test-agents.js +6 -1
  44. package/src/commands/workflow-next.js +8 -1
  45. package/src/commands/workflow-status.js +250 -0
  46. package/src/constants.js +80 -16
  47. package/src/context-memory.js +837 -0
  48. package/src/context-writer.js +2 -0
  49. package/src/delivery-runner.js +319 -0
  50. package/src/genome-files.js +1 -1
  51. package/src/genome-format.js +1 -1
  52. package/src/i18n/messages/en.js +206 -7
  53. package/src/i18n/messages/es.js +123 -6
  54. package/src/i18n/messages/fr.js +122 -5
  55. package/src/i18n/messages/pt-BR.js +205 -12
  56. package/src/installer.js +30 -2
  57. package/src/lib/genomes/compat.js +1 -1
  58. package/src/runtime-store.js +780 -42
  59. package/src/session-handoff.js +77 -0
  60. package/template/.aioson/agents/analyst.md +36 -9
  61. package/template/.aioson/agents/architect.md +20 -5
  62. package/template/.aioson/agents/dev.md +135 -15
  63. package/template/.aioson/agents/deyvin.md +166 -0
  64. package/template/.aioson/agents/discovery-design-doc.md +25 -1
  65. package/template/.aioson/agents/{genoma.md → genome.md} +20 -20
  66. package/template/.aioson/agents/orache.md +371 -0
  67. package/template/.aioson/agents/orchestrator.md +37 -2
  68. package/template/.aioson/agents/pair.md +5 -0
  69. package/template/.aioson/agents/pm.md +17 -5
  70. package/template/.aioson/agents/product.md +58 -22
  71. package/template/.aioson/agents/profiler-enricher.md +1 -1
  72. package/template/.aioson/agents/profiler-forge.md +9 -9
  73. package/template/.aioson/agents/profiler-researcher.md +1 -1
  74. package/template/.aioson/agents/qa.md +17 -5
  75. package/template/.aioson/agents/setup.md +81 -5
  76. package/template/.aioson/agents/squad.md +675 -28
  77. package/template/.aioson/agents/ux-ui.md +277 -34
  78. package/template/.aioson/config.md +175 -0
  79. package/template/.aioson/context/spec.md.template +17 -0
  80. package/template/.aioson/genomes/.gitkeep +0 -0
  81. package/template/.aioson/installed-skills/.gitkeep +0 -0
  82. package/template/.aioson/locales/en/agents/analyst.md +26 -4
  83. package/template/.aioson/locales/en/agents/architect.md +10 -0
  84. package/template/.aioson/locales/en/agents/dev.md +89 -4
  85. package/template/.aioson/locales/en/agents/deyvin.md +129 -0
  86. package/template/.aioson/locales/en/agents/{genoma.md → genome.md} +14 -14
  87. package/template/.aioson/locales/en/agents/orchestrator.md +36 -2
  88. package/template/.aioson/locales/en/agents/pair.md +5 -0
  89. package/template/.aioson/locales/en/agents/pm.md +7 -0
  90. package/template/.aioson/locales/en/agents/product.md +35 -17
  91. package/template/.aioson/locales/en/agents/qa.md +7 -0
  92. package/template/.aioson/locales/en/agents/setup.md +51 -5
  93. package/template/.aioson/locales/en/agents/squad.md +203 -15
  94. package/template/.aioson/locales/en/agents/ux-ui.md +375 -35
  95. package/template/.aioson/locales/es/agents/analyst.md +16 -4
  96. package/template/.aioson/locales/es/agents/architect.md +10 -0
  97. package/template/.aioson/locales/es/agents/dev.md +70 -2
  98. package/template/.aioson/locales/es/agents/deyvin.md +89 -0
  99. package/template/.aioson/locales/es/agents/{genoma.md → genome.md} +13 -13
  100. package/template/.aioson/locales/es/agents/orache.md +103 -0
  101. package/template/.aioson/locales/es/agents/orchestrator.md +36 -2
  102. package/template/.aioson/locales/es/agents/pair.md +5 -0
  103. package/template/.aioson/locales/es/agents/pm.md +7 -0
  104. package/template/.aioson/locales/es/agents/product.md +13 -3
  105. package/template/.aioson/locales/es/agents/qa.md +7 -0
  106. package/template/.aioson/locales/es/agents/setup.md +28 -5
  107. package/template/.aioson/locales/es/agents/squad.md +221 -15
  108. package/template/.aioson/locales/es/agents/ux-ui.md +26 -25
  109. package/template/.aioson/locales/fr/agents/analyst.md +16 -4
  110. package/template/.aioson/locales/fr/agents/architect.md +10 -0
  111. package/template/.aioson/locales/fr/agents/dev.md +70 -2
  112. package/template/.aioson/locales/fr/agents/deyvin.md +89 -0
  113. package/template/.aioson/locales/fr/agents/{genoma.md → genome.md} +7 -7
  114. package/template/.aioson/locales/fr/agents/orache.md +104 -0
  115. package/template/.aioson/locales/fr/agents/orchestrator.md +36 -2
  116. package/template/.aioson/locales/fr/agents/pair.md +5 -0
  117. package/template/.aioson/locales/fr/agents/pm.md +7 -0
  118. package/template/.aioson/locales/fr/agents/product.md +13 -3
  119. package/template/.aioson/locales/fr/agents/qa.md +7 -0
  120. package/template/.aioson/locales/fr/agents/setup.md +28 -5
  121. package/template/.aioson/locales/fr/agents/squad.md +216 -10
  122. package/template/.aioson/locales/fr/agents/ux-ui.md +26 -25
  123. package/template/.aioson/locales/pt-BR/agents/analyst.md +26 -4
  124. package/template/.aioson/locales/pt-BR/agents/architect.md +10 -0
  125. package/template/.aioson/locales/pt-BR/agents/dev.md +93 -4
  126. package/template/.aioson/locales/pt-BR/agents/deyvin.md +129 -0
  127. package/template/.aioson/locales/pt-BR/agents/{genoma.md → genome.md} +49 -49
  128. package/template/.aioson/locales/pt-BR/agents/orache.md +137 -0
  129. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +36 -2
  130. package/template/.aioson/locales/pt-BR/agents/pair.md +5 -0
  131. package/template/.aioson/locales/pt-BR/agents/pm.md +7 -0
  132. package/template/.aioson/locales/pt-BR/agents/product.md +35 -17
  133. package/template/.aioson/locales/pt-BR/agents/qa.md +7 -0
  134. package/template/.aioson/locales/pt-BR/agents/setup.md +51 -5
  135. package/template/.aioson/locales/pt-BR/agents/squad.md +486 -47
  136. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +361 -22
  137. package/template/.aioson/my-agents/.gitkeep +0 -0
  138. package/template/.aioson/rules/.gitkeep +0 -0
  139. package/template/.aioson/rules/squad/.gitkeep +0 -0
  140. package/template/.aioson/rules/squad/README.md +50 -0
  141. package/template/.aioson/schemas/genome-meta.schema.json +1 -1
  142. package/template/.aioson/schemas/genome.schema.json +1 -1
  143. package/template/.aioson/schemas/squad-blueprint.schema.json +11 -0
  144. package/template/.aioson/schemas/squad-manifest.schema.json +257 -1
  145. package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +157 -0
  146. package/template/.aioson/skills/design/cognitive-core-ui/references/components.md +407 -0
  147. package/template/.aioson/skills/design/cognitive-core-ui/references/dashboards.md +172 -0
  148. package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +490 -0
  149. package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +237 -0
  150. package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +289 -0
  151. package/template/.aioson/skills/design/cognitive-core-ui/references/websites.md +350 -0
  152. package/template/.aioson/skills/design/interface-design/SKILL.md +47 -0
  153. package/template/.aioson/skills/design/interface-design/references/components-and-states.md +105 -0
  154. package/template/.aioson/skills/design/interface-design/references/design-directions.md +101 -0
  155. package/template/.aioson/skills/design/interface-design/references/handoff-and-quality.md +71 -0
  156. package/template/.aioson/skills/design/interface-design/references/intent-and-domain.md +74 -0
  157. package/template/.aioson/skills/design/interface-design/references/tokens-and-depth.md +173 -0
  158. package/template/.aioson/skills/design/premium-command-center-ui/SKILL.md +62 -0
  159. package/template/.aioson/skills/design/premium-command-center-ui/references/operations.md +74 -0
  160. package/template/.aioson/skills/design/premium-command-center-ui/references/patterns.md +116 -0
  161. package/template/.aioson/skills/design/premium-command-center-ui/references/validation.md +47 -0
  162. package/template/.aioson/skills/design/premium-command-center-ui/references/visual-system.md +215 -0
  163. package/template/.aioson/skills/design-system/SKILL.md +92 -0
  164. package/template/.aioson/skills/design-system/cognitive-core-ui.skill +0 -0
  165. package/template/.aioson/skills/design-system/components/SKILL.md +274 -0
  166. package/template/.aioson/skills/design-system/components/SKILL.md:Zone.Identifier +0 -0
  167. package/template/.aioson/skills/design-system/dashboards/SKILL.md +184 -0
  168. package/template/.aioson/skills/design-system/dashboards/SKILL.md:Zone.Identifier +0 -0
  169. package/template/.aioson/skills/design-system/foundations/SKILL.md +250 -0
  170. package/template/.aioson/skills/design-system/foundations/SKILL.md:Zone.Identifier +0 -0
  171. package/template/.aioson/skills/design-system/motion/SKILL.md +197 -0
  172. package/template/.aioson/skills/design-system/motion/SKILL.md:Zone.Identifier +0 -0
  173. package/template/.aioson/skills/design-system/patterns/SKILL.md +231 -0
  174. package/template/.aioson/skills/design-system/patterns/SKILL.md:Zone.Identifier +0 -0
  175. package/template/.aioson/skills/squad/SKILL.md +58 -0
  176. package/template/.aioson/skills/squad/domains/.gitkeep +0 -0
  177. package/template/.aioson/skills/squad/formats/.gitkeep +0 -0
  178. package/template/.aioson/skills/squad/patterns/.gitkeep +0 -0
  179. package/template/.aioson/skills/squad/references/.gitkeep +0 -0
  180. package/template/.aioson/tasks/implementation-plan.md +288 -0
  181. package/template/.aioson/tasks/squad-create.md +1 -1
  182. package/template/.aioson/tasks/squad-execution-plan.md +279 -0
  183. package/template/.aioson/tasks/squad-export.md +1 -1
  184. package/template/.aioson/tasks/squad-investigate.md +44 -0
  185. package/template/.aioson/tasks/squad-learning-review.md +44 -0
  186. package/template/.aioson/tasks/squad-output-config.md +177 -0
  187. package/template/.aioson/tasks/squad-validate.md +1 -1
  188. package/template/.claude/commands/aioson/agent/deyvin.md +5 -0
  189. package/template/.claude/commands/aioson/agent/discovery-design-doc.md +5 -0
  190. package/template/.claude/commands/aioson/agent/genome.md +5 -0
  191. package/template/.claude/commands/aioson/agent/product.md +5 -0
  192. package/template/.claude/commands/aioson/agent/profiler-enricher.md +5 -0
  193. package/template/.claude/commands/aioson/agent/profiler-forge.md +5 -0
  194. package/template/.claude/commands/aioson/agent/profiler-researcher.md +5 -0
  195. package/template/.claude/commands/aioson/agent/squad.md +5 -0
  196. package/template/.gemini/GEMINI.md +2 -0
  197. package/template/.gemini/commands/aios-deyvin.toml +6 -0
  198. package/template/.gemini/commands/aios-pair.toml +6 -0
  199. package/template/AGENTS.md +34 -6
  200. package/template/CLAUDE.md +31 -4
  201. package/template/OPENCODE.md +6 -2
  202. package/template/squad-searches/.gitkeep +0 -0
  203. package/template/.aioson/skills/static/interface-design.md +0 -372
  204. package/template/.aioson/skills/static/premium-command-center-ui.md +0 -190
  205. /package/template/.aioson/{genomas → docs}/.gitkeep +0 -0
  206. /package/template/.claude/commands/aioson/{analyst.md → agent/analyst.md} +0 -0
  207. /package/template/.claude/commands/aioson/{architect.md → agent/architect.md} +0 -0
  208. /package/template/.claude/commands/aioson/{dev.md → agent/dev.md} +0 -0
  209. /package/template/.claude/commands/aioson/{orchestrator.md → agent/orchestrator.md} +0 -0
  210. /package/template/.claude/commands/aioson/{pm.md → agent/pm.md} +0 -0
  211. /package/template/.claude/commands/aioson/{qa.md → agent/qa.md} +0 -0
  212. /package/template/.claude/commands/aioson/{setup.md → agent/setup.md} +0 -0
  213. /package/template/.claude/commands/aioson/{ux-ui.md → agent/ux-ui.md} +0 -0
@@ -0,0 +1,533 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const {
6
+ resolveRuntimePaths,
7
+ openRuntimeDb,
8
+ runtimeStoreExists
9
+ } = require('../runtime-store');
10
+ const { createProvider, contentHash } = require('../backup-provider');
11
+
12
+ function resolveTargetDir(args) {
13
+ return path.resolve(process.cwd(), args[0] || '.');
14
+ }
15
+
16
+ async function readBackupConfig(targetDir) {
17
+ try {
18
+ const raw = await fs.readFile(path.join(targetDir, '.aioson/install.json'), 'utf8');
19
+ const meta = JSON.parse(raw);
20
+ return meta.backup || null;
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ function nowIso() {
27
+ return new Date().toISOString();
28
+ }
29
+
30
+ // ── Backup manifest helpers ──
31
+
32
+ function getManifestEntry(db, recordKey) {
33
+ return db.prepare('SELECT * FROM backup_manifest WHERE record_key = ?').get(recordKey);
34
+ }
35
+
36
+ function upsertManifest(db, recordKey, recordType, hash, remoteKey) {
37
+ db.prepare(`
38
+ INSERT INTO backup_manifest (record_key, record_type, content_hash, backed_up_at, remote_key)
39
+ VALUES (?, ?, ?, ?, ?)
40
+ ON CONFLICT(record_key) DO UPDATE SET
41
+ content_hash = excluded.content_hash,
42
+ backed_up_at = excluded.backed_up_at,
43
+ remote_key = excluded.remote_key
44
+ `).run(recordKey, recordType, hash, nowIso(), remoteKey);
45
+ }
46
+
47
+ function needsBackup(db, recordKey, currentHash) {
48
+ const existing = getManifestEntry(db, recordKey);
49
+ if (!existing) return true;
50
+ return existing.content_hash !== currentHash;
51
+ }
52
+
53
+ // ── Table exporters ──
54
+
55
+ function exportTasks(db) {
56
+ return db.prepare('SELECT * FROM tasks ORDER BY created_at').all();
57
+ }
58
+
59
+ function exportRuns(db) {
60
+ return db.prepare('SELECT * FROM agent_runs ORDER BY started_at').all();
61
+ }
62
+
63
+ function exportAgentEvents(db) {
64
+ return db.prepare('SELECT * FROM agent_events ORDER BY created_at').all();
65
+ }
66
+
67
+ function exportExecutionEvents(db, since) {
68
+ if (since) {
69
+ return db.prepare('SELECT * FROM execution_events WHERE created_at >= ? ORDER BY created_at').all(since);
70
+ }
71
+ return db.prepare('SELECT * FROM execution_events ORDER BY created_at').all();
72
+ }
73
+
74
+ function exportArtifacts(db) {
75
+ return db.prepare('SELECT * FROM artifacts ORDER BY created_at').all();
76
+ }
77
+
78
+ function exportContentItems(db) {
79
+ return db.prepare('SELECT * FROM content_items ORDER BY created_at').all();
80
+ }
81
+
82
+ function exportDeliveryLog(db) {
83
+ return db.prepare('SELECT * FROM delivery_log ORDER BY created_at').all();
84
+ }
85
+
86
+ // ── Tables filter ──
87
+
88
+ const ALL_BACKUP_TABLES = ['tasks', 'runs', 'agent_events', 'execution_events', 'artifacts', 'content_items', 'delivery_log', 'devlogs'];
89
+
90
+ function parseTables(option) {
91
+ if (!option) return ALL_BACKUP_TABLES;
92
+ const requested = String(option).split(',').map(s => s.trim()).filter(Boolean);
93
+ return requested.length > 0 ? requested : ALL_BACKUP_TABLES;
94
+ }
95
+
96
+ // ── Main backup command ──
97
+
98
+ async function runRuntimeBackup({ args, options = {}, logger, t }) {
99
+ const targetDir = resolveTargetDir(args);
100
+ const dryRun = Boolean(options['dry-run'] || options.dryRun);
101
+ const force = Boolean(options.force);
102
+ const tables = parseTables(options.tables);
103
+
104
+ // Read backup config
105
+ const config = await readBackupConfig(targetDir);
106
+ if (!config) {
107
+ logger.error('No backup config found in .aioson/install.json. Add a "backup" section with provider settings.');
108
+ return { ok: false, error: 'missing_backup_config' };
109
+ }
110
+
111
+ // Verify runtime store exists
112
+ if (!(await runtimeStoreExists(targetDir))) {
113
+ const { dbPath } = resolveRuntimePaths(targetDir);
114
+ logger.error(`Runtime store not found at ${dbPath}. Run 'aioson runtime:init' first.`);
115
+ return { ok: false, error: 'store_missing' };
116
+ }
117
+
118
+ const provider = createProvider(config);
119
+ const { db, dbPath } = await openRuntimeDb(targetDir);
120
+
121
+ let uploaded = 0;
122
+ let skipped = 0;
123
+ const errors = [];
124
+
125
+ try {
126
+ // ── Tasks ──
127
+ if (tables.includes('tasks')) {
128
+ const tasks = exportTasks(db);
129
+ for (const task of tasks) {
130
+ const key = `tasks/${task.task_key}`;
131
+ const hash = contentHash(task);
132
+ if (!force && !needsBackup(db, `task:${task.task_key}`, hash)) {
133
+ skipped++;
134
+ continue;
135
+ }
136
+ if (!dryRun) {
137
+ try {
138
+ const remoteKey = `${key}.json`;
139
+ await provider.upload(remoteKey, Buffer.from(JSON.stringify(task, null, 2)), 'application/json');
140
+ upsertManifest(db, `task:${task.task_key}`, 'task', hash, remoteKey);
141
+ uploaded++;
142
+ } catch (err) {
143
+ errors.push(`task:${task.task_key}: ${err.message}`);
144
+ }
145
+ } else {
146
+ uploaded++;
147
+ }
148
+ }
149
+ logger.log(` tasks: ${tasks.length} found`);
150
+ }
151
+
152
+ // ── Agent runs ──
153
+ if (tables.includes('runs')) {
154
+ const runs = exportRuns(db);
155
+ for (const run of runs) {
156
+ const key = `runs/${run.run_key}`;
157
+ const hash = contentHash(run);
158
+ if (!force && !needsBackup(db, `run:${run.run_key}`, hash)) {
159
+ skipped++;
160
+ continue;
161
+ }
162
+ if (!dryRun) {
163
+ try {
164
+ const remoteKey = `${key}.json`;
165
+ await provider.upload(remoteKey, Buffer.from(JSON.stringify(run, null, 2)), 'application/json');
166
+ upsertManifest(db, `run:${run.run_key}`, 'run', hash, remoteKey);
167
+ uploaded++;
168
+ } catch (err) {
169
+ errors.push(`run:${run.run_key}: ${err.message}`);
170
+ }
171
+ } else {
172
+ uploaded++;
173
+ }
174
+ }
175
+ logger.log(` runs: ${runs.length} found`);
176
+ }
177
+
178
+ // ── Events (batched) ──
179
+ if (tables.includes('agent_events')) {
180
+ const events = exportAgentEvents(db);
181
+ if (events.length > 0) {
182
+ const batchKey = `events/agent-events-${Date.now()}`;
183
+ const hash = contentHash(events);
184
+ if (force || needsBackup(db, 'agent_events:batch', hash)) {
185
+ if (!dryRun) {
186
+ try {
187
+ const remoteKey = `${batchKey}.json`;
188
+ await provider.upload(remoteKey, Buffer.from(JSON.stringify(events, null, 2)), 'application/json');
189
+ upsertManifest(db, 'agent_events:batch', 'agent_events', hash, remoteKey);
190
+ uploaded++;
191
+ } catch (err) {
192
+ errors.push(`agent_events: ${err.message}`);
193
+ }
194
+ } else {
195
+ uploaded++;
196
+ }
197
+ } else {
198
+ skipped++;
199
+ }
200
+ logger.log(` agent_events: ${events.length} found`);
201
+ }
202
+ }
203
+
204
+ if (tables.includes('execution_events')) {
205
+ // Get last backup time for incremental
206
+ const lastEntry = getManifestEntry(db, 'execution_events:batch');
207
+ const since = (!force && lastEntry) ? lastEntry.backed_up_at : null;
208
+ const events = exportExecutionEvents(db, since);
209
+ if (events.length > 0) {
210
+ const batchKey = `events/execution-events-${Date.now()}`;
211
+ const hash = contentHash(events);
212
+ if (force || needsBackup(db, 'execution_events:batch', hash)) {
213
+ if (!dryRun) {
214
+ try {
215
+ const remoteKey = `${batchKey}.json`;
216
+ await provider.upload(remoteKey, Buffer.from(JSON.stringify(events, null, 2)), 'application/json');
217
+ upsertManifest(db, 'execution_events:batch', 'execution_events', hash, remoteKey);
218
+ uploaded++;
219
+ } catch (err) {
220
+ errors.push(`execution_events: ${err.message}`);
221
+ }
222
+ } else {
223
+ uploaded++;
224
+ }
225
+ } else {
226
+ skipped++;
227
+ }
228
+ logger.log(` execution_events: ${events.length} found${since ? ' (incremental)' : ''}`);
229
+ }
230
+ }
231
+
232
+ // ── Artifacts ──
233
+ if (tables.includes('artifacts')) {
234
+ const artifacts = exportArtifacts(db);
235
+ for (const art of artifacts) {
236
+ const key = `artifacts/${art.id}`;
237
+ const hash = contentHash(art);
238
+ if (!force && !needsBackup(db, `artifact:${art.id}`, hash)) {
239
+ skipped++;
240
+ continue;
241
+ }
242
+ if (!dryRun) {
243
+ try {
244
+ // Upload metadata
245
+ const metaKey = `${key}.json`;
246
+ await provider.upload(metaKey, Buffer.from(JSON.stringify(art, null, 2)), 'application/json');
247
+
248
+ // Upload file if it exists and is under 10MB
249
+ if (art.file_path) {
250
+ const absPath = path.isAbsolute(art.file_path)
251
+ ? art.file_path
252
+ : path.join(targetDir, art.file_path);
253
+ try {
254
+ const stat = await fs.stat(absPath);
255
+ if (stat.isFile() && stat.size < 10 * 1024 * 1024) {
256
+ const fileBuffer = await fs.readFile(absPath);
257
+ const ext = path.extname(art.file_path) || '';
258
+ await provider.upload(`${key}/file${ext}`, fileBuffer);
259
+ }
260
+ } catch {
261
+ // File may not exist locally — skip
262
+ }
263
+ }
264
+
265
+ upsertManifest(db, `artifact:${art.id}`, 'artifact', hash, metaKey);
266
+ uploaded++;
267
+ } catch (err) {
268
+ errors.push(`artifact:${art.id}: ${err.message}`);
269
+ }
270
+ } else {
271
+ uploaded++;
272
+ }
273
+ }
274
+ logger.log(` artifacts: ${artifacts.length} found`);
275
+ }
276
+
277
+ // ── Content items ──
278
+ if (tables.includes('content_items')) {
279
+ const items = exportContentItems(db);
280
+ for (const item of items) {
281
+ const key = `content/${item.content_key}`;
282
+ const hash = contentHash(item);
283
+ if (!force && !needsBackup(db, `content:${item.content_key}`, hash)) {
284
+ skipped++;
285
+ continue;
286
+ }
287
+ if (!dryRun) {
288
+ try {
289
+ const remoteKey = `${key}.json`;
290
+ await provider.upload(remoteKey, Buffer.from(JSON.stringify(item, null, 2)), 'application/json');
291
+ upsertManifest(db, `content:${item.content_key}`, 'content', hash, remoteKey);
292
+ uploaded++;
293
+ } catch (err) {
294
+ errors.push(`content:${item.content_key}: ${err.message}`);
295
+ }
296
+ } else {
297
+ uploaded++;
298
+ }
299
+ }
300
+ logger.log(` content_items: ${items.length} found`);
301
+ }
302
+
303
+ // ── Delivery log ──
304
+ if (tables.includes('delivery_log')) {
305
+ const logs = exportDeliveryLog(db);
306
+ if (logs.length > 0) {
307
+ const hash = contentHash(logs);
308
+ if (force || needsBackup(db, 'delivery_log:batch', hash)) {
309
+ if (!dryRun) {
310
+ try {
311
+ const remoteKey = `delivery/delivery-log-${Date.now()}.json`;
312
+ await provider.upload(remoteKey, Buffer.from(JSON.stringify(logs, null, 2)), 'application/json');
313
+ upsertManifest(db, 'delivery_log:batch', 'delivery_log', hash, remoteKey);
314
+ uploaded++;
315
+ } catch (err) {
316
+ errors.push(`delivery_log: ${err.message}`);
317
+ }
318
+ } else {
319
+ uploaded++;
320
+ }
321
+ } else {
322
+ skipped++;
323
+ }
324
+ logger.log(` delivery_log: ${logs.length} found`);
325
+ }
326
+ }
327
+
328
+ // ── Devlogs (files from aioson-logs/) ──
329
+ if (tables.includes('devlogs')) {
330
+ const logsDir = path.join(targetDir, 'aioson-logs');
331
+ try {
332
+ const entries = await fs.readdir(logsDir);
333
+ const devlogFiles = entries.filter(f => f.startsWith('devlog-') && f.endsWith('.md'));
334
+ for (const file of devlogFiles) {
335
+ const filePath = path.join(logsDir, file);
336
+ const content = await fs.readFile(filePath, 'utf8');
337
+ const hash = contentHash(content);
338
+ if (!force && !needsBackup(db, `devlog:${file}`, hash)) {
339
+ skipped++;
340
+ continue;
341
+ }
342
+ if (!dryRun) {
343
+ try {
344
+ const remoteKey = `devlogs/${file}`;
345
+ await provider.upload(remoteKey, Buffer.from(content, 'utf8'), 'text/markdown');
346
+ upsertManifest(db, `devlog:${file}`, 'devlog', hash, remoteKey);
347
+ uploaded++;
348
+ } catch (err) {
349
+ errors.push(`devlog:${file}: ${err.message}`);
350
+ }
351
+ } else {
352
+ uploaded++;
353
+ }
354
+ }
355
+ logger.log(` devlogs: ${devlogFiles.length} files found`);
356
+ } catch {
357
+ // No aioson-logs/ — skip
358
+ }
359
+ }
360
+
361
+ // ── Upload backup index ──
362
+ if (!dryRun && uploaded > 0) {
363
+ const index = {
364
+ schemaVersion: 1,
365
+ provider: config.provider,
366
+ lastBackupAt: nowIso(),
367
+ uploaded,
368
+ skipped,
369
+ errors: errors.length,
370
+ tables
371
+ };
372
+ try {
373
+ await provider.upload('backup-index.json', Buffer.from(JSON.stringify(index, null, 2)), 'application/json');
374
+ } catch {
375
+ // Non-fatal
376
+ }
377
+ }
378
+
379
+ const prefix = dryRun ? '[DRY RUN] ' : '';
380
+ logger.log(`\n${prefix}Backup ${errors.length === 0 ? 'complete' : 'completed with errors'}:`);
381
+ logger.log(` Uploaded: ${uploaded}, Skipped (unchanged): ${skipped}`);
382
+ if (errors.length > 0) {
383
+ logger.log(` Errors: ${errors.length}`);
384
+ for (const e of errors.slice(0, 5)) {
385
+ logger.log(` - ${e}`);
386
+ }
387
+ }
388
+
389
+ return {
390
+ ok: errors.length === 0,
391
+ dbPath,
392
+ uploaded,
393
+ skipped,
394
+ errors,
395
+ dryRun,
396
+ timestamp: nowIso()
397
+ };
398
+ } finally {
399
+ db.close();
400
+ }
401
+ }
402
+
403
+ // ── Restore command ──
404
+
405
+ async function runRuntimeRestore({ args, options = {}, logger, t }) {
406
+ const targetDir = resolveTargetDir(args);
407
+ const dryRun = Boolean(options['dry-run'] || options.dryRun);
408
+ const tables = parseTables(options.tables);
409
+
410
+ const config = await readBackupConfig(targetDir);
411
+ if (!config) {
412
+ logger.error('No backup config found in .aioson/install.json.');
413
+ return { ok: false, error: 'missing_backup_config' };
414
+ }
415
+
416
+ const provider = createProvider(config);
417
+
418
+ // Check backup index exists
419
+ const indexBuffer = await provider.download('backup-index.json');
420
+ if (!indexBuffer) {
421
+ logger.error('No backup-index.json found at the remote. Has a backup been created?');
422
+ return { ok: false, error: 'no_backup_index' };
423
+ }
424
+
425
+ const backupIndex = JSON.parse(indexBuffer.toString());
426
+ logger.log(`Found backup from ${backupIndex.lastBackupAt} (schema v${backupIndex.schemaVersion})`);
427
+
428
+ const { db, dbPath } = await openRuntimeDb(targetDir);
429
+ let restored = 0;
430
+ const errors = [];
431
+
432
+ try {
433
+ const restoreTable = async (prefix, tableName, insertFn) => {
434
+ if (!tables.includes(tableName)) return;
435
+ const items = await provider.list(`${prefix}/`);
436
+ for (const item of items) {
437
+ if (!item.key.endsWith('.json')) continue;
438
+ try {
439
+ const buffer = await provider.download(item.key);
440
+ if (!buffer) continue;
441
+ const data = JSON.parse(buffer.toString());
442
+ if (!dryRun) {
443
+ insertFn(data);
444
+ }
445
+ restored++;
446
+ } catch (err) {
447
+ errors.push(`${item.key}: ${err.message}`);
448
+ }
449
+ }
450
+ logger.log(` ${tableName}: ${items.length} items`);
451
+ };
452
+
453
+ // Restore tasks
454
+ await restoreTable('tasks', 'tasks', (task) => {
455
+ db.prepare(`
456
+ INSERT OR REPLACE INTO tasks (task_key, squad_slug, session_key, title, goal, status, created_by, created_at, updated_at, finished_at)
457
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
458
+ `).run(task.task_key, task.squad_slug, task.session_key, task.title, task.goal, task.status, task.created_by, task.created_at, task.updated_at, task.finished_at);
459
+ });
460
+
461
+ // Restore runs
462
+ await restoreTable('runs', 'runs', (run) => {
463
+ db.prepare(`
464
+ INSERT OR REPLACE INTO agent_runs (run_key, task_key, agent_name, agent_kind, squad_slug, session_key, source, workflow_id, workflow_stage, parent_run_key, title, status, summary, used_skills_json, output_path, started_at, updated_at, finished_at)
465
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
466
+ `).run(run.run_key, run.task_key, run.agent_name, run.agent_kind, run.squad_slug, run.session_key, run.source, run.workflow_id, run.workflow_stage, run.parent_run_key, run.title, run.status, run.summary, run.used_skills_json, run.output_path, run.started_at, run.updated_at, run.finished_at);
467
+ });
468
+
469
+ // Restore artifacts
470
+ await restoreTable('artifacts', 'artifacts', (art) => {
471
+ db.prepare(`
472
+ INSERT OR REPLACE INTO artifacts (id, task_key, run_key, squad_slug, agent_name, kind, title, file_path, created_at)
473
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
474
+ `).run(art.id, art.task_key, art.run_key, art.squad_slug, art.agent_name, art.kind, art.title, art.file_path, art.created_at);
475
+ });
476
+
477
+ // Restore content items
478
+ await restoreTable('content', 'content_items', (item) => {
479
+ db.prepare(`
480
+ INSERT OR REPLACE INTO content_items (content_key, task_key, run_key, squad_slug, session_key, title, content_type, layout_type, status, summary, blueprint_slug, used_skills_json, payload_json, json_path, html_path, created_by_agent, created_at, updated_at)
481
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
482
+ `).run(item.content_key, item.task_key, item.run_key, item.squad_slug, item.session_key, item.title, item.content_type, item.layout_type, item.status, item.summary, item.blueprint_slug, item.used_skills_json, item.payload_json, item.json_path, item.html_path, item.created_by_agent, item.created_at, item.updated_at);
483
+ });
484
+
485
+ // Restore devlog files
486
+ if (tables.includes('devlogs')) {
487
+ const devlogItems = await provider.list('devlogs/');
488
+ const logsDir = path.join(targetDir, 'aioson-logs');
489
+ await fs.mkdir(logsDir, { recursive: true });
490
+ for (const item of devlogItems) {
491
+ if (!item.key.endsWith('.md')) continue;
492
+ try {
493
+ const buffer = await provider.download(item.key);
494
+ if (!buffer) continue;
495
+ const filename = path.basename(item.key);
496
+ if (!dryRun) {
497
+ await fs.writeFile(path.join(logsDir, filename), buffer);
498
+ }
499
+ restored++;
500
+ } catch (err) {
501
+ errors.push(`devlog:${item.key}: ${err.message}`);
502
+ }
503
+ }
504
+ logger.log(` devlogs: ${devlogItems.length} files`);
505
+ }
506
+
507
+ const prefix = dryRun ? '[DRY RUN] ' : '';
508
+ logger.log(`\n${prefix}Restore ${errors.length === 0 ? 'complete' : 'completed with errors'}:`);
509
+ logger.log(` Restored: ${restored}`);
510
+ if (errors.length > 0) {
511
+ logger.log(` Errors: ${errors.length}`);
512
+ for (const e of errors.slice(0, 5)) {
513
+ logger.log(` - ${e}`);
514
+ }
515
+ }
516
+
517
+ return {
518
+ ok: errors.length === 0,
519
+ dbPath,
520
+ restored,
521
+ errors,
522
+ dryRun,
523
+ backupIndex
524
+ };
525
+ } finally {
526
+ db.close();
527
+ }
528
+ }
529
+
530
+ module.exports = {
531
+ runRuntimeBackup,
532
+ runRuntimeRestore
533
+ };
@@ -35,7 +35,7 @@ function squadImportFilePath(projectDir, slug, versionNumber) {
35
35
  }
36
36
 
37
37
  function genomeImportFilePath(projectDir, slug, versionNumber) {
38
- const safeSlug = sanitizeSegment(slug, 'genoma');
38
+ const safeSlug = sanitizeSegment(slug, 'genome');
39
39
  const safeVersion = sanitizeSegment(versionNumber, 'latest');
40
40
  return path.join(cloudImportsRoot(projectDir), 'genomes', safeSlug, `${safeVersion}.json`);
41
41
  }
@@ -53,7 +53,7 @@ function historyImportFilePath(projectDir, slug, versionNumber) {
53
53
  }
54
54
 
55
55
  function genomeHistoryImportFilePath(projectDir, slug, versionNumber) {
56
- const safeSlug = sanitizeSegment(slug, 'genoma');
56
+ const safeSlug = sanitizeSegment(slug, 'genome');
57
57
  const safeVersion = sanitizeSegment(versionNumber, 'latest');
58
58
  return path.join(
59
59
  cloudImportsRoot(projectDir),
@@ -73,7 +73,7 @@ function installedManifestPath(projectDir, slug) {
73
73
  }
74
74
 
75
75
  function installedGenomeManifestPath(projectDir, slug) {
76
- return path.join(installedRoot(projectDir), 'genomes', sanitizeSegment(slug, 'genoma'), 'manifest.json');
76
+ return path.join(installedRoot(projectDir), 'genomes', sanitizeSegment(slug, 'genome'), 'manifest.json');
77
77
  }
78
78
 
79
79
  function localSquadPackageDir(projectDir, slug) {
@@ -113,7 +113,7 @@ function localSquadOutputDir(projectDir, slug) {
113
113
  }
114
114
 
115
115
  function localSquadLogsDir(projectDir, slug) {
116
- return path.join(projectDir, 'aios-logs', sanitizeSegment(slug, 'squad'));
116
+ return path.join(projectDir, 'aioson-logs', sanitizeSegment(slug, 'squad'));
117
117
  }
118
118
 
119
119
  function localSquadMediaDir(projectDir, slug) {
@@ -161,7 +161,7 @@ function localLegacySquadReadinessPath(projectDir, slug) {
161
161
  }
162
162
 
163
163
  function localGenomeFilePath(projectDir, slug) {
164
- return path.join(projectDir, '.aioson', 'genomas', `${sanitizeSegment(slug, 'genoma')}.md`);
164
+ return path.join(projectDir, '.aioson', 'genomes', `${sanitizeSegment(slug, 'genome')}.md`);
165
165
  }
166
166
 
167
167
  function findPrimaryHeading(markdown, fallback) {
@@ -428,7 +428,7 @@ function buildLocalSquadManifest(snapshot, agents) {
428
428
  ),
429
429
  rules: {
430
430
  outputsDir: `output/${slug}`,
431
- logsDir: `aios-logs/${slug}`,
431
+ logsDir: `aioson-logs/${slug}`,
432
432
  mediaDir: `media/${slug}`,
433
433
  reviewPolicy: Array.isArray(source?.rules?.reviewPolicy)
434
434
  ? source.rules.reviewPolicy
@@ -532,7 +532,7 @@ function buildSquadTextManifest(snapshot, manifest) {
532
532
  '## Outputs and review',
533
533
  `- Drafts: \`output/${manifest.slug}/\``,
534
534
  `- Final HTML: \`output/${manifest.slug}/{session-id}.html\``,
535
- `- Logs: \`aios-logs/${manifest.slug}/\``,
535
+ `- Logs: \`aioson-logs/${manifest.slug}/\``,
536
536
  `- Media: \`media/${manifest.slug}/\``,
537
537
  `- Package root: \`.aioson/squads/${manifest.slug}/\``,
538
538
  `- Design doc: \`.aioson/squads/${manifest.slug}/docs/design-doc.md\``,
@@ -586,7 +586,7 @@ function buildSquadMetadata(snapshot, options = {}) {
586
586
  `Templates: ${packageRoot}/templates/`,
587
587
  `Docs: ${packageRoot}/docs/`,
588
588
  `Output: output/${slug}/`,
589
- `Logs: aios-logs/${slug}/`,
589
+ `Logs: aioson-logs/${slug}/`,
590
590
  `Media: media/${slug}/`,
591
591
  `DesignDoc: ${packageRoot}/docs/design-doc.md`,
592
592
  `Readiness: ${packageRoot}/docs/readiness.md`,
@@ -602,7 +602,7 @@ function buildSquadMetadata(snapshot, options = {}) {
602
602
  ? snapshot.appliedGenomes.filter((item) => String(item.scopeType || 'SQUAD').toUpperCase() === 'SQUAD')
603
603
  : [];
604
604
  for (const genome of shared) {
605
- lines.push(`- .aioson/genomas/${sanitizeSegment(genome.genome.slug, 'genoma')}.md`);
605
+ lines.push(`- .aioson/genomes/${sanitizeSegment(genome.genome.slug, 'genome')}.md`);
606
606
  }
607
607
 
608
608
  lines.push('', 'AgentGenomes:');
@@ -614,7 +614,7 @@ function buildSquadMetadata(snapshot, options = {}) {
614
614
 
615
615
  for (const genome of scoped) {
616
616
  lines.push(
617
- `- ${normalizeAgentSlug(genome.agentSlug)}: .aioson/genomas/${sanitizeSegment(genome.genome.slug, 'genoma')}.md`
617
+ `- ${normalizeAgentSlug(genome.agentSlug)}: .aioson/genomes/${sanitizeSegment(genome.genome.slug, 'genome')}.md`
618
618
  );
619
619
  }
620
620
 
@@ -898,7 +898,7 @@ async function materializeImportedSquad(projectDir, payload, sourceUrl, force) {
898
898
  packageDir: `.aioson/squads/${slug}`,
899
899
  agentsDir: `.aioson/squads/${slug}/agents`,
900
900
  outputDir: `output/${slug}`,
901
- logsDir: `aios-logs/${slug}`,
901
+ logsDir: `aioson-logs/${slug}`,
902
902
  mediaDir: `media/${slug}`,
903
903
  latestSessionPath: `output/${slug}/latest.html`
904
904
  });
@@ -1109,7 +1109,7 @@ function buildInstalledGenomeManifest(snapshot, sourceUrl) {
1109
1109
  }
1110
1110
 
1111
1111
  async function materializeImportedGenome(projectDir, payload, sourceUrl, force) {
1112
- const slug = sanitizeSegment(payload.genome.slug, 'genoma');
1112
+ const slug = sanitizeSegment(payload.genome.slug, 'genome');
1113
1113
  const genomePath = localGenomeFilePath(projectDir, slug);
1114
1114
  const manifestPath = installedGenomeManifestPath(projectDir, slug);
1115
1115
 
@@ -1298,7 +1298,7 @@ async function loadLocalGenomeSnapshot(projectDir, slug, options = {}) {
1298
1298
  genome: {
1299
1299
  id: null,
1300
1300
  name: genomeName,
1301
- slug: sanitizeSegment(slug, 'genoma'),
1301
+ slug: sanitizeSegment(slug, 'genome'),
1302
1302
  description: options.description ? String(options.description).trim() : firstParagraph(markdown),
1303
1303
  visibility: String(options.visibility || 'PRIVATE').toUpperCase(),
1304
1304
  status: 'PUBLISHED',
@@ -1353,7 +1353,7 @@ async function buildAppliedGenomesFromMetadata(projectDir, metadataContent, opti
1353
1353
  const genomeAbsPath = path.join(projectDir, normalizeRel(genomeRelPath));
1354
1354
  const markdown = await fs.readFile(genomeAbsPath, 'utf8').catch(() => null);
1355
1355
  if (!markdown) continue;
1356
- const slug = sanitizeSegment(path.basename(genomeAbsPath, '.md'), 'genoma');
1356
+ const slug = sanitizeSegment(path.basename(genomeAbsPath, '.md'), 'genome');
1357
1357
  items.push({
1358
1358
  scopeType: 'SQUAD',
1359
1359
  agentSlug: null,
@@ -1384,7 +1384,7 @@ async function buildAppliedGenomesFromMetadata(projectDir, metadataContent, opti
1384
1384
  const genomeAbsPath = path.join(projectDir, normalizeRel(entry.genomePath));
1385
1385
  const markdown = await fs.readFile(genomeAbsPath, 'utf8').catch(() => null);
1386
1386
  if (!markdown) continue;
1387
- const slug = sanitizeSegment(path.basename(genomeAbsPath, '.md'), 'genoma');
1387
+ const slug = sanitizeSegment(path.basename(genomeAbsPath, '.md'), 'genome');
1388
1388
  items.push({
1389
1389
  scopeType: 'AGENT',
1390
1390
  agentSlug: entry.agentSlug,
@@ -1420,7 +1420,7 @@ async function buildAppliedGenomesFromBindings(projectDir, genomeBindings, optio
1420
1420
  const items = [];
1421
1421
 
1422
1422
  for (const binding of flattened) {
1423
- const genomeAbsPath = path.join(projectDir, '.aioson', 'genomas', `${binding.slug}.md`);
1423
+ const genomeAbsPath = path.join(projectDir, '.aioson', 'genomes', `${binding.slug}.md`);
1424
1424
  const markdown = await fs.readFile(genomeAbsPath, 'utf8').catch(() => null);
1425
1425
  if (!markdown) continue;
1426
1426
 
@@ -1477,7 +1477,7 @@ async function loadLocalSquadSnapshot(projectDir, slug, options = {}) {
1477
1477
  const packageRootRel = `.aioson/squads/${slug}`;
1478
1478
  const agentsDirRel = normalizeRel(extractField(content, 'Agents') || `${packageRootRel}/agents`);
1479
1479
  const outputDirRel = normalizeRel(extractField(content, 'Output') || `output/${slug}`);
1480
- const logsDirRel = normalizeRel(extractField(content, 'Logs') || `aios-logs/${slug}`);
1480
+ const logsDirRel = normalizeRel(extractField(content, 'Logs') || `aioson-logs/${slug}`);
1481
1481
  const mediaDirRel = normalizeRel(extractField(content, 'Media') || `media/${slug}`);
1482
1482
  const agentsDirAbs = path.join(projectDir, agentsDirRel);
1483
1483
  const localManifest = (await loadLocalSquadManifest(projectDir, slug)) || {};