ultra-builder-pro-cli 0.25.1 → 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.
- package/.ultra-template/contexts/TEMPLATE.md +87 -0
- package/.ultra-template/decisions/.gitkeep +1 -0
- package/.ultra-template/evidence/.gitkeep +1 -0
- package/.ultra-template/north-star.md +46 -0
- package/.ultra-template/research/.gitkeep +1 -0
- package/.ultra-template/specs/discovery.md +2 -2
- package/.ultra-template/tasks.json +3 -0
- package/.ultra-template/test-report.json +27 -0
- package/CHANGELOG.md +40 -0
- package/README.md +217 -579
- package/adapters/_shared/delegate-profiles/kimi-read-only.md +15 -0
- package/adapters/_shared/delegate-profiles/kimi-write.md +17 -0
- package/adapters/_shared/file-ops.cjs +76 -2
- package/adapters/_shared/host-profile.cjs +97 -0
- package/adapters/_shared/plugin-core.cjs +204 -0
- package/adapters/_shared/runtime-assets.cjs +31 -96
- package/adapters/claude.js +92 -291
- package/adapters/codex.js +309 -842
- package/adapters/grok.js +143 -1469
- package/adapters/kimi.js +124 -1053
- package/adapters/opencode.js +230 -742
- package/bin/delegate-worker.cjs +323 -0
- package/bin/delegate.cjs +456 -0
- package/bin/install.js +38 -11
- package/docs/ARCHITECTURE.md +122 -216
- package/docs/ARTIFACT-AUTHORITY.md +90 -268
- package/docs/DECISIONS.md +75 -87
- package/docs/PHILOSOPHY.md +177 -0
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +62 -39
- package/docs/RUNTIME-COMPAT-MATRIX.md +99 -156
- package/docs/SKILL-AUTHORING.md +94 -73
- package/docs/WORKFLOW-LIFECYCLE.md +93 -189
- package/hooks/README.md +17 -37
- package/hooks/_common.py +113 -0
- package/hooks/adapters/codex.py +9 -16
- package/hooks/adapters/grok.py +5 -5
- package/hooks/adapters/kimi.py +8 -14
- package/hooks/block_dangerous_commands.py +95 -0
- package/hooks/compact_context.py +65 -0
- package/hooks/mid_workflow_recall.py +25 -0
- package/hooks/post_edit_guard.py +105 -0
- package/hooks/session_context.py +30 -0
- package/package.json +17 -52
- package/skills/ultra-change/SKILL.md +97 -50
- package/skills/ultra-change/references/change-contract.md +40 -1
- package/skills/ultra-delegate/SKILL.md +82 -0
- package/skills/ultra-delegate/references/delegation-contract.md +51 -0
- package/skills/ultra-delegate/scripts/delegate_wait.py +86 -0
- package/skills/ultra-deliver/SKILL.md +61 -42
- package/skills/ultra-deliver/references/baseline-reconciliation.md +39 -20
- package/skills/ultra-dev/SKILL.md +96 -62
- package/skills/ultra-dev/references/debugging.md +22 -0
- package/skills/ultra-domain-modeling/SKILL.md +79 -0
- package/skills/ultra-grilling/SKILL.md +79 -0
- package/skills/ultra-init/SKILL.md +94 -37
- package/skills/ultra-init/references/brownfield-adoption.md +33 -83
- package/skills/ultra-init/scripts/init_project.cjs +84 -0
- package/skills/ultra-plan/SKILL.md +88 -68
- package/skills/ultra-research/SKILL.md +62 -48
- package/skills/ultra-research/references/00-problem-validation.md +5 -5
- package/skills/ultra-research/references/01-opportunity-discovery.md +3 -3
- package/skills/ultra-research/references/02-market-assessment.md +5 -4
- package/skills/ultra-research/references/03-competitive-landscape.md +3 -3
- package/skills/ultra-research/references/04-product-strategy.md +3 -3
- package/skills/ultra-research/references/05-assumptions-validation.md +5 -5
- package/skills/ultra-research/references/10-user-personas.md +3 -2
- package/skills/ultra-research/references/11-user-scenarios.md +3 -3
- package/skills/ultra-research/references/20-user-stories.md +4 -3
- package/skills/ultra-research/references/21-features-scope.md +3 -2
- package/skills/ultra-research/references/22-success-metrics.md +3 -3
- package/skills/ultra-research/references/30-architecture-context.md +3 -3
- package/skills/ultra-research/references/31-solution-strategy.md +3 -3
- package/skills/ultra-research/references/32-building-blocks.md +3 -3
- package/skills/ultra-research/references/40-deployment.md +3 -3
- package/skills/ultra-research/references/41-quality-risks.md +3 -3
- package/skills/ultra-research/references/99-synthesis.md +7 -7
- package/skills/ultra-review/SKILL.md +59 -32
- package/skills/ultra-review/references/code.md +17 -0
- package/skills/ultra-review/references/comments.md +12 -0
- package/skills/ultra-review/references/design.md +12 -0
- package/skills/ultra-review/references/errors.md +13 -0
- package/skills/ultra-review/references/spec.md +13 -0
- package/skills/ultra-review/references/tests.md +12 -0
- package/skills/ultra-review/references/worker-packet.md +40 -0
- package/skills/ultra-review/scripts/review_wait.py +26 -15
- package/skills/ultra-status/SKILL.md +61 -36
- package/skills/ultra-tdd/SKILL.md +81 -0
- package/skills/ultra-tdd/references/templates/README.md +20 -0
- package/skills/ultra-tdd/references/templates/feature-flag-default-audit.sh +79 -0
- package/skills/ultra-tdd/references/templates/persistence-real.ts +78 -0
- package/skills/ultra-tdd/references/templates/testcontainer-postgres.py +78 -0
- package/skills/ultra-tdd/references/templates/testcontainer-postgres.ts +73 -0
- package/skills/ultra-tdd/references/templates/vertical-slice.ts +66 -0
- package/skills/ultra-tdd/references/test-execution.md +18 -0
- package/skills/ultra-test/SKILL.md +70 -30
- package/skills/ultra-test/references/export-syntax.md +18 -0
- package/skills/ultra-test/scripts/worktree_digest.cjs +54 -0
- package/skills/ultra-think/SKILL.md +49 -27
- package/skills/ultra-think/references/autonomy-boundary.md +48 -0
- package/skills/ultra-think/references/interaction-boundary.md +18 -33
- package/.ultra-template/docs/research/README.md +0 -55
- package/.ultra-template/reports/templates/delivery-report.json +0 -17
- package/.ultra-template/reports/templates/test-report.json +0 -18
- package/.ultra-template/tasks/tasks.json +0 -15
- package/.ultra-template/templates/task-context.md +0 -19
- package/adapters/_shared/codex-assets.cjs +0 -725
- package/adapters/_shared/codex-ultra-tools-entry.cjs +0 -11
- package/adapters/_shared/interaction-contract.cjs +0 -129
- package/adapters/_shared/path-rewrite.cjs +0 -79
- package/adapters/_shared/runtime-native.cjs +0 -132
- package/adapters/_shared/settings-merge.cjs +0 -76
- package/agents/code-reviewer.md +0 -42
- package/agents/debugger.md +0 -40
- package/agents/review-code.md +0 -49
- package/agents/review-comments.md +0 -38
- package/agents/review-coordinator.md +0 -56
- package/agents/review-design.md +0 -40
- package/agents/review-errors.md +0 -43
- package/agents/review-spec.md +0 -39
- package/agents/review-tests.md +0 -40
- package/agents/tdd-runner.md +0 -38
- package/bin/orchestrator.js +0 -383
- package/commands/ultra-change.md +0 -10
- package/commands/ultra-deliver.md +0 -10
- package/commands/ultra-dev.md +0 -10
- package/commands/ultra-doctor.md +0 -10
- package/commands/ultra-init.md +0 -10
- package/commands/ultra-plan.md +0 -10
- package/commands/ultra-research.md +0 -10
- package/commands/ultra-review.md +0 -10
- package/commands/ultra-status.md +0 -10
- package/commands/ultra-test.md +0 -10
- package/commands/ultra-think.md +0 -10
- package/docs/AGENT-CONTEXT.md +0 -191
- package/docs/COMMIT-HASH-BACKFILL.md +0 -51
- package/docs/LEGACY-CLI-CROSSWALK.md +0 -175
- package/docs/ROADMAP.md +0 -101
- package/docs/STATE-DB-ACCESS-POLICY.md +0 -161
- package/hooks/active_task_context.py +0 -152
- package/hooks/context_envelope.py +0 -100
- package/hooks/health_check.py +0 -196
- package/hooks/pre_stop_check.py +0 -66
- package/hooks/runtime_paths.py +0 -396
- package/hooks/subagent_tracker.py +0 -88
- package/hooks/workflow_checkpoint.py +0 -76
- package/hooks/workflow_context.py +0 -48
- package/hooks/workflow_resume.py +0 -84
- package/mcp-server/hook-context.cjs +0 -38
- package/mcp-server/hook-event.cjs +0 -34
- package/mcp-server/lib/archive-journal.cjs +0 -1119
- package/mcp-server/lib/archive-mutation-worker.py +0 -365
- package/mcp-server/lib/artifact-registry.cjs +0 -1232
- package/mcp-server/lib/baseline-workflow.cjs +0 -1193
- package/mcp-server/lib/canonical-json.cjs +0 -19
- package/mcp-server/lib/change-authority.cjs +0 -26
- package/mcp-server/lib/change-packet.cjs +0 -790
- package/mcp-server/lib/change-workflow.cjs +0 -65
- package/mcp-server/lib/context-envelope.cjs +0 -581
- package/mcp-server/lib/context-paths.cjs +0 -142
- package/mcp-server/lib/decision-records.cjs +0 -423
- package/mcp-server/lib/delivery-transaction.cjs +0 -471
- package/mcp-server/lib/doctor-backup-worker.cjs +0 -328
- package/mcp-server/lib/doctor.cjs +0 -627
- package/mcp-server/lib/git-bootstrap.cjs +0 -651
- package/mcp-server/lib/init-project.cjs +0 -1021
- package/mcp-server/lib/kernel-change-workflow.cjs +0 -2029
- package/mcp-server/lib/legacy-change-workflow.cjs +0 -3032
- package/mcp-server/lib/managed-file-write.cjs +0 -172
- package/mcp-server/lib/plan-checkpoint.cjs +0 -177
- package/mcp-server/lib/plan-publication-worker.cjs +0 -830
- package/mcp-server/lib/plan-store.cjs +0 -794
- package/mcp-server/lib/prd-parser.cjs +0 -83
- package/mcp-server/lib/pricing.cjs +0 -44
- package/mcp-server/lib/project-context.cjs +0 -178
- package/mcp-server/lib/projector.cjs +0 -386
- package/mcp-server/lib/provider-refs.cjs +0 -42
- package/mcp-server/lib/runtime-paths.cjs +0 -1714
- package/mcp-server/lib/runtime-state.cjs +0 -222
- package/mcp-server/lib/safe-project-file.cjs +0 -385
- package/mcp-server/lib/stage-checkpoints.cjs +0 -463
- package/mcp-server/lib/state-authority.cjs +0 -95
- package/mcp-server/lib/state-db.cjs +0 -1580
- package/mcp-server/lib/state-ops.cjs +0 -1428
- package/mcp-server/lib/task-contract.cjs +0 -404
- package/mcp-server/lib/task-expander.cjs +0 -105
- package/mcp-server/lib/task-ledger.cjs +0 -1964
- package/mcp-server/lib/telemetry.cjs +0 -85
- package/mcp-server/lib/topo.cjs +0 -131
- package/mcp-server/lib/ultra-facade.cjs +0 -2684
- package/mcp-server/lib/worker-packet.cjs +0 -358
- package/mcp-server/server.cjs +0 -437
- package/orchestrator/auto-merge.cjs +0 -128
- package/orchestrator/daemon.cjs +0 -264
- package/orchestrator/dispatch-rules.cjs +0 -78
- package/orchestrator/parallel-orchestrator.cjs +0 -903
- package/orchestrator/planner/plan-builder.cjs +0 -125
- package/orchestrator/recovery.cjs +0 -188
- package/orchestrator/session-close-journal-worker.cjs +0 -5
- package/orchestrator/session-close-journal.cjs +0 -1014
- package/orchestrator/session-runner.cjs +0 -1153
- package/orchestrator/worktree-manager.cjs +0 -282
- package/settings.json +0 -7
- package/skills/cc-collab/SKILL.md +0 -42
- package/skills/code-review-expert/SKILL.md +0 -43
- package/skills/code-review-expert/references/correctness-reliability.md +0 -30
- package/skills/code-review-expert/references/design-boundaries.md +0 -26
- package/skills/code-review-expert/references/removal-plan.md +0 -24
- package/skills/codex-collab/SKILL.md +0 -49
- package/skills/codex-collab/evals/evals.json +0 -35
- package/skills/integration-rules/SKILL.md +0 -32
- package/skills/security-rules/SKILL.md +0 -38
- package/skills/testing-rules/SKILL.md +0 -32
- package/skills/ultra-doctor/SKILL.md +0 -41
- package/skills/ultra-plan/references/semantic-preflight.md +0 -20
- package/skills/ultra-verify/SKILL.md +0 -46
- package/skills/ultra-verify/evals/evals.json +0 -46
- package/skills/ultra-verify/references/cross-verify-modes.md +0 -28
- package/skills/ultra-verify/references/evidence-status.md +0 -21
- package/skills/ultra-verify/scripts/verify_wait.py +0 -94
- package/spec/cli-protocol.md +0 -102
- package/spec/mcp-tools.yaml +0 -312
- package/spec/schemas/baseline-reconciliation.v1.schema.json +0 -73
- package/spec/schemas/change-delta.v1.schema.json +0 -153
- package/spec/schemas/command-manifest.schema.json +0 -29
- package/spec/schemas/context-file.v4.5.schema.json +0 -54
- package/spec/schemas/delivery-report.v1.schema.json +0 -66
- package/spec/schemas/documentation-reconciliation.v1.schema.json +0 -106
- package/spec/schemas/mcp-tools.schema.json +0 -136
- package/spec/schemas/skill-manifest.schema.json +0 -24
- package/spec/schemas/state-db.sql +0 -641
- package/spec/schemas/task-ledger.v2.schema.json +0 -296
- package/spec/schemas/tasks.v4.5.schema.json +0 -96
- package/spec/schemas/test-report.v1.schema.json +0 -225
- package/ultra-tools/cli.cjs +0 -129
- package/ultra-tools/commands/db.cjs +0 -226
- package/ultra-tools/commands/legacy-memory.cjs +0 -159
- package/ultra-tools/commands/migrate.cjs +0 -1386
- package/ultra-tools/commands/session.cjs +0 -213
- package/ultra-tools/commands/status.cjs +0 -559
- package/ultra-tools/commands/system.cjs +0 -498
- package/ultra-tools/commands/task.cjs +0 -105
package/ultra-tools/cli.cjs
DELETED
|
@@ -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
|
-
};
|