@jhizzard/termdeck 1.0.0 → 1.0.2
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/package.json +2 -1
- package/packages/cli/src/init-mnestra.js +149 -1
- package/packages/cli/src/init-rumen.js +370 -35
- package/packages/server/src/setup/audit-upgrade.js +425 -0
- package/packages/server/src/setup/index.js +2 -1
- package/packages/server/src/setup/mnestra-migrations/013_reclassify_uncertain.sql +39 -0
- package/packages/server/src/setup/mnestra-migrations/014_explicit_grants.sql +46 -0
- package/packages/server/src/setup/mnestra-migrations/015_source_agent.sql +51 -0
- package/packages/server/src/setup/mnestra-migrations/016_mnestra_doctor_probes.sql +117 -0
- package/packages/server/src/setup/mnestra-migrations/017_memory_sessions_session_metadata.sql +94 -0
- package/packages/server/src/setup/preconditions.js +36 -4
- package/packages/server/src/setup/rumen/functions/graph-inference/index.ts +6 -2
- package/packages/server/src/setup/rumen/functions/rumen-tick/index.ts +6 -2
- package/packages/stack-installer/README.md +73 -0
- package/packages/stack-installer/assets/hooks/README.md +172 -0
- package/packages/stack-installer/assets/hooks/memory-session-end.js +740 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jhizzard/termdeck",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Browser-based terminal multiplexer with metadata overlays, panel flashback memory recall, and AI-aware session management",
|
|
5
5
|
"bin": {
|
|
6
6
|
"termdeck": "./packages/cli/src/index.js"
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"packages/cli/templates/**",
|
|
12
12
|
"packages/server/src/**",
|
|
13
13
|
"packages/client/public/**",
|
|
14
|
+
"packages/stack-installer/assets/hooks/**",
|
|
14
15
|
"config/config.example.yaml",
|
|
15
16
|
"config/secrets.env.example",
|
|
16
17
|
"config/transcript-migration.sql",
|
|
@@ -45,7 +45,8 @@ const {
|
|
|
45
45
|
supabaseUrl: urlHelper,
|
|
46
46
|
migrations,
|
|
47
47
|
pgRunner,
|
|
48
|
-
preconditions
|
|
48
|
+
preconditions,
|
|
49
|
+
auditUpgrade: auditUpgradeMod
|
|
49
50
|
} = require(SETUP_DIR);
|
|
50
51
|
|
|
51
52
|
const HELP = [
|
|
@@ -339,6 +340,43 @@ async function applyMigrations(client, dryRun) {
|
|
|
339
340
|
}
|
|
340
341
|
}
|
|
341
342
|
|
|
343
|
+
// Sprint 51.5 T1 — schema-introspection audit-upgrade. Runs AFTER the
|
|
344
|
+
// fresh-install applyMigrations() loop completes, but reports separately.
|
|
345
|
+
// On a Sprint-37-era project that pre-dated several mnestra migrations,
|
|
346
|
+
// applyMigrations now has the full bundled set (Sprint 51.5 synced 013-015
|
|
347
|
+
// from canonical engram), so this audit is mostly a belt-and-suspenders
|
|
348
|
+
// confirmation. It still surfaces drift if, e.g., the user manually
|
|
349
|
+
// dropped a column post-install. Mnestra-kind only — rumen cron probes
|
|
350
|
+
// reference vault secrets the user hasn't set up yet at this point. Run
|
|
351
|
+
// init-rumen for the rumen-side audit.
|
|
352
|
+
async function runMnestraAudit(client, projectRef, dryRun) {
|
|
353
|
+
step('Audit-upgrade: probing for missing mnestra schema artifacts...');
|
|
354
|
+
if (dryRun) { ok('(dry-run)'); return; }
|
|
355
|
+
const probes = auditUpgradeMod.PROBES.filter((p) => p.kind === 'mnestra');
|
|
356
|
+
let result;
|
|
357
|
+
try {
|
|
358
|
+
result = await auditUpgradeMod.auditUpgrade({
|
|
359
|
+
pgClient: client,
|
|
360
|
+
projectRef,
|
|
361
|
+
probes
|
|
362
|
+
});
|
|
363
|
+
} catch (err) {
|
|
364
|
+
fail(err.message);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
if (result.applied.length === 0 && result.errors.length === 0) {
|
|
368
|
+
ok(`(install up to date — ${result.probed.length} probes all present)`);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
ok(`(probed ${result.probed.length}, applied ${result.applied.length})`);
|
|
372
|
+
for (const name of result.applied) {
|
|
373
|
+
process.stdout.write(` ✓ applied ${name}\n`);
|
|
374
|
+
}
|
|
375
|
+
for (const e of result.errors) {
|
|
376
|
+
process.stdout.write(` ! ${e.name}: ${e.error}\n`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
342
380
|
async function checkExistingStore(client) {
|
|
343
381
|
step('Checking for existing memory_items table...');
|
|
344
382
|
try {
|
|
@@ -443,6 +481,92 @@ function writeYamlConfig(dryRun) {
|
|
|
443
481
|
else ok();
|
|
444
482
|
}
|
|
445
483
|
|
|
484
|
+
// Sprint 51.6 T3 — hook upgrade gap fix.
|
|
485
|
+
//
|
|
486
|
+
// Codex's Sprint 51.6 GAP at 20:11 ET surfaced this: the bundled session-end
|
|
487
|
+
// hook ships in `packages/stack-installer/assets/hooks/memory-session-end.js`,
|
|
488
|
+
// and `npm install -g @jhizzard/termdeck@latest` lands the new bundled file
|
|
489
|
+
// in node_modules — but `termdeck init --mnestra` never touched
|
|
490
|
+
// ~/.claude/hooks/memory-session-end.js. The user's daily-driver kept
|
|
491
|
+
// running the OLD installed copy forever. v1.0.2 closes that gap by adding
|
|
492
|
+
// this refresh step to init --mnestra. The version stamp in the bundled
|
|
493
|
+
// hook (// @termdeck/stack-installer-hook v<N>) gates the overwrite — only
|
|
494
|
+
// strictly-newer bundled stamps trigger a refresh, so a hand-edited
|
|
495
|
+
// installed file with v=current stays put.
|
|
496
|
+
//
|
|
497
|
+
// Backup is best-effort timestamped: `<dest>.bak.<YYYYMMDDhhmmss>`. Matches
|
|
498
|
+
// the pattern Joshua already had on disk from earlier stack-installer runs.
|
|
499
|
+
function refreshBundledHookIfNewer(opts = {}) {
|
|
500
|
+
const dryRun = !!opts.dryRun;
|
|
501
|
+
const HOME = require('os').homedir();
|
|
502
|
+
const HOOK_DEST = opts.destPath || path.join(HOME, '.claude', 'hooks', 'memory-session-end.js');
|
|
503
|
+
// Sprint 51.6 T4-CODEX audit 20:28 ET fix: bundled hook source must be on
|
|
504
|
+
// a path that ships in @jhizzard/termdeck's npm tarball. Root package.json
|
|
505
|
+
// includes `packages/stack-installer/assets/hooks/**` (added 51.6 T3) so
|
|
506
|
+
// this path resolves both in the monorepo and in the published tarball.
|
|
507
|
+
const HOOK_SOURCE = opts.sourcePath
|
|
508
|
+
|| path.join(__dirname, '..', '..', 'stack-installer', 'assets', 'hooks', 'memory-session-end.js');
|
|
509
|
+
const SIG_RE = /@termdeck\/stack-installer-hook\s+v(\d+)/;
|
|
510
|
+
const TERMDECK_MARKERS = [
|
|
511
|
+
/TermDeck session-end memory hook/,
|
|
512
|
+
/@jhizzard\/termdeck-stack/,
|
|
513
|
+
/Vendored into ~\/\.claude\/hooks\/memory-session-end\.js by @jhizzard/i,
|
|
514
|
+
];
|
|
515
|
+
|
|
516
|
+
function readHead(p) {
|
|
517
|
+
try { return fs.readFileSync(p, 'utf8').slice(0, 4096); }
|
|
518
|
+
catch (_) { return null; }
|
|
519
|
+
}
|
|
520
|
+
function readVersion(p) {
|
|
521
|
+
const head = readHead(p);
|
|
522
|
+
if (!head) return null;
|
|
523
|
+
const m = head.match(SIG_RE);
|
|
524
|
+
return m ? parseInt(m[1], 10) : null;
|
|
525
|
+
}
|
|
526
|
+
function looksTermdeckManaged(p) {
|
|
527
|
+
const head = readHead(p);
|
|
528
|
+
if (!head) return false;
|
|
529
|
+
return TERMDECK_MARKERS.some((m) => m.test(head));
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (!fs.existsSync(HOOK_SOURCE)) {
|
|
533
|
+
return { status: 'no-bundled', message: 'bundled hook source not found' };
|
|
534
|
+
}
|
|
535
|
+
const bundled = readVersion(HOOK_SOURCE);
|
|
536
|
+
if (bundled === null) {
|
|
537
|
+
return { status: 'bundled-unsigned', message: 'bundled hook missing version stamp; skipping refresh' };
|
|
538
|
+
}
|
|
539
|
+
if (!fs.existsSync(HOOK_DEST)) {
|
|
540
|
+
if (dryRun) return { status: 'would-install', bundled };
|
|
541
|
+
fs.mkdirSync(path.dirname(HOOK_DEST), { recursive: true });
|
|
542
|
+
fs.copyFileSync(HOOK_SOURCE, HOOK_DEST);
|
|
543
|
+
fs.chmodSync(HOOK_DEST, 0o644);
|
|
544
|
+
return { status: 'installed', bundled };
|
|
545
|
+
}
|
|
546
|
+
const installed = readVersion(HOOK_DEST);
|
|
547
|
+
if (installed !== null && installed >= bundled) {
|
|
548
|
+
return { status: 'up-to-date', installed, bundled };
|
|
549
|
+
}
|
|
550
|
+
// Sprint 51.6 T4-CODEX audit 20:23 ET safety gate: an unsigned installed
|
|
551
|
+
// hook gets refreshed ONLY if it looks TermDeck-managed (carries one of
|
|
552
|
+
// the docstring markers from a prior bundled cut). A genuinely custom
|
|
553
|
+
// user hook with no TermDeck fingerprint stays put.
|
|
554
|
+
if (installed === null && !looksTermdeckManaged(HOOK_DEST)) {
|
|
555
|
+
return {
|
|
556
|
+
status: 'custom-hook-preserved',
|
|
557
|
+
message: 'installed hook lacks TermDeck-managed markers; keeping as-is. Re-run with --force-overwrite to bypass.',
|
|
558
|
+
bundled,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
if (dryRun) return { status: 'would-refresh', from: installed, to: bundled };
|
|
562
|
+
const stamp = new Date().toISOString().replace(/[-:T.Z]/g, '').slice(0, 14);
|
|
563
|
+
const backup = `${HOOK_DEST}.bak.${stamp}`;
|
|
564
|
+
try { fs.copyFileSync(HOOK_DEST, backup); } catch (_) { /* best-effort */ }
|
|
565
|
+
fs.copyFileSync(HOOK_SOURCE, HOOK_DEST);
|
|
566
|
+
fs.chmodSync(HOOK_DEST, 0o644);
|
|
567
|
+
return { status: 'refreshed', from: installed, to: bundled, backup };
|
|
568
|
+
}
|
|
569
|
+
|
|
446
570
|
function printNextSteps() {
|
|
447
571
|
process.stdout.write(`
|
|
448
572
|
Mnestra is configured.
|
|
@@ -538,7 +662,29 @@ async function main(argv) {
|
|
|
538
662
|
try {
|
|
539
663
|
await checkExistingStore(client);
|
|
540
664
|
await applyMigrations(client, false);
|
|
665
|
+
await runMnestraAudit(client, inputs.projectUrl.projectRef, false);
|
|
541
666
|
writeYamlConfig(false);
|
|
667
|
+
// Sprint 51.6 T3: refresh ~/.claude/hooks/memory-session-end.js when the
|
|
668
|
+
// bundled hook's version stamp is newer than the installed copy. Closes
|
|
669
|
+
// the upgrade gap where bundled fixes never reached users' machines via
|
|
670
|
+
// the standard `npm install -g @jhizzard/termdeck@latest && termdeck
|
|
671
|
+
// init --mnestra` path. Best-effort, timestamped backup, fail-soft.
|
|
672
|
+
step('Refreshing ~/.claude/hooks/memory-session-end.js (if bundled is newer)...');
|
|
673
|
+
try {
|
|
674
|
+
const r = refreshBundledHookIfNewer({ dryRun: false });
|
|
675
|
+
if (r.status === 'refreshed') {
|
|
676
|
+
ok(`refreshed v${r.from ?? 0} → v${r.to} (backup: ${path.basename(r.backup)})`);
|
|
677
|
+
} else if (r.status === 'installed') {
|
|
678
|
+
ok(`installed v${r.bundled} (no prior copy)`);
|
|
679
|
+
} else if (r.status === 'up-to-date') {
|
|
680
|
+
ok(`up-to-date (v${r.installed})`);
|
|
681
|
+
} else {
|
|
682
|
+
ok(`(${r.status}${r.message ? ': ' + r.message : ''})`);
|
|
683
|
+
}
|
|
684
|
+
} catch (err) {
|
|
685
|
+
// Don't abort init for a hook-refresh failure — log + continue.
|
|
686
|
+
process.stdout.write(` ! hook refresh failed: ${err.message} (continuing)\n`);
|
|
687
|
+
}
|
|
542
688
|
// v0.6.9: post-write outcome verification. Confirms each migration's
|
|
543
689
|
// expected schema bits actually landed — including memory_items.
|
|
544
690
|
// source_session_id (the v0.6.5 column whose absence cascaded into
|
|
@@ -584,3 +730,5 @@ if (require.main === module) {
|
|
|
584
730
|
}
|
|
585
731
|
|
|
586
732
|
module.exports = main;
|
|
733
|
+
// Sprint 51.6 T3 — exported for tests/init-mnestra-hook-refresh.test.js.
|
|
734
|
+
module.exports.refreshBundledHookIfNewer = refreshBundledHookIfNewer;
|