@theglitchking/semantic-memory 1.2.2 → 1.5.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 (65) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +137 -0
  4. package/ROADMAP.md +5 -2
  5. package/commands/healthcheck.md +13 -11
  6. package/dist/{chunk-UQSASB7Q.js → chunk-2EELGTLX.js} +32 -3
  7. package/dist/chunk-2EELGTLX.js.map +1 -0
  8. package/dist/chunk-5ZKNO6ZD.js +96 -0
  9. package/dist/chunk-5ZKNO6ZD.js.map +1 -0
  10. package/dist/chunk-7M3JZOYH.js +105 -0
  11. package/dist/chunk-7M3JZOYH.js.map +1 -0
  12. package/dist/chunk-A3KYHKB5.js +176 -0
  13. package/dist/chunk-A3KYHKB5.js.map +1 -0
  14. package/dist/{chunk-CYWPDFNI.js → chunk-CSXZ7QUU.js} +4 -203
  15. package/dist/chunk-CSXZ7QUU.js.map +1 -0
  16. package/dist/chunk-FM26NFTR.js +272 -0
  17. package/dist/chunk-FM26NFTR.js.map +1 -0
  18. package/dist/chunk-NTYYS53O.js +91 -0
  19. package/dist/chunk-NTYYS53O.js.map +1 -0
  20. package/dist/{chunk-E2Z4J4MK.js → chunk-OWZWMB4P.js} +70 -2
  21. package/dist/chunk-OWZWMB4P.js.map +1 -0
  22. package/dist/chunk-S4KW7VH4.js +203 -0
  23. package/dist/chunk-S4KW7VH4.js.map +1 -0
  24. package/dist/{chunk-HODDRXJK.js → chunk-V45NJIHI.js} +4 -2
  25. package/dist/chunk-V45NJIHI.js.map +1 -0
  26. package/dist/chunk-WSU4QIPB.js +311 -0
  27. package/dist/chunk-WSU4QIPB.js.map +1 -0
  28. package/dist/cli/index.js +265 -65
  29. package/dist/cli/index.js.map +1 -1
  30. package/dist/core/index.d.ts +39 -2
  31. package/dist/core/index.js +17 -11
  32. package/dist/decay-ELJUKVQG.js +14 -0
  33. package/dist/dossier-2UG724LB.js +39 -0
  34. package/dist/healthcheck-IMB47LZE.js +19 -0
  35. package/dist/indexer-YW4O3VVM.js +8 -0
  36. package/dist/lexicon-5PXQEMUF.js +26 -0
  37. package/dist/lexicon-5PXQEMUF.js.map +1 -0
  38. package/dist/lint-XUQ7JDYX.js +16 -0
  39. package/dist/lint-XUQ7JDYX.js.map +1 -0
  40. package/dist/mcp/server.js +478 -40
  41. package/dist/mcp/server.js.map +1 -1
  42. package/dist/profile-LXUEF7TX.js +20 -0
  43. package/dist/profile-LXUEF7TX.js.map +1 -0
  44. package/dist/{schema-F3RTY6SQ.js → schema-ATFCLC6Y.js} +2 -2
  45. package/dist/schema-ATFCLC6Y.js.map +1 -0
  46. package/dist/{search-27WYAI24.js → search-AHKEFI2B.js} +2 -2
  47. package/dist/telemetry-GB2TEHVE.js +21 -0
  48. package/dist/telemetry-GB2TEHVE.js.map +1 -0
  49. package/hooks/vault-context.js +270 -11
  50. package/package.json +2 -1
  51. package/skills/vault-first/SKILL.md +18 -0
  52. package/dist/chunk-CAN2GWUG.js +0 -88
  53. package/dist/chunk-CAN2GWUG.js.map +0 -1
  54. package/dist/chunk-CYWPDFNI.js.map +0 -1
  55. package/dist/chunk-E2Z4J4MK.js.map +0 -1
  56. package/dist/chunk-HODDRXJK.js.map +0 -1
  57. package/dist/chunk-UQSASB7Q.js.map +0 -1
  58. package/dist/healthcheck-E4GI5RMZ.js +0 -14
  59. package/dist/indexer-KD6UTXZB.js +0 -8
  60. package/dist/lint-GMXQF4RW.js +0 -11
  61. /package/dist/{healthcheck-E4GI5RMZ.js.map → decay-ELJUKVQG.js.map} +0 -0
  62. /package/dist/{indexer-KD6UTXZB.js.map → dossier-2UG724LB.js.map} +0 -0
  63. /package/dist/{lint-GMXQF4RW.js.map → healthcheck-IMB47LZE.js.map} +0 -0
  64. /package/dist/{schema-F3RTY6SQ.js.map → indexer-YW4O3VVM.js.map} +0 -0
  65. /package/dist/{search-27WYAI24.js.map → search-AHKEFI2B.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -7,6 +7,46 @@ import { resolve as resolve2, join as join3, dirname, relative } from "path";
7
7
  import { existsSync as existsSync3, readFileSync, writeFileSync, rmSync } from "fs";
8
8
  import { createRequire } from "module";
9
9
  import { spawnSync } from "child_process";
10
+ import { pathToFileURL } from "url";
11
+
12
+ // src/core/healthcheck-fix.ts
13
+ var MANUAL_REASON = {
14
+ "regenerate-contract": "AGENTS.md needs a human decision \u2014 run `/contract` (regenerate_contract), or move custom content into a Local Notes section outside the managed block.",
15
+ lint_vault: "Vault lint findings (stale notes, broken wikilinks, schema violations) require human review \u2014 run `lint_vault` for the full report.",
16
+ session_staleness: "Resume the session (session_run / session_finish) or remove .claude/.semantic-memory/session.json to abandon it."
17
+ };
18
+ function planFixes(findings) {
19
+ const fixes = [];
20
+ const skipped = [];
21
+ for (const f of findings) {
22
+ if (f.severity === "ok") continue;
23
+ switch (f.fixable_via) {
24
+ case "skill-link":
25
+ fixes.push({ check: f.check, action: "skill-link", summary: f.summary });
26
+ break;
27
+ case "mcp-reconcile":
28
+ fixes.push({ check: f.check, action: "mcp-reconcile", summary: f.summary });
29
+ break;
30
+ case "reindex":
31
+ fixes.push({ check: f.check, action: "reindex", summary: f.summary });
32
+ break;
33
+ case "state-migrate":
34
+ fixes.push({ check: f.check, action: "state-migrate", summary: f.summary });
35
+ break;
36
+ case "regenerate-contract":
37
+ skipped.push({ check: f.check, reason: MANUAL_REASON["regenerate-contract"] });
38
+ break;
39
+ default:
40
+ skipped.push({
41
+ check: f.check,
42
+ reason: MANUAL_REASON[f.check] ?? "No automatic fix available \u2014 needs human review."
43
+ });
44
+ }
45
+ }
46
+ return { fixes, skipped };
47
+ }
48
+
49
+ // src/cli/index.ts
10
50
  import { registerUpdateCommands } from "@theglitchking/claude-plugin-runtime";
11
51
 
12
52
  // src/cli/skills.ts
@@ -287,6 +327,61 @@ function runRelink(cwd) {
287
327
  stdio: "inherit"
288
328
  });
289
329
  }
330
+ async function executeFixes(cwd, fixes) {
331
+ const outcomes = [];
332
+ const actions = [...new Set(fixes.map((f) => f.action))];
333
+ for (const action of actions) {
334
+ const checks = fixes.filter((f) => f.action === action).map((f) => f.check).join(", ");
335
+ try {
336
+ switch (action) {
337
+ case "skill-link": {
338
+ runRelink(cwd);
339
+ outcomes.push({ check: checks, action, status: "fixed", detail: "re-linked skills" });
340
+ break;
341
+ }
342
+ case "state-migrate": {
343
+ const r = migrateState({ projectRoot: cwd, force: true });
344
+ outcomes.push({
345
+ check: checks,
346
+ action,
347
+ status: "fixed",
348
+ detail: `migrated ${r.migrated.length} file(s), resolved ${r.conflicts.length} conflict(s)`
349
+ });
350
+ break;
351
+ }
352
+ case "mcp-reconcile": {
353
+ const reconcilePath = join3(findSourceRoot(), "hooks", "reconcile.js");
354
+ if (!existsSync3(reconcilePath)) {
355
+ outcomes.push({ check: checks, action, status: "failed", detail: "reconcile.js not found" });
356
+ break;
357
+ }
358
+ const mod = await import(pathToFileURL(reconcilePath).href);
359
+ mod.reconcile(cwd);
360
+ outcomes.push({ check: checks, action, status: "fixed", detail: "reconciled .mcp.json" });
361
+ break;
362
+ }
363
+ case "reindex": {
364
+ const bin = findLocalBin(cwd);
365
+ if (!bin) {
366
+ outcomes.push({ check: checks, action, status: "failed", detail: "no local bin to run reindex" });
367
+ break;
368
+ }
369
+ const vault = join3(cwd, ".claude", ".vault");
370
+ const r = spawnSync("node", [bin, "--notes", vault, "--reindex"], { cwd, stdio: "pipe", timeout: 12e4 });
371
+ if (r.status === 0) {
372
+ outcomes.push({ check: checks, action, status: "fixed", detail: "reindexed vault" });
373
+ } else {
374
+ outcomes.push({ check: checks, action, status: "failed", detail: (r.stderr?.toString() || "reindex failed").slice(0, 200) });
375
+ }
376
+ break;
377
+ }
378
+ }
379
+ } catch (err) {
380
+ outcomes.push({ check: checks, action, status: "failed", detail: err?.message ?? String(err) });
381
+ }
382
+ }
383
+ return outcomes;
384
+ }
290
385
  function findLocalBin(cwd) {
291
386
  const candidates = [
292
387
  join3(cwd, "node_modules", "@theglitchking", "semantic-memory", "bin", "semantic-memory"),
@@ -526,7 +621,7 @@ program.name("semantic-memory").description(
526
621
  "Unified memory-layer MCP server for Claude Code (rebrand of semantic-sidekick at v1.0.0).\nThe `semantic-sidekick` command is preserved as an alias.\n\n Start MCP server: semantic-memory --notes ./vault\n Show vault stats: semantic-memory --notes ./vault --stats\n Force reindex: semantic-memory --notes ./vault --reindex\n List MCP tools: semantic-memory tools\n Tool details: semantic-memory tools search_semantic"
527
622
  ).version(version);
528
623
  program.command("search <query>").description("Run a vault search and print top hits as JSON (used by activation hooks)").requiredOption("--notes <path>", "Path to markdown notes directory").option("--limit <n>", "Max results", (v) => parseInt(v, 10), 8).option("--text-only", "Skip embedder \u2014 keyword-only search (fast path, no model init)").option("--json", "Emit raw JSON (default)", true).action(async (query, opts) => {
529
- const { runSearch } = await import("../search-27WYAI24.js");
624
+ const { runSearch } = await import("../search-AHKEFI2B.js");
530
625
  try {
531
626
  const hits = await runSearch({
532
627
  notes: opts.notes,
@@ -542,7 +637,7 @@ program.command("search <query>").description("Run a vault search and print top
542
637
  }
543
638
  });
544
639
  program.command("lint").description("Lint the vault against vault.schema.yml (schema violations + missing provenance + stale)").requiredOption("--notes <path>", "Path to markdown notes directory").option("--rule <name>", "Limit output to one rule: schema_violations | missing_provenance | stale").option("--json", "Emit JSON instead of the human-readable report").option("--strict", "Exit non-zero on warnings too (default: exit non-zero only on errors)").action(async (opts) => {
545
- const { lintVault, formatLintReport } = await import("../lint-GMXQF4RW.js");
640
+ const { lintVault, formatLintReport } = await import("../lint-XUQ7JDYX.js");
546
641
  const report = await lintVault(resolve2(opts.notes));
547
642
  if (opts.json) {
548
643
  const out = opts.rule ? report.byRule[opts.rule] : report;
@@ -593,7 +688,7 @@ program.command("log-query").description("Read structured log entries from log.m
593
688
  }
594
689
  });
595
690
  program.command("install-schema").description("Bootstrap vault.schema.yml in the vault root with the default schema").requiredOption("--notes <path>", "Path to markdown notes directory").option("--force", "Overwrite existing schema file").action(async (opts) => {
596
- const { installDefaultSchema } = await import("../schema-F3RTY6SQ.js");
691
+ const { installDefaultSchema } = await import("../schema-ATFCLC6Y.js");
597
692
  const r = await installDefaultSchema(resolve2(opts.notes), opts.force);
598
693
  console.log(r.written ? `Installed default schema: ${r.path}` : `Schema already exists: ${r.path} (use --force to overwrite)`);
599
694
  process.exit(0);
@@ -613,7 +708,7 @@ program.command("serve", { isDefault: true }).description("Start the MCP server
613
708
  process.exit(1);
614
709
  }
615
710
  if (opts.stats) {
616
- const { Indexer } = await import("../indexer-KD6UTXZB.js");
711
+ const { Indexer } = await import("../indexer-YW4O3VVM.js");
617
712
  const indexer = new Indexer(notesPath);
618
713
  const docs = await indexer.indexAll();
619
714
  console.log(`Notes: ${docs.length}`);
@@ -722,8 +817,8 @@ ${verify.stderr?.toString() || ""}`);
722
817
  process.exit(0);
723
818
  });
724
819
  program.command("healthcheck").description(
725
- "Verify the local install starts cleanly + run drift detection. Self-heals common npx-cache corruption (ERR_MODULE_NOT_FOUND). With --fast, skips the slow tier (full vault lint)."
726
- ).option("--fast", "Run only the fast-tier drift checks (file-system probes). Skips the slow tier (full vault lint).", false).option("--json", "Emit structured JSON output instead of human-readable text.", false).action(async (opts) => {
820
+ "Verify the local install starts cleanly + run drift detection. Self-heals common npx-cache corruption (ERR_MODULE_NOT_FOUND). With --fast, skips the slow tier (full vault lint). With --fix, auto-applies safe fixes for fixable findings."
821
+ ).option("--fast", "Run only the fast-tier drift checks (file-system probes). Skips the slow tier (full vault lint).", false).option("--json", "Emit structured JSON output instead of human-readable text.", false).option("--fix", "Auto-apply safe fixes for fixable drift findings (re-link skills, reconcile .mcp.json, reindex, migrate state). Findings that need human review are reported, not touched.", false).action(async (opts) => {
727
822
  const cwd = process.cwd();
728
823
  const bin = findLocalBin(cwd);
729
824
  if (!bin) {
@@ -732,85 +827,86 @@ program.command("healthcheck").description(
732
827
  process.exit(1);
733
828
  }
734
829
  const mcpPath = join3(cwd, ".mcp.json");
735
- if (existsSync3(mcpPath)) {
830
+ if (existsSync3(mcpPath) && !opts.json) {
736
831
  const data = readJsonSafe(mcpPath);
737
832
  const entries = data?.mcpServers && typeof data.mcpServers === "object" ? Object.entries(data.mcpServers) : [];
738
833
  const fragile = entries.filter(([, e]) => isNpxForm(e)).map(([k]) => k);
739
834
  if (fragile.length > 0) {
740
- console.warn(
741
- `\u26A0\uFE0F .mcp.json uses the fragile npx-@latest form for: ${fragile.join(", ")}`
742
- );
835
+ console.warn(`\u26A0\uFE0F .mcp.json uses the fragile npx-@latest form for: ${fragile.join(", ")}`);
743
836
  console.warn(` Rewrite to the stable form:`);
744
837
  console.warn(` npx --no @theglitchking/semantic-memory normalize-config`);
745
838
  }
746
839
  }
747
- const r = spawnSync("node", [bin, "--version"], { cwd, stdio: "pipe", timeout: 15e3 });
748
- if (r.status === 0) {
749
- console.log(`\u2713 local install starts cleanly (v${r.stdout?.toString().trim()})`);
750
- process.exit(0);
751
- }
752
- const stderr = r.stderr?.toString() ?? "";
753
- if (stderr.includes("ERR_MODULE_NOT_FOUND")) {
754
- const match = stderr.match(/([\/~][^'"\s]*\/_npx\/[^\/'"\s]+)/);
755
- if (match) {
756
- const bad = match[1];
757
- console.warn(`detected broken npx cache at ${bad} \u2014 clearing and retrying...`);
758
- try {
759
- rmSync(bad, { recursive: true, force: true });
760
- } catch {
761
- }
762
- const r2 = spawnSync("node", [bin, "--version"], { cwd, stdio: "pipe", timeout: 15e3 });
763
- if (r2.status === 0) {
764
- console.log(`\u2713 cleared npx cache and verified (v${r2.stdout?.toString().trim()})`);
765
- process.exit(0);
766
- }
767
- console.error(`retry still failed:
768
- ${r2.stderr?.toString() || ""}`);
840
+ let smokeOk = false;
841
+ let smokeVersion = "";
842
+ {
843
+ const r = spawnSync("node", [bin, "--version"], { cwd, stdio: "pipe", timeout: 15e3 });
844
+ if (r.status === 0) {
845
+ smokeOk = true;
846
+ smokeVersion = r.stdout?.toString().trim() ?? "";
769
847
  } else {
770
- console.error(`ERR_MODULE_NOT_FOUND but could not locate offending cache path in the error.`);
848
+ const stderr = r.stderr?.toString() ?? "";
849
+ if (stderr.includes("ERR_MODULE_NOT_FOUND")) {
850
+ const match = stderr.match(/([\/~][^'"\s]*\/_npx\/[^\/'"\s]+)/);
851
+ if (match) {
852
+ const bad = match[1];
853
+ if (!opts.json) console.warn(`detected broken npx cache at ${bad} \u2014 clearing and retrying...`);
854
+ try {
855
+ rmSync(bad, { recursive: true, force: true });
856
+ } catch {
857
+ }
858
+ const r2 = spawnSync("node", [bin, "--version"], { cwd, stdio: "pipe", timeout: 15e3 });
859
+ if (r2.status === 0) {
860
+ smokeOk = true;
861
+ smokeVersion = r2.stdout?.toString().trim() ?? "";
862
+ } else if (!opts.json) {
863
+ console.error(`retry still failed:
864
+ ${r2.stderr?.toString() || ""}`);
865
+ }
866
+ }
867
+ }
868
+ if (!smokeOk && !opts.json && stderr) console.error(stderr);
771
869
  }
772
870
  }
773
- if (stderr) console.error(stderr);
774
- if (r.status !== 0) {
775
- console.error(`local install failed (exit ${r.status})`);
776
- process.exit(1);
871
+ if (smokeOk && !opts.json) console.log(`\u2713 local install starts cleanly (v${smokeVersion})`);
872
+ const { runHealthcheck, formatDriftBanner, filterToDrift } = await import("../healthcheck-IMB47LZE.js");
873
+ const vaultCandidate = join3(cwd, ".claude", ".vault");
874
+ const vaultPath = existsSync3(vaultCandidate) ? vaultCandidate : void 0;
875
+ let result = await runHealthcheck({ projectRoot: cwd, vaultPath, tier: opts.fast ? "fast" : "all", force: true });
876
+ let fixOutcomes = [];
877
+ let plan = { fixes: [], skipped: [] };
878
+ if (opts.fix) {
879
+ plan = planFixes(filterToDrift(result));
880
+ if (plan.fixes.length > 0) {
881
+ fixOutcomes = await executeFixes(cwd, plan.fixes);
882
+ result = await runHealthcheck({ projectRoot: cwd, vaultPath, tier: opts.fast ? "fast" : "all", force: true });
883
+ }
777
884
  }
778
- });
779
- var driftHealthcheckActionPostprocess = async (opts) => {
780
- try {
781
- const { runHealthcheck, formatDriftBanner } = await import("../healthcheck-E4GI5RMZ.js");
782
- const projectRoot = process.cwd();
783
- const vaultCandidate = join3(projectRoot, ".claude", ".vault");
784
- const vaultPath = existsSync3(vaultCandidate) ? vaultCandidate : void 0;
785
- const result = await runHealthcheck({
786
- projectRoot,
787
- vaultPath,
788
- tier: opts.fast ? "fast" : "all",
789
- force: true
790
- });
791
- if (opts.json) {
792
- console.log(JSON.stringify(result, null, 2));
793
- return;
885
+ if (opts.json) {
886
+ console.log(JSON.stringify({ smoke: { ok: smokeOk, version: smokeVersion }, result, fix: opts.fix ? { plan, outcomes: fixOutcomes } : void 0 }, null, 2));
887
+ } else {
888
+ if (opts.fix) {
889
+ if (fixOutcomes.length === 0 && plan.skipped.length === 0) {
890
+ console.log(`
891
+ \u2713 nothing to fix`);
892
+ } else {
893
+ console.log(`
894
+ --fix applied ${fixOutcomes.filter((o) => o.status === "fixed").length}/${fixOutcomes.length} action(s):`);
895
+ for (const o of fixOutcomes) console.log(` ${o.status === "fixed" ? "\u2713" : "\u2717"} ${o.action} (${o.check}) \u2014 ${o.detail}`);
896
+ for (const s of plan.skipped) console.log(` \u2022 skipped ${s.check} \u2014 ${s.reason}`);
897
+ }
794
898
  }
795
899
  const banner = formatDriftBanner(result);
796
900
  if (banner) {
797
901
  console.log("");
798
902
  console.log(banner);
799
903
  } else {
800
- console.log("");
801
- console.log(`\u2713 no drift detected (${result.findings.length} checks ran in ${result.duration_ms}ms)`);
904
+ console.log(`
905
+ \u2713 no drift detected (${result.findings.length} checks ran in ${result.duration_ms}ms)`);
802
906
  }
803
- } catch (err) {
804
- console.error(`drift check threw: ${err?.message ?? err}`);
805
907
  }
806
- };
807
- var healthcheckCmd = program.commands.find((c) => c.name() === "healthcheck");
808
- if (healthcheckCmd) {
809
- healthcheckCmd.hook("postAction", async (thisCmd) => {
810
- const opts = thisCmd.opts();
811
- await driftHealthcheckActionPostprocess({ fast: !!opts.fast, json: !!opts.json });
812
- });
813
- }
908
+ process.exit(smokeOk ? 0 : 1);
909
+ });
814
910
  function parseAgent(input) {
815
911
  const a = input.toLowerCase();
816
912
  if (!SKILL_BUNDLER_AGENTS.includes(a)) {
@@ -899,5 +995,109 @@ ${result.conflicts.length} conflict${result.conflicts.length === 1 ? "" : "s"} d
899
995
  process.exit(1);
900
996
  }
901
997
  });
998
+ program.command("decay-config").description("Print the active confidence-decay config (shipped defaults + any vault.schema.yml override).").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
999
+ const { loadDecayConfig, DEFAULT_DECAY_CONFIG } = await import("../decay-ELJUKVQG.js");
1000
+ const cfg = loadDecayConfig(resolve2(opts.notes));
1001
+ const source = JSON.stringify(cfg) === JSON.stringify(DEFAULT_DECAY_CONFIG) ? "defaults" : "vault.schema.yml (overridden)";
1002
+ console.log(JSON.stringify({ source, config: cfg }, null, 2));
1003
+ process.exit(0);
1004
+ });
1005
+ program.command("decay-trace <notePath>").description("Show the decay calculation for one note: type, last_verified, age, half-life, evergreen, multiplier.").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (notePath, opts) => {
1006
+ const { loadDecayConfig, computeDecay, normalizeVerifiedDate } = await import("../decay-ELJUKVQG.js");
1007
+ const matter = (await import("gray-matter")).default;
1008
+ const abs = resolve2(opts.notes, notePath);
1009
+ if (!existsSync3(abs)) {
1010
+ console.error(`note not found: ${abs}`);
1011
+ process.exit(1);
1012
+ }
1013
+ const { data: fm } = matter(readFileSync(abs, "utf8"));
1014
+ const lastVerified = normalizeVerifiedDate(fm.last_verified);
1015
+ const d = computeDecay({
1016
+ type: typeof fm.type === "string" ? fm.type : void 0,
1017
+ last_verified: lastVerified,
1018
+ evergreen: fm.evergreen === true,
1019
+ config: loadDecayConfig(resolve2(opts.notes))
1020
+ });
1021
+ console.log(
1022
+ JSON.stringify(
1023
+ {
1024
+ path: notePath,
1025
+ type: fm.type ?? null,
1026
+ last_verified: lastVerified ?? null,
1027
+ evergreen: fm.evergreen === true,
1028
+ multiplier: d.multiplier,
1029
+ age_days: Math.round(d.age_days),
1030
+ effective_half_life: d.effective_half_life,
1031
+ reason: d.reason
1032
+ },
1033
+ null,
1034
+ 2
1035
+ )
1036
+ );
1037
+ process.exit(0);
1038
+ });
1039
+ var lexiconCmd = program.command("lexicon").description("Manage the learned human\u2192artifact lexicon (aliases).");
1040
+ lexiconCmd.command("list").description("List all aliases in the lexicon.").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
1041
+ const { loadLexiconCache } = await import("../lexicon-5PXQEMUF.js");
1042
+ console.log(JSON.stringify(await loadLexiconCache(resolve2(opts.notes)), null, 2));
1043
+ process.exit(0);
1044
+ });
1045
+ lexiconCmd.command("compile").description("Rebuild the derived lexicon cache from the lexicon/ notes.").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
1046
+ const { compileLexicon } = await import("../lexicon-5PXQEMUF.js");
1047
+ const entries = await compileLexicon(resolve2(opts.notes));
1048
+ console.log(`compiled ${entries.length} alias(es)`);
1049
+ process.exit(0);
1050
+ });
1051
+ lexiconCmd.command("add <canonical> <phrases...>").description("Upsert an alias: a canonical target and the human phrases that refer to it.").requiredOption("--notes <path>", "Path to markdown notes directory").option("--authored", "Mark as human-authored (default: learned)").action(async (canonical, phrases, opts) => {
1052
+ const { addAlias } = await import("../lexicon-5PXQEMUF.js");
1053
+ const r = await addAlias(resolve2(opts.notes), { canonical, phrases, source: opts.authored ? "authored" : "learned" });
1054
+ console.log(JSON.stringify(r, null, 2));
1055
+ process.exit(0);
1056
+ });
1057
+ var dossierCmd = program.command("dossier").description("Manage entity dossiers \u2014 per-component living notes (purpose / failure modes / knobs / incident log / current state).");
1058
+ dossierCmd.command("init <entity>").description("Scaffold a dossier for an entity (no-op if one already exists). Optionally seed aliases and provenance.").requiredOption("--notes <path>", "Path to markdown notes directory").option("--alias <phrase...>", "Human phrase(s) for this entity \u2014 fed to the lexicon compiler").option("--seeded-from <ref>", "Provenance of the scaffold (e.g. a babel-fish project-map entry)").action(async (entity, opts) => {
1059
+ const { initDossier } = await import("../dossier-2UG724LB.js");
1060
+ const r = await initDossier(resolve2(opts.notes), entity, { aliases: opts.alias, seeded_from: opts.seededFrom });
1061
+ console.log(JSON.stringify(r, null, 2));
1062
+ process.exit(0);
1063
+ });
1064
+ dossierCmd.command("list").description("List all dossiers with their entity, aliases, and current state.").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
1065
+ const { listDossiers } = await import("../dossier-2UG724LB.js");
1066
+ console.log(JSON.stringify(await listDossiers(resolve2(opts.notes)), null, 2));
1067
+ process.exit(0);
1068
+ });
1069
+ var profileCmd = program.command("profile").description("Manage the speaker profile \u2014 how THIS human communicates (severity calibration, chronic omissions, verbosity, shorthand).");
1070
+ profileCmd.command("init").description("Scaffold profile/speaker.md with the fixed sections (no-op if it exists).").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
1071
+ const { initSpeakerProfile } = await import("../profile-LXUEF7TX.js");
1072
+ const r = await initSpeakerProfile(resolve2(opts.notes));
1073
+ console.log(JSON.stringify(r, null, 2));
1074
+ process.exit(0);
1075
+ });
1076
+ profileCmd.command("show").description("Print the speaker profile's injection head (the capped, placeholder-stripped view).").requiredOption("--notes <path>", "Path to markdown notes directory").action(async (opts) => {
1077
+ const { readSpeakerProfile } = await import("../profile-LXUEF7TX.js");
1078
+ const p = await readSpeakerProfile(resolve2(opts.notes));
1079
+ console.log(p ? p.head || "(profile is empty \u2014 only placeholders)" : "(no speaker profile \u2014 run `profile init`)");
1080
+ process.exit(0);
1081
+ });
1082
+ program.command("selection-stats").description("Summarize the local selection log: searches, selections, most-cited notes, and retrieved-but-never-cited notes.").requiredOption("--notes <path>", "Path to markdown notes directory").option("--json", "Emit raw JSON instead of a human summary").action(async (opts) => {
1083
+ const { computeSelectionStats, selectionLogPath } = await import("../telemetry-GB2TEHVE.js");
1084
+ const stats = await computeSelectionStats(resolve2(opts.notes));
1085
+ if (opts.json) {
1086
+ console.log(JSON.stringify(stats, null, 2));
1087
+ process.exit(0);
1088
+ }
1089
+ console.log(`Selection log: ${selectionLogPath(resolve2(opts.notes))}`);
1090
+ console.log(` searches: ${stats.searches}`);
1091
+ console.log(` selections: ${stats.selections}`);
1092
+ const topCited = Object.entries(stats.citedPaths).sort((a, b) => b[1] - a[1]).slice(0, 10);
1093
+ if (topCited.length) {
1094
+ console.log(` most cited:`);
1095
+ for (const [p, n] of topCited) console.log(` ${n}\xD7 ${p}`);
1096
+ }
1097
+ if (stats.retrievedNeverCited.length) {
1098
+ console.log(` retrieved but never cited (${stats.retrievedNeverCited.length}): ${stats.retrievedNeverCited.slice(0, 10).join(", ")}${stats.retrievedNeverCited.length > 10 ? " \u2026" : ""}`);
1099
+ }
1100
+ process.exit(0);
1101
+ });
902
1102
  program.parse();
903
1103
  //# sourceMappingURL=index.js.map