ultra-builder-pro-cli 0.25.0 → 0.26.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 (241) hide show
  1. package/.ultra-template/contexts/TEMPLATE.md +87 -0
  2. package/.ultra-template/decisions/.gitkeep +1 -0
  3. package/.ultra-template/evidence/.gitkeep +1 -0
  4. package/.ultra-template/north-star.md +46 -0
  5. package/.ultra-template/research/.gitkeep +1 -0
  6. package/.ultra-template/specs/discovery.md +2 -2
  7. package/.ultra-template/tasks.json +3 -0
  8. package/.ultra-template/test-report.json +27 -0
  9. package/CHANGELOG.md +49 -0
  10. package/README.md +217 -579
  11. package/adapters/_shared/delegate-profiles/kimi-read-only.md +15 -0
  12. package/adapters/_shared/delegate-profiles/kimi-write.md +17 -0
  13. package/adapters/_shared/file-ops.cjs +76 -2
  14. package/adapters/_shared/host-profile.cjs +97 -0
  15. package/adapters/_shared/plugin-core.cjs +204 -0
  16. package/adapters/_shared/runtime-assets.cjs +31 -96
  17. package/adapters/claude.js +92 -291
  18. package/adapters/codex.js +309 -842
  19. package/adapters/grok.js +143 -1469
  20. package/adapters/kimi.js +124 -1053
  21. package/adapters/opencode.js +230 -742
  22. package/bin/delegate-worker.cjs +323 -0
  23. package/bin/delegate.cjs +456 -0
  24. package/bin/install.js +38 -11
  25. package/docs/ARCHITECTURE.md +122 -216
  26. package/docs/ARTIFACT-AUTHORITY.md +90 -268
  27. package/docs/DECISIONS.md +75 -87
  28. package/docs/PHILOSOPHY.md +177 -0
  29. package/docs/PLUGIN-ISOLATION-CONTRACT.md +62 -39
  30. package/docs/RUNTIME-COMPAT-MATRIX.md +99 -156
  31. package/docs/SKILL-AUTHORING.md +94 -73
  32. package/docs/WORKFLOW-LIFECYCLE.md +93 -189
  33. package/hooks/README.md +17 -37
  34. package/hooks/_common.py +113 -0
  35. package/hooks/adapters/codex.py +9 -16
  36. package/hooks/adapters/grok.py +5 -5
  37. package/hooks/adapters/kimi.py +8 -14
  38. package/hooks/block_dangerous_commands.py +95 -0
  39. package/hooks/compact_context.py +65 -0
  40. package/hooks/mid_workflow_recall.py +25 -0
  41. package/hooks/post_edit_guard.py +105 -0
  42. package/hooks/session_context.py +30 -0
  43. package/package.json +17 -52
  44. package/skills/ultra-change/SKILL.md +97 -50
  45. package/skills/ultra-change/references/change-contract.md +40 -1
  46. package/skills/ultra-delegate/SKILL.md +82 -0
  47. package/skills/ultra-delegate/references/delegation-contract.md +51 -0
  48. package/skills/ultra-delegate/scripts/delegate_wait.py +86 -0
  49. package/skills/ultra-deliver/SKILL.md +61 -42
  50. package/skills/ultra-deliver/references/baseline-reconciliation.md +39 -20
  51. package/skills/ultra-dev/SKILL.md +96 -62
  52. package/skills/ultra-dev/references/debugging.md +22 -0
  53. package/skills/ultra-domain-modeling/SKILL.md +79 -0
  54. package/skills/ultra-grilling/SKILL.md +79 -0
  55. package/skills/ultra-init/SKILL.md +94 -37
  56. package/skills/ultra-init/references/brownfield-adoption.md +33 -83
  57. package/skills/ultra-init/scripts/init_project.cjs +84 -0
  58. package/skills/ultra-plan/SKILL.md +88 -68
  59. package/skills/ultra-research/SKILL.md +62 -48
  60. package/skills/ultra-research/references/00-problem-validation.md +5 -5
  61. package/skills/ultra-research/references/01-opportunity-discovery.md +3 -3
  62. package/skills/ultra-research/references/02-market-assessment.md +5 -4
  63. package/skills/ultra-research/references/03-competitive-landscape.md +3 -3
  64. package/skills/ultra-research/references/04-product-strategy.md +3 -3
  65. package/skills/ultra-research/references/05-assumptions-validation.md +5 -5
  66. package/skills/ultra-research/references/10-user-personas.md +3 -2
  67. package/skills/ultra-research/references/11-user-scenarios.md +3 -3
  68. package/skills/ultra-research/references/20-user-stories.md +4 -3
  69. package/skills/ultra-research/references/21-features-scope.md +3 -2
  70. package/skills/ultra-research/references/22-success-metrics.md +3 -3
  71. package/skills/ultra-research/references/30-architecture-context.md +3 -3
  72. package/skills/ultra-research/references/31-solution-strategy.md +3 -3
  73. package/skills/ultra-research/references/32-building-blocks.md +3 -3
  74. package/skills/ultra-research/references/40-deployment.md +3 -3
  75. package/skills/ultra-research/references/41-quality-risks.md +3 -3
  76. package/skills/ultra-research/references/99-synthesis.md +7 -7
  77. package/skills/ultra-review/SKILL.md +59 -32
  78. package/skills/ultra-review/references/code.md +17 -0
  79. package/skills/ultra-review/references/comments.md +12 -0
  80. package/skills/ultra-review/references/design.md +12 -0
  81. package/skills/ultra-review/references/errors.md +13 -0
  82. package/skills/ultra-review/references/spec.md +13 -0
  83. package/skills/ultra-review/references/tests.md +12 -0
  84. package/skills/ultra-review/references/worker-packet.md +40 -0
  85. package/skills/ultra-review/scripts/review_wait.py +26 -15
  86. package/skills/ultra-status/SKILL.md +61 -36
  87. package/skills/ultra-tdd/SKILL.md +81 -0
  88. package/skills/ultra-tdd/references/templates/README.md +20 -0
  89. package/skills/ultra-tdd/references/templates/feature-flag-default-audit.sh +79 -0
  90. package/skills/ultra-tdd/references/templates/persistence-real.ts +78 -0
  91. package/skills/ultra-tdd/references/templates/testcontainer-postgres.py +78 -0
  92. package/skills/ultra-tdd/references/templates/testcontainer-postgres.ts +73 -0
  93. package/skills/ultra-tdd/references/templates/vertical-slice.ts +66 -0
  94. package/skills/ultra-tdd/references/test-execution.md +18 -0
  95. package/skills/ultra-test/SKILL.md +70 -30
  96. package/skills/ultra-test/references/export-syntax.md +18 -0
  97. package/skills/ultra-test/scripts/worktree_digest.cjs +54 -0
  98. package/skills/ultra-think/SKILL.md +49 -27
  99. package/skills/ultra-think/references/autonomy-boundary.md +48 -0
  100. package/skills/ultra-think/references/interaction-boundary.md +18 -33
  101. package/.ultra-template/docs/research/README.md +0 -55
  102. package/.ultra-template/reports/templates/delivery-report.json +0 -17
  103. package/.ultra-template/reports/templates/test-report.json +0 -18
  104. package/.ultra-template/tasks/tasks.json +0 -15
  105. package/.ultra-template/templates/task-context.md +0 -19
  106. package/adapters/_shared/codex-assets.cjs +0 -725
  107. package/adapters/_shared/codex-ultra-tools-entry.cjs +0 -11
  108. package/adapters/_shared/interaction-contract.cjs +0 -129
  109. package/adapters/_shared/path-rewrite.cjs +0 -79
  110. package/adapters/_shared/runtime-native.cjs +0 -132
  111. package/adapters/_shared/settings-merge.cjs +0 -76
  112. package/agents/code-reviewer.md +0 -42
  113. package/agents/debugger.md +0 -40
  114. package/agents/review-code.md +0 -49
  115. package/agents/review-comments.md +0 -38
  116. package/agents/review-coordinator.md +0 -56
  117. package/agents/review-design.md +0 -40
  118. package/agents/review-errors.md +0 -43
  119. package/agents/review-spec.md +0 -39
  120. package/agents/review-tests.md +0 -40
  121. package/agents/tdd-runner.md +0 -38
  122. package/bin/orchestrator.js +0 -383
  123. package/commands/ultra-change.md +0 -10
  124. package/commands/ultra-deliver.md +0 -10
  125. package/commands/ultra-dev.md +0 -10
  126. package/commands/ultra-doctor.md +0 -10
  127. package/commands/ultra-init.md +0 -10
  128. package/commands/ultra-plan.md +0 -10
  129. package/commands/ultra-research.md +0 -10
  130. package/commands/ultra-review.md +0 -10
  131. package/commands/ultra-status.md +0 -10
  132. package/commands/ultra-test.md +0 -10
  133. package/commands/ultra-think.md +0 -10
  134. package/docs/AGENT-CONTEXT.md +0 -191
  135. package/docs/COMMIT-HASH-BACKFILL.md +0 -51
  136. package/docs/LEGACY-CLI-CROSSWALK.md +0 -175
  137. package/docs/ROADMAP.md +0 -101
  138. package/docs/STATE-DB-ACCESS-POLICY.md +0 -161
  139. package/hooks/active_task_context.py +0 -152
  140. package/hooks/context_envelope.py +0 -100
  141. package/hooks/health_check.py +0 -196
  142. package/hooks/pre_stop_check.py +0 -66
  143. package/hooks/runtime_paths.py +0 -396
  144. package/hooks/subagent_tracker.py +0 -88
  145. package/hooks/workflow_checkpoint.py +0 -76
  146. package/hooks/workflow_context.py +0 -48
  147. package/hooks/workflow_resume.py +0 -84
  148. package/mcp-server/hook-context.cjs +0 -38
  149. package/mcp-server/hook-event.cjs +0 -34
  150. package/mcp-server/lib/archive-journal.cjs +0 -1119
  151. package/mcp-server/lib/archive-mutation-worker.py +0 -365
  152. package/mcp-server/lib/artifact-registry.cjs +0 -1232
  153. package/mcp-server/lib/baseline-workflow.cjs +0 -1193
  154. package/mcp-server/lib/canonical-json.cjs +0 -19
  155. package/mcp-server/lib/change-authority.cjs +0 -26
  156. package/mcp-server/lib/change-packet.cjs +0 -790
  157. package/mcp-server/lib/change-workflow.cjs +0 -65
  158. package/mcp-server/lib/context-envelope.cjs +0 -581
  159. package/mcp-server/lib/context-paths.cjs +0 -142
  160. package/mcp-server/lib/decision-records.cjs +0 -423
  161. package/mcp-server/lib/delivery-transaction.cjs +0 -471
  162. package/mcp-server/lib/doctor-backup-worker.cjs +0 -328
  163. package/mcp-server/lib/doctor.cjs +0 -627
  164. package/mcp-server/lib/git-bootstrap.cjs +0 -651
  165. package/mcp-server/lib/init-project.cjs +0 -1021
  166. package/mcp-server/lib/kernel-change-workflow.cjs +0 -2029
  167. package/mcp-server/lib/legacy-change-workflow.cjs +0 -3032
  168. package/mcp-server/lib/managed-file-write.cjs +0 -172
  169. package/mcp-server/lib/plan-checkpoint.cjs +0 -177
  170. package/mcp-server/lib/plan-publication-worker.cjs +0 -830
  171. package/mcp-server/lib/plan-store.cjs +0 -794
  172. package/mcp-server/lib/prd-parser.cjs +0 -83
  173. package/mcp-server/lib/pricing.cjs +0 -44
  174. package/mcp-server/lib/project-context.cjs +0 -178
  175. package/mcp-server/lib/projector.cjs +0 -386
  176. package/mcp-server/lib/provider-refs.cjs +0 -42
  177. package/mcp-server/lib/runtime-paths.cjs +0 -1687
  178. package/mcp-server/lib/runtime-state.cjs +0 -222
  179. package/mcp-server/lib/safe-project-file.cjs +0 -385
  180. package/mcp-server/lib/stage-checkpoints.cjs +0 -463
  181. package/mcp-server/lib/state-authority.cjs +0 -95
  182. package/mcp-server/lib/state-db.cjs +0 -1580
  183. package/mcp-server/lib/state-ops.cjs +0 -1428
  184. package/mcp-server/lib/task-contract.cjs +0 -404
  185. package/mcp-server/lib/task-expander.cjs +0 -105
  186. package/mcp-server/lib/task-ledger.cjs +0 -1964
  187. package/mcp-server/lib/telemetry.cjs +0 -85
  188. package/mcp-server/lib/topo.cjs +0 -131
  189. package/mcp-server/lib/ultra-facade.cjs +0 -2684
  190. package/mcp-server/lib/worker-packet.cjs +0 -358
  191. package/mcp-server/server.cjs +0 -437
  192. package/orchestrator/auto-merge.cjs +0 -128
  193. package/orchestrator/daemon.cjs +0 -264
  194. package/orchestrator/dispatch-rules.cjs +0 -78
  195. package/orchestrator/parallel-orchestrator.cjs +0 -903
  196. package/orchestrator/planner/plan-builder.cjs +0 -125
  197. package/orchestrator/recovery.cjs +0 -188
  198. package/orchestrator/session-close-journal-worker.cjs +0 -5
  199. package/orchestrator/session-close-journal.cjs +0 -1014
  200. package/orchestrator/session-runner.cjs +0 -1153
  201. package/orchestrator/worktree-manager.cjs +0 -282
  202. package/settings.json +0 -7
  203. package/skills/cc-collab/SKILL.md +0 -42
  204. package/skills/code-review-expert/SKILL.md +0 -43
  205. package/skills/code-review-expert/references/correctness-reliability.md +0 -30
  206. package/skills/code-review-expert/references/design-boundaries.md +0 -26
  207. package/skills/code-review-expert/references/removal-plan.md +0 -24
  208. package/skills/codex-collab/SKILL.md +0 -49
  209. package/skills/codex-collab/evals/evals.json +0 -35
  210. package/skills/integration-rules/SKILL.md +0 -32
  211. package/skills/security-rules/SKILL.md +0 -38
  212. package/skills/testing-rules/SKILL.md +0 -32
  213. package/skills/ultra-doctor/SKILL.md +0 -41
  214. package/skills/ultra-plan/references/semantic-preflight.md +0 -20
  215. package/skills/ultra-verify/SKILL.md +0 -46
  216. package/skills/ultra-verify/evals/evals.json +0 -46
  217. package/skills/ultra-verify/references/cross-verify-modes.md +0 -28
  218. package/skills/ultra-verify/references/evidence-status.md +0 -21
  219. package/skills/ultra-verify/scripts/verify_wait.py +0 -94
  220. package/spec/cli-protocol.md +0 -102
  221. package/spec/mcp-tools.yaml +0 -312
  222. package/spec/schemas/baseline-reconciliation.v1.schema.json +0 -73
  223. package/spec/schemas/change-delta.v1.schema.json +0 -153
  224. package/spec/schemas/command-manifest.schema.json +0 -29
  225. package/spec/schemas/context-file.v4.5.schema.json +0 -54
  226. package/spec/schemas/delivery-report.v1.schema.json +0 -66
  227. package/spec/schemas/documentation-reconciliation.v1.schema.json +0 -106
  228. package/spec/schemas/mcp-tools.schema.json +0 -136
  229. package/spec/schemas/skill-manifest.schema.json +0 -24
  230. package/spec/schemas/state-db.sql +0 -641
  231. package/spec/schemas/task-ledger.v2.schema.json +0 -296
  232. package/spec/schemas/tasks.v4.5.schema.json +0 -96
  233. package/spec/schemas/test-report.v1.schema.json +0 -225
  234. package/ultra-tools/cli.cjs +0 -129
  235. package/ultra-tools/commands/db.cjs +0 -226
  236. package/ultra-tools/commands/legacy-memory.cjs +0 -159
  237. package/ultra-tools/commands/migrate.cjs +0 -1386
  238. package/ultra-tools/commands/session.cjs +0 -213
  239. package/ultra-tools/commands/status.cjs +0 -559
  240. package/ultra-tools/commands/system.cjs +0 -498
  241. package/ultra-tools/commands/task.cjs +0 -105
@@ -1,129 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * ultra-tools — runtime-agnostic state engine for Ultra Builder Pro.
5
- *
6
- * Maintenance CLI for Ultra initialization, status, database operations,
7
- * migration, and explicit legacy-memory cleanup. Authoritative task mutations
8
- * stay on the live MCP server; this CLI is not a task-state fallback.
9
- *
10
- * Usage:
11
- * ultra-tools <subcommand> [...]
12
- * ultra-tools --help
13
- * ultra-tools --version
14
- */
15
-
16
- 'use strict';
17
-
18
- const fs = require('node:fs');
19
- const path = require('node:path');
20
- const runtimePaths = require('../mcp-server/lib/runtime-paths.cjs');
21
-
22
- const VERSION = (() => {
23
- try {
24
- const pkg = require('../package.json');
25
- return pkg.version;
26
- } catch (_err) {
27
- return '0.1.0';
28
- }
29
- })();
30
-
31
- const USAGE = `ultra-tools v${VERSION}
32
-
33
- USAGE:
34
- ultra-tools <subcommand> [args]
35
-
36
- SUBCOMMANDS:
37
- task init-project
38
- session close | get | list | admission | heartbeat | subscribe | reap
39
- system doctor [--repair] | restore | rebaseline
40
- status [--cost] [--since <duration>] [--json]
41
- db init | checkpoint | vacuum | integrity | backup (Phase 2)
42
- migrate --from=<4.4|4.5> --to=<4.5|20.0> [--dry|--rollback]
43
- legacy-memory inspect | archive | prune --confirm DELETE_ULTRA_LEGACY_MEMORY
44
-
45
- --help / -h show this message
46
- --version / -v show version
47
-
48
- All state lives under .ultra/ in the project root.
49
- `;
50
-
51
- function readJsonIfExists(p) {
52
- if (!fs.existsSync(p)) return null;
53
- try { return JSON.parse(fs.readFileSync(p, 'utf8')); }
54
- catch (err) {
55
- fail(`malformed JSON at ${p}: ${err.message}`);
56
- }
57
- }
58
-
59
- function fail(msg, code) {
60
- process.stderr.write(`ultra-tools: ${msg}\n`);
61
- process.exit(code || 1);
62
- }
63
-
64
- const dbCommand = require('./commands/db.cjs');
65
- const migrateCommand = require('./commands/migrate.cjs');
66
- const taskCommand = require('./commands/task.cjs');
67
- const sessionCommand = require('./commands/session.cjs');
68
- const statusCommand = require('./commands/status.cjs');
69
- const legacyMemoryCommand = require('./commands/legacy-memory.cjs');
70
- const systemCommand = require('./commands/system.cjs');
71
-
72
- // Session commands run only against healthy authority and may emit best-effort
73
- // telemetry. Initialization, diagnosis, migration, and recovery commands must
74
- // never open state.db before their own evidence-preserving checks.
75
- const TELEMETRY_SUBCOMMANDS = new Set(['session']);
76
-
77
- function emitCliTelemetry(sub, rest) {
78
- try {
79
- const dbPath = runtimePaths.locateStateDb(process.cwd());
80
- if (!fs.existsSync(dbPath)) return;
81
- const Database = require('better-sqlite3');
82
- const db = new Database(dbPath);
83
- try {
84
- const telemetry = require('../mcp-server/lib/telemetry.cjs');
85
- telemetry.appendTelemetry(db, {
86
- event_type: 'tool_call',
87
- tool_name: `cli.${sub}`,
88
- session_id: process.env.UBP_SESSION_ID || null,
89
- rootDir: process.cwd(),
90
- payload: { args: Array.isArray(rest) ? rest.slice(0, 4) : [] },
91
- });
92
- } finally { db.close(); }
93
- } catch (_) { /* telemetry is opt-in side channel; never crash the CLI */ }
94
- }
95
-
96
- const SUBCOMMANDS = {
97
- task: (args) => process.exit(taskCommand.dispatch(args)),
98
- session: (args) => process.exit(sessionCommand.dispatch(args)),
99
- status: (args) => process.exit(statusCommand.dispatch(args)),
100
- db: (args) => process.exit(dbCommand.dispatch(args)),
101
- migrate: (args) => process.exit(migrateCommand.dispatch(args)),
102
- 'legacy-memory': (args) => process.exit(legacyMemoryCommand.dispatch(args)),
103
- system: (args) => systemCommand.dispatch(args).then((code) => process.exit(code)),
104
- };
105
-
106
- function main(argv) {
107
- const args = argv.slice(2);
108
- if (args.length === 0 || args[0] === '-h' || args[0] === '--help') {
109
- process.stdout.write(USAGE);
110
- return;
111
- }
112
- if (args[0] === '-v' || args[0] === '--version') {
113
- process.stdout.write(`${VERSION}\n`);
114
- return;
115
- }
116
-
117
- const [sub, ...rest] = args;
118
- const handler = SUBCOMMANDS[sub];
119
- if (!handler) fail(`unknown subcommand: ${sub}\n\n${USAGE}`);
120
- if (TELEMETRY_SUBCOMMANDS.has(sub)) emitCliTelemetry(sub, rest);
121
- handler(rest);
122
- }
123
-
124
- // exports kept for unit tests (Phase 1+)
125
- module.exports = { USAGE, SUBCOMMANDS, readJsonIfExists, VERSION, main };
126
-
127
- if (require.main === module) {
128
- main(process.argv);
129
- }
@@ -1,226 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
- const { randomUUID } = require('node:crypto');
6
- const Database = require('better-sqlite3');
7
-
8
- const { initStateDb, openStateDb, closeStateDb } = require('../../mcp-server/lib/state-db.cjs');
9
- const runtimePaths = require('../../mcp-server/lib/runtime-paths.cjs');
10
-
11
- const DEFAULT_DB_PATH = runtimePaths.STATE_DB_RELATIVE_PATH;
12
- const DEFAULT_BACKUP_DIR = path.join(runtimePaths.RUNTIME_RELATIVE_DIR, 'backups');
13
-
14
- function parseFlags(args) {
15
- const flags = { _: [] };
16
- for (let i = 0; i < args.length; i++) {
17
- const a = args[i];
18
- if (a === '--path') {
19
- flags.path = args[++i];
20
- } else if (a === '--to') {
21
- flags.to = args[++i];
22
- } else if (a === '--help' || a === '-h') {
23
- flags.help = true;
24
- } else {
25
- flags._.push(a);
26
- }
27
- }
28
- return flags;
29
- }
30
-
31
- function emit(envelope) {
32
- process.stdout.write(`${JSON.stringify(envelope)}\n`);
33
- }
34
-
35
- function dispatch(args) {
36
- const [verb, ...rest] = args;
37
- const flags = parseFlags(rest);
38
-
39
- if (!verb || flags.help) {
40
- process.stdout.write(USAGE);
41
- return 0;
42
- }
43
-
44
- switch (verb) {
45
- case 'init': return cmdInit(flags);
46
- case 'checkpoint': return cmdCheckpoint(flags);
47
- case 'vacuum': return cmdVacuum(flags);
48
- case 'integrity': return cmdIntegrity(flags);
49
- case 'backup': return cmdBackup(flags);
50
- default:
51
- emit({ ok: false, error: { code: 'UNKNOWN_VERB', message: `unknown db verb '${verb}'; supported: init, checkpoint, vacuum, integrity, backup` } });
52
- return 1;
53
- }
54
- }
55
-
56
- function resolveAuthorityPath(flags, { migrateLegacy = true } = {}) {
57
- const env = flags.path
58
- ? { UBP_DB_PATH: path.resolve(flags.path) }
59
- : process.env;
60
- return runtimePaths.resolveConfiguredStateDb(process.cwd(), {
61
- env,
62
- migrateLegacy,
63
- }).stateDbPath;
64
- }
65
-
66
- function cmdInit(flags) {
67
- try {
68
- const dbPath = resolveAuthorityPath(flags, { migrateLegacy: true });
69
- const { db, schema_version, created, tables, path: actualPath } = initStateDb(dbPath);
70
- closeStateDb(db);
71
- emit({
72
- ok: true,
73
- data: {
74
- path: actualPath,
75
- schema_version,
76
- created,
77
- tables,
78
- size_bytes: fs.statSync(actualPath).size,
79
- },
80
- });
81
- return 0;
82
- } catch (err) {
83
- emit({
84
- ok: false,
85
- error: {
86
- code: 'INIT_FAILED',
87
- message: err.message,
88
- retriable: false,
89
- },
90
- });
91
- return 2;
92
- }
93
- }
94
-
95
- function withOpenDb(flags, fn, errorCode) {
96
- let db;
97
- try {
98
- const dbPath = resolveAuthorityPath(flags, { migrateLegacy: true });
99
- if (!fs.existsSync(dbPath)) {
100
- emit({ ok: false, error: { code: 'DB_NOT_FOUND', message: `state.db missing at ${dbPath}; run 'db init' first` } });
101
- return 2;
102
- }
103
- db = openStateDb(dbPath);
104
- const data = fn(db, dbPath);
105
- emit({ ok: true, data: { path: dbPath, ...data } });
106
- return 0;
107
- } catch (err) {
108
- emit({ ok: false, error: { code: errorCode, message: err.message, retriable: false } });
109
- return 2;
110
- } finally {
111
- if (db) closeStateDb(db);
112
- }
113
- }
114
-
115
- function cmdCheckpoint(flags) {
116
- return withOpenDb(flags, (db) => {
117
- const result = db.pragma('wal_checkpoint(TRUNCATE)');
118
- // better-sqlite3 returns [{ busy, log, checkpointed }]
119
- const row = Array.isArray(result) && result[0] ? result[0] : { busy: 0, log: 0, checkpointed: 0 };
120
- return { busy: row.busy, log: row.log, checkpointed: row.checkpointed };
121
- }, 'CHECKPOINT_FAILED');
122
- }
123
-
124
- function cmdVacuum(flags) {
125
- return withOpenDb(flags, (db) => {
126
- const before = db.prepare('SELECT page_count * page_size AS bytes FROM pragma_page_count(), pragma_page_size()').get();
127
- db.prepare('VACUUM').run();
128
- const after = db.prepare('SELECT page_count * page_size AS bytes FROM pragma_page_count(), pragma_page_size()').get();
129
- return { bytes_before: before.bytes, bytes_after: after.bytes, reclaimed_bytes: before.bytes - after.bytes };
130
- }, 'VACUUM_FAILED');
131
- }
132
-
133
- function cmdIntegrity(flags) {
134
- return withOpenDb(flags, (db) => {
135
- const result = db.pragma('integrity_check');
136
- const messages = result.map((r) => r.integrity_check);
137
- const ok = messages.length === 1 && messages[0] === 'ok';
138
- return { integrity_ok: ok, messages };
139
- }, 'INTEGRITY_FAILED');
140
- }
141
-
142
- function defaultBackupPath() {
143
- const ts = new Date().toISOString().replace(/[:.]/g, '-');
144
- return path.join(DEFAULT_BACKUP_DIR, `state-db-${ts}.db`);
145
- }
146
-
147
- function quoteSqlString(value) {
148
- return `'${String(value).replaceAll("'", "''")}'`;
149
- }
150
-
151
- function createOnlineBackup(db, targetPath, {
152
- beforeSnapshot = null,
153
- } = {}) {
154
- const stage = path.join(
155
- path.dirname(targetPath),
156
- `.${path.basename(targetPath)}.snapshot-${process.pid}-${randomUUID()}`,
157
- );
158
- try {
159
- if (beforeSnapshot) beforeSnapshot();
160
- // VACUUM INTO is SQLite's synchronous, transactionally consistent
161
- // snapshot primitive. It reads committed pages through SQLite rather than
162
- // copying a main file that may lag committed WAL frames.
163
- db.exec(`VACUUM INTO ${quoteSqlString(stage)}`);
164
- const snapshot = new Database(stage, { readonly: true, fileMustExist: true });
165
- try {
166
- const integrity = snapshot.pragma('integrity_check');
167
- if (integrity.length !== 1 || integrity[0].integrity_check !== 'ok') {
168
- throw new Error('SQLite backup snapshot failed integrity_check');
169
- }
170
- } finally {
171
- snapshot.close();
172
- }
173
- fs.renameSync(stage, targetPath);
174
- return fs.statSync(targetPath).size;
175
- } finally {
176
- fs.rmSync(stage, { force: true });
177
- }
178
- }
179
-
180
- function cmdBackup(flags) {
181
- let db;
182
- try {
183
- const dbPath = resolveAuthorityPath(flags, { migrateLegacy: true });
184
- const targetPath = runtimePaths.assertManagedBackupDestination(
185
- process.cwd(),
186
- flags.to || defaultBackupPath(),
187
- ).targetPath;
188
- if (!fs.existsSync(dbPath)) {
189
- emit({ ok: false, error: { code: 'DB_NOT_FOUND', message: `state.db missing at ${dbPath}` } });
190
- return 2;
191
- }
192
- fs.mkdirSync(path.dirname(targetPath), { recursive: true });
193
- db = openStateDb(dbPath);
194
- const size = createOnlineBackup(db, targetPath);
195
- emit({ ok: true, data: { source: dbPath, target: targetPath, size_bytes: size } });
196
- return 0;
197
- } catch (err) {
198
- emit({ ok: false, error: { code: 'BACKUP_FAILED', message: err.message, retriable: true } });
199
- return 2;
200
- } finally {
201
- if (db) closeStateDb(db);
202
- }
203
- }
204
-
205
- const USAGE = `ultra-tools db <verb> [flags]
206
-
207
- Verbs (Phase 2):
208
- init [--path <db>] create or open state.db, apply schema
209
- checkpoint [--path <db>] run PRAGMA wal_checkpoint(TRUNCATE)
210
- vacuum [--path <db>] run VACUUM and report reclaimed bytes
211
- integrity [--path <db>] run PRAGMA integrity_check
212
- backup [--path <db>] [--to <out>] online .backup() to a file
213
-
214
- Default db: .ultra/.runtime/state.db
215
- Default backup: .ultra/.runtime/backups/state-db-{iso}.db
216
- `;
217
-
218
- module.exports = {
219
- dispatch,
220
- USAGE,
221
- DEFAULT_DB_PATH,
222
- DEFAULT_BACKUP_DIR,
223
- _internal: {
224
- createOnlineBackup,
225
- },
226
- };
@@ -1,159 +0,0 @@
1
- 'use strict';
2
-
3
- /** Explicit migration boundary for retired Ultra memory stores. */
4
-
5
- const fs = require('node:fs');
6
- const path = require('node:path');
7
- const Database = require('better-sqlite3');
8
-
9
- const CONFIRMATION = 'DELETE_ULTRA_LEGACY_MEMORY';
10
-
11
- function tableExists(db, name) {
12
- return !!db.prepare("SELECT 1 FROM sqlite_master WHERE type IN ('table', 'view') AND name = ?").get(name);
13
- }
14
-
15
- function inspectLegacy(sourceDir = '.') {
16
- const root = path.resolve(sourceDir);
17
- const ultra = path.join(root, '.ultra');
18
- const hookMemory = path.join(ultra, 'memory');
19
- const dbPath = path.join(ultra, 'state.db');
20
- let stateMemoryTable = false;
21
- let stateMemoryEntries = 0;
22
- if (fs.existsSync(dbPath)) {
23
- const db = new Database(dbPath, { readonly: true, fileMustExist: true });
24
- try {
25
- stateMemoryTable = tableExists(db, 'memory_entries');
26
- if (stateMemoryTable) {
27
- stateMemoryEntries = db.prepare('SELECT COUNT(*) AS count FROM memory_entries').get().count;
28
- }
29
- } finally {
30
- db.close();
31
- }
32
- }
33
- return {
34
- source_dir: root,
35
- hook_memory_dir: fs.existsSync(hookMemory),
36
- hook_memory_path: hookMemory,
37
- state_db_path: dbPath,
38
- state_memory_table: stateMemoryTable,
39
- state_memory_entries: stateMemoryEntries,
40
- };
41
- }
42
-
43
- function archiveLegacy(sourceDir = '.', { timestamp } = {}) {
44
- const status = inspectLegacy(sourceDir);
45
- if (!status.hook_memory_dir && !status.state_memory_table) {
46
- return { ...status, archived: false, archive_dir: null };
47
- }
48
- const stamp = timestamp || new Date().toISOString().replace(/[:.]/g, '-');
49
- const archiveDir = path.join(status.source_dir, '.ultra', `legacy-memory-archive-${stamp}`);
50
- if (fs.existsSync(archiveDir)) throw new Error(`legacy memory archive already exists: ${archiveDir}`);
51
- fs.mkdirSync(archiveDir, { recursive: true });
52
-
53
- if (status.hook_memory_dir) {
54
- fs.cpSync(status.hook_memory_path, path.join(archiveDir, 'hook-memory'), {
55
- recursive: true,
56
- dereference: false,
57
- errorOnExist: true,
58
- });
59
- }
60
-
61
- if (status.state_memory_table) {
62
- const db = new Database(status.state_db_path, { readonly: true, fileMustExist: true });
63
- let entries;
64
- try {
65
- entries = db.prepare('SELECT * FROM memory_entries ORDER BY id ASC').all();
66
- } finally {
67
- db.close();
68
- }
69
- fs.writeFileSync(
70
- path.join(archiveDir, 'state-memory-entries.json'),
71
- JSON.stringify({ schema: 1, source: status.state_db_path, entries }, null, 2) + '\n',
72
- { encoding: 'utf8', flag: 'wx' },
73
- );
74
- }
75
-
76
- fs.writeFileSync(
77
- path.join(archiveDir, 'manifest.json'),
78
- JSON.stringify({ schema: 1, archived_at: new Date().toISOString(), ...status }, null, 2) + '\n',
79
- { encoding: 'utf8', flag: 'wx' },
80
- );
81
- return { ...status, archived: true, archive_dir: archiveDir };
82
- }
83
-
84
- function pruneLegacy(sourceDir = '.', { confirm, timestamp } = {}) {
85
- if (confirm !== CONFIRMATION) {
86
- throw new Error(`prune requires --confirm ${CONFIRMATION}`);
87
- }
88
- const archived = archiveLegacy(sourceDir, { timestamp });
89
- if (!archived.archived) return { ...archived, pruned: false };
90
-
91
- if (archived.hook_memory_dir) {
92
- fs.rmSync(archived.hook_memory_path, { recursive: true, force: false });
93
- }
94
- if (archived.state_memory_table) {
95
- const db = new Database(archived.state_db_path, { fileMustExist: true });
96
- try {
97
- db.transaction(() => {
98
- db.exec('DROP TRIGGER IF EXISTS memory_ai');
99
- db.exec('DROP TRIGGER IF EXISTS memory_ad');
100
- db.exec('DROP TRIGGER IF EXISTS memory_au');
101
- db.exec('DROP TABLE IF EXISTS memory_fts');
102
- db.exec('DROP TABLE IF EXISTS memory_entries');
103
- })();
104
- } finally {
105
- db.close();
106
- }
107
- }
108
- return { ...archived, pruned: true };
109
- }
110
-
111
- function parseFlags(args) {
112
- const flags = { _: [] };
113
- for (let i = 0; i < args.length; i += 1) {
114
- const token = args[i];
115
- if (token === '--source-dir') {
116
- flags.sourceDir = args[i + 1];
117
- i += 1;
118
- } else if (token.startsWith('--source-dir=')) {
119
- flags.sourceDir = token.slice('--source-dir='.length);
120
- } else if (token === '--confirm') {
121
- flags.confirm = args[i + 1];
122
- i += 1;
123
- } else if (token.startsWith('--confirm=')) {
124
- flags.confirm = token.slice('--confirm='.length);
125
- } else {
126
- flags._.push(token);
127
- }
128
- }
129
- return flags;
130
- }
131
-
132
- function dispatch(args) {
133
- const flags = parseFlags(args);
134
- const action = flags._[0] || 'inspect';
135
- try {
136
- let data;
137
- if (action === 'inspect') data = inspectLegacy(flags.sourceDir);
138
- else if (action === 'archive') data = archiveLegacy(flags.sourceDir);
139
- else if (action === 'prune') data = pruneLegacy(flags.sourceDir, { confirm: flags.confirm });
140
- else throw new Error(`unknown legacy-memory action: ${action}`);
141
- process.stdout.write(`${JSON.stringify({ ok: true, data })}\n`);
142
- return 0;
143
- } catch (error) {
144
- process.stdout.write(`${JSON.stringify({
145
- ok: false,
146
- error: { code: 'LEGACY_MEMORY_MIGRATION_FAILED', message: error.message, retriable: false },
147
- })}\n`);
148
- return 2;
149
- }
150
- }
151
-
152
- module.exports = {
153
- CONFIRMATION,
154
- inspectLegacy,
155
- archiveLegacy,
156
- pruneLegacy,
157
- parseFlags,
158
- dispatch,
159
- };