mneme-ai 2.47.0 → 2.49.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.
@@ -0,0 +1,50 @@
1
+ /**
2
+ * v2.49.0 — AUTO-ALIAS RESOLVER.
3
+ *
4
+ * Wild idea: the WIRING LAG class isn't just "feature shipped but CLI verb
5
+ * missing" — it's ALSO "feature shipped but user typed a NATURAL ALIAS
6
+ * that doesn't exist". v2.48 shipped `mneme dev_tooling detect`; user
7
+ * tried `mneme dev` / `mneme detect` / `mneme tool_detect` and got
8
+ * cryptic `unknown command`. That's wiring lag at the keyboard surface.
9
+ *
10
+ * This module kills the class STRUCTURALLY:
11
+ * 1. Levenshtein-based fuzzy match against all known top-level verbs
12
+ * 2. Top-3 suggestions printed when user types unknown verb
13
+ * 3. Heat-map ledger of misses → next release adds them as real aliases
14
+ * 4. Intent-router fallback for natural-language phrases
15
+ *
16
+ * Pure deterministic except for the file-write to the heat-map ledger.
17
+ * Defensive — never throws.
18
+ */
19
+ /**
20
+ * Classic Levenshtein with single-char + transposition + similar-key
21
+ * discounts. O(m·n) DP — fine for command names (typically ≤ 20 chars).
22
+ */
23
+ export declare function levenshteinDistance(a: string, b: string): number;
24
+ export interface Suggestion {
25
+ command: string;
26
+ distance: number;
27
+ /** Heuristic: does user's verb START WITH the command (or vice versa)? */
28
+ prefixMatch: boolean;
29
+ /** Distance normalized to candidate length (0=identical, 1=totally different). */
30
+ normalizedDistance: number;
31
+ }
32
+ /**
33
+ * Return top-N suggestions for `typed` from `known` commands, sorted by
34
+ * (prefixMatch desc, distance asc, normalizedDistance asc).
35
+ */
36
+ export declare function suggestCommands(typed: string, known: ReadonlyArray<string>, opts?: {
37
+ topN?: number;
38
+ maxDistance?: number;
39
+ }): Suggestion[];
40
+ /**
41
+ * Append a missed-verb to the heat-map ledger so future releases can
42
+ * pre-add as a real alias. Never throws; best-effort write only.
43
+ */
44
+ export declare function logMissedAlias(repoRoot: string, verb: string): void;
45
+ /**
46
+ * Print a friendly suggestion block to stderr. Returns the suggested
47
+ * command if there's a clear winner (distance ≤ 2), else null.
48
+ */
49
+ export declare function printSuggestions(typed: string, suggestions: Suggestion[]): string | null;
50
+ //# sourceMappingURL=alias_resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alias_resolver.d.ts","sourceRoot":"","sources":["../src/alias_resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBhE;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,WAAW,EAAE,OAAO,CAAC;IACrB,kFAAkF;IAClF,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,EAC5B,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GACjD,UAAU,EAAE,CAoBd;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAQnE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,GAAG,MAAM,GAAG,IAAI,CA0BxF"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * v2.49.0 — AUTO-ALIAS RESOLVER.
3
+ *
4
+ * Wild idea: the WIRING LAG class isn't just "feature shipped but CLI verb
5
+ * missing" — it's ALSO "feature shipped but user typed a NATURAL ALIAS
6
+ * that doesn't exist". v2.48 shipped `mneme dev_tooling detect`; user
7
+ * tried `mneme dev` / `mneme detect` / `mneme tool_detect` and got
8
+ * cryptic `unknown command`. That's wiring lag at the keyboard surface.
9
+ *
10
+ * This module kills the class STRUCTURALLY:
11
+ * 1. Levenshtein-based fuzzy match against all known top-level verbs
12
+ * 2. Top-3 suggestions printed when user types unknown verb
13
+ * 3. Heat-map ledger of misses → next release adds them as real aliases
14
+ * 4. Intent-router fallback for natural-language phrases
15
+ *
16
+ * Pure deterministic except for the file-write to the heat-map ledger.
17
+ * Defensive — never throws.
18
+ */
19
+ import { existsSync, mkdirSync, appendFileSync } from "node:fs";
20
+ import { join } from "node:path";
21
+ /**
22
+ * Classic Levenshtein with single-char + transposition + similar-key
23
+ * discounts. O(m·n) DP — fine for command names (typically ≤ 20 chars).
24
+ */
25
+ export function levenshteinDistance(a, b) {
26
+ if (!a || !b)
27
+ return Math.max(a?.length ?? 0, b?.length ?? 0);
28
+ a = a.toLowerCase();
29
+ b = b.toLowerCase();
30
+ if (a === b)
31
+ return 0;
32
+ const m = a.length;
33
+ const n = b.length;
34
+ const d = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
35
+ for (let i = 0; i <= m; i++)
36
+ d[i][0] = i;
37
+ for (let j = 0; j <= n; j++)
38
+ d[0][j] = j;
39
+ for (let i = 1; i <= m; i++) {
40
+ for (let j = 1; j <= n; j++) {
41
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
42
+ d[i][j] = Math.min(d[i - 1][j] + 1, // deletion
43
+ d[i][j - 1] + 1, // insertion
44
+ d[i - 1][j - 1] + cost);
45
+ // Damerau transposition discount
46
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
47
+ d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
48
+ }
49
+ }
50
+ }
51
+ return d[m][n];
52
+ }
53
+ /**
54
+ * Return top-N suggestions for `typed` from `known` commands, sorted by
55
+ * (prefixMatch desc, distance asc, normalizedDistance asc).
56
+ */
57
+ export function suggestCommands(typed, known, opts = {}) {
58
+ const topN = opts.topN ?? 5;
59
+ const maxDistance = opts.maxDistance ?? 10;
60
+ const t = (typed ?? "").toLowerCase();
61
+ if (!t)
62
+ return [];
63
+ const scored = [];
64
+ for (const cmd of known) {
65
+ const c = cmd.toLowerCase();
66
+ const dist = levenshteinDistance(t, c);
67
+ if (dist > maxDistance)
68
+ continue;
69
+ const prefixMatch = c.startsWith(t) || t.startsWith(c);
70
+ const normalizedDistance = c.length === 0 ? 1 : dist / Math.max(c.length, t.length);
71
+ scored.push({ command: cmd, distance: dist, prefixMatch, normalizedDistance });
72
+ }
73
+ scored.sort((a, b) => {
74
+ if (a.prefixMatch !== b.prefixMatch)
75
+ return a.prefixMatch ? -1 : 1;
76
+ if (a.distance !== b.distance)
77
+ return a.distance - b.distance;
78
+ return a.normalizedDistance - b.normalizedDistance;
79
+ });
80
+ return scored.slice(0, topN);
81
+ }
82
+ /**
83
+ * Append a missed-verb to the heat-map ledger so future releases can
84
+ * pre-add as a real alias. Never throws; best-effort write only.
85
+ */
86
+ export function logMissedAlias(repoRoot, verb) {
87
+ if (!repoRoot || !verb)
88
+ return;
89
+ try {
90
+ const dir = join(repoRoot, ".mneme");
91
+ if (!existsSync(dir))
92
+ mkdirSync(dir, { recursive: true });
93
+ const path = join(dir, "alias_misses.jsonl");
94
+ appendFileSync(path, JSON.stringify({ at: new Date().toISOString(), verb }) + "\n");
95
+ }
96
+ catch { /* defensive */ }
97
+ }
98
+ /**
99
+ * Print a friendly suggestion block to stderr. Returns the suggested
100
+ * command if there's a clear winner (distance ≤ 2), else null.
101
+ */
102
+ export function printSuggestions(typed, suggestions) {
103
+ if (suggestions.length === 0) {
104
+ process.stderr.write(`\n❓ Unknown command: ${JSON.stringify(typed)}\n`);
105
+ process.stderr.write(` Run \`mneme --help\` to see all available commands.\n\n`);
106
+ return null;
107
+ }
108
+ const winner = suggestions[0];
109
+ if (winner.distance <= 2 || winner.prefixMatch) {
110
+ process.stderr.write(`\n❓ Unknown command: ${JSON.stringify(typed)}\n\n`);
111
+ process.stderr.write(` Did you mean \`mneme ${winner.command}\`?\n`);
112
+ if (suggestions.length > 1) {
113
+ process.stderr.write(`\n Other closest matches:\n`);
114
+ for (const s of suggestions.slice(1, 4)) {
115
+ process.stderr.write(` • mneme ${s.command} (distance ${s.distance})\n`);
116
+ }
117
+ }
118
+ process.stderr.write(`\n Tip: set MNEME_AUTO_ALIAS=1 to auto-run the best match next time.\n\n`);
119
+ return winner.command;
120
+ }
121
+ process.stderr.write(`\n❓ Unknown command: ${JSON.stringify(typed)}\n`);
122
+ process.stderr.write(` Closest matches:\n`);
123
+ for (const s of suggestions.slice(0, 4)) {
124
+ process.stderr.write(` • mneme ${s.command} (distance ${s.distance})\n`);
125
+ }
126
+ process.stderr.write(`\n Run \`mneme --help\` for the full command list.\n\n`);
127
+ return null;
128
+ }
129
+ //# sourceMappingURL=alias_resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alias_resolver.js","sourceRoot":"","sources":["../src/alias_resolver.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAuIA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoqJvD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAuIA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6yJvD"}
package/dist/index.js CHANGED
@@ -4526,6 +4526,117 @@ export async function run(argv) {
4526
4526
  registerArgusCommand(program);
4527
4527
  // v2.46.0 — NEMESIS (Anti-Identity-Lie Engine + EU AI Act Article 50)
4528
4528
  registerNemesisCommand(program);
4529
+ // v2.49.0 — B5 MULTI-ALIAS + F7 probe-coverage CLI + AUTO-ALIAS RESOLVER.
4530
+ // Closes wiring-lag-of-wiring-lag-fix: v2.48 shipped one verb; users
4531
+ // typed natural aliases (dev/detect/tool_detect) — all unknown. v2.49
4532
+ // wires every alias to the same handler.
4533
+ const devToolingDetect = async (path) => {
4534
+ try {
4535
+ const core = await import("@mneme-ai/core");
4536
+ const r = core.autoInit.detectDevTooling(path);
4537
+ process.stdout.write(JSON.stringify({ ok: true, path, result: r }, null, 2) + "\n");
4538
+ }
4539
+ catch (e) {
4540
+ process.stdout.write(JSON.stringify({ ok: false, error: e.message }) + "\n");
4541
+ process.exitCode = 1;
4542
+ }
4543
+ };
4544
+ // Aliases: `mneme detect`, `mneme tool_detect`, `mneme dev` (with no
4545
+ // sub) — all route to dev-tooling detect on current CWD.
4546
+ for (const alias of ["detect", "tool_detect"]) {
4547
+ program
4548
+ .command(alias)
4549
+ .description(`v2.49 alias for \`mneme dev_tooling detect\` — detect AI-dev scratch folder vs customer git repo.`)
4550
+ .option("--path <dir>", "Folder to check (default cwd).")
4551
+ .action(async (opts) => devToolingDetect(opts.path ?? process.cwd()));
4552
+ }
4553
+ // `mneme dev` parent — has `detect` subcommand AND default action.
4554
+ const devParent = program
4555
+ .command("dev")
4556
+ .description("v2.49 short alias for `mneme dev_tooling` — short-form access to DEV-TOOLING DETECTOR + RETROACTIVE CLEANSE.")
4557
+ .option("--path <dir>", "Folder to check (default cwd).")
4558
+ .action(async (opts) => devToolingDetect(opts.path ?? process.cwd()));
4559
+ devParent.command("detect")
4560
+ .description("Detect AI-dev folder.")
4561
+ .option("--path <dir>", "Folder.")
4562
+ .action(async (opts) => devToolingDetect(opts.path ?? process.cwd()));
4563
+ // v2.49.0 — F7 surface: `mneme release check` + `mneme probe coverage`.
4564
+ const releaseParent = program.command("release").description("v2.49 — release-time gates including MANDATORY probe-coverage check.");
4565
+ releaseParent.command("check")
4566
+ .description("Run the probe-coverage gate (refuses tag when new tool lacks TRUTH GATE probe binding).")
4567
+ .action(async () => {
4568
+ try {
4569
+ const core = await import("@mneme-ai/core");
4570
+ const r = core.releaseGate.crossCheckFromDisk(process.cwd());
4571
+ process.stdout.write(JSON.stringify(r, null, 2) + "\n");
4572
+ if (!r.ok)
4573
+ process.exitCode = 1;
4574
+ }
4575
+ catch (e) {
4576
+ process.stdout.write(JSON.stringify({ ok: false, error: e.message }) + "\n");
4577
+ process.exitCode = 1;
4578
+ }
4579
+ });
4580
+ const probeParent = program.command("probe").description("v2.49 — TRUTH GATE probe utilities.");
4581
+ probeParent.command("coverage")
4582
+ .description("Cross-check tool catalog vs claim catalog; report uncovered tools.")
4583
+ .action(async () => {
4584
+ try {
4585
+ const core = await import("@mneme-ai/core");
4586
+ const r = core.releaseGate.crossCheckFromDisk(process.cwd());
4587
+ process.stdout.write(JSON.stringify(r, null, 2) + "\n");
4588
+ if (!r.ok)
4589
+ process.exitCode = 1;
4590
+ }
4591
+ catch (e) {
4592
+ process.stdout.write(JSON.stringify({ ok: false, error: e.message }) + "\n");
4593
+ process.exitCode = 1;
4594
+ }
4595
+ });
4596
+ // v2.48.0 — Top-level `mneme dev_tooling` CLI (B5 fix: WIRING LAG class).
4597
+ // v2.45 shipped detectDevTooling() in core; v2.47 exposed it as MCP +
4598
+ // `mneme nemesis detect_tooling` subcommand — but users expect the
4599
+ // top-level verb. This adds `mneme dev_tooling [detect|cleanse]`.
4600
+ const dt = program.command("dev_tooling").description("v2.48 — Top-level surface for DEV-TOOLING DETECTOR (v2.45) + RETROACTIVE CLEANSE (v2.45). Closes WIRING LAG class: feature shipped in core but no top-level CLI verb.");
4601
+ dt.command("detect")
4602
+ .description("Detect whether CWD (or --path) is an AI-dev scratch folder vs customer git repo.")
4603
+ .option("--path <dir>", "Folder to check (default cwd).")
4604
+ .option("--json", "Force JSON output (default).")
4605
+ .action(async (opts) => {
4606
+ try {
4607
+ const core = await import("@mneme-ai/core");
4608
+ const path = opts.path ?? process.cwd();
4609
+ const r = core.autoInit.detectDevTooling(path);
4610
+ process.stdout.write(JSON.stringify({ ok: true, path, result: r }, null, 2) + "\n");
4611
+ }
4612
+ catch (e) {
4613
+ process.stdout.write(JSON.stringify({ ok: false, error: e.message }) + "\n");
4614
+ process.exitCode = 1;
4615
+ }
4616
+ });
4617
+ dt.command("cleanse")
4618
+ .description("Retroactive cleanse of AI-fingerprint files from git history. DRY-RUN default.")
4619
+ .option("--mode <m>", "scan | uncommit | filter-repo", "scan")
4620
+ .option("--execute", "Actually mutate (default dry-run).", false)
4621
+ .option("--confirm", "Required for filter-repo (destructive).", false)
4622
+ .action(async (opts) => {
4623
+ try {
4624
+ const core = await import("@mneme-ai/core");
4625
+ const r = core.cleanse({
4626
+ repoRoot: process.cwd(),
4627
+ mode: (opts.mode ?? "scan"),
4628
+ dryRun: !opts.execute,
4629
+ confirm: Boolean(opts.confirm),
4630
+ });
4631
+ process.stdout.write(JSON.stringify(r, null, 2) + "\n");
4632
+ if (!r.ok)
4633
+ process.exitCode = 1;
4634
+ }
4635
+ catch (e) {
4636
+ process.stdout.write(JSON.stringify({ ok: false, error: e.message }) + "\n");
4637
+ process.exitCode = 1;
4638
+ }
4639
+ });
4529
4640
  // v2.19.8 — UNIVERSAL MCP SUBCOMMAND AUTO-ROUTER
4530
4641
  // Reads the MCP tool catalog and auto-registers `mneme <family> <action>`
4531
4642
  // for every MCP tool. Closes the "no CLI route for shipped MCP tool" bug
@@ -4709,8 +4820,45 @@ export async function run(argv) {
4709
4820
  }
4710
4821
  }
4711
4822
  }
4712
- // Restore writeErr so the genuine error surfaces.
4823
+ // v2.49.0 AUTO-ALIAS RESOLVER. Before bailing with a cryptic
4824
+ // "unknown command" error, intercept the message + run Levenshtein
4825
+ // fuzzy match against all registered top-level verbs + print
4826
+ // suggestions. Closes the wiring-lag-at-keyboard-surface class.
4713
4827
  restoreWriteErr();
4828
+ const unknownMatch = /unknown command (?:'|")([^'"]+)(?:'|")/i.exec(message);
4829
+ if (unknownMatch && unknownMatch[1]) {
4830
+ const typed = unknownMatch[1];
4831
+ try {
4832
+ const { suggestCommands, printSuggestions, logMissedAlias } = await import("./alias_resolver.js");
4833
+ // Gather all registered top-level command names.
4834
+ const known = [];
4835
+ for (const c of program.commands) {
4836
+ const name = (c.name?.() ?? c._name);
4837
+ if (name)
4838
+ known.push(name);
4839
+ }
4840
+ const suggestions = suggestCommands(typed, known, { topN: 5 });
4841
+ const winner = printSuggestions(typed, suggestions);
4842
+ try {
4843
+ logMissedAlias(process.cwd(), typed);
4844
+ }
4845
+ catch { /* */ }
4846
+ // Optional auto-run via env var
4847
+ if (winner && process.env["MNEME_AUTO_ALIAS"] === "1") {
4848
+ process.stderr.write(`\n→ MNEME_AUTO_ALIAS=1 set — auto-running \`mneme ${winner}\`...\n\n`);
4849
+ const restArgs = argv.slice(argv.findIndex((a) => a === typed) + 1);
4850
+ try {
4851
+ await program.parseAsync(["node", "mneme", winner, ...restArgs]);
4852
+ process.exit(0);
4853
+ }
4854
+ catch {
4855
+ process.exit(1);
4856
+ }
4857
+ }
4858
+ process.exit(1);
4859
+ }
4860
+ catch { /* fall through to generic error */ }
4861
+ }
4714
4862
  ui.error(message);
4715
4863
  process.exit(1);
4716
4864
  }