memhook 0.2.2 → 0.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"memhook.js","sourceRoot":"","sources":["../../bin/memhook.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memhook.js","sourceRoot":"","sources":["../../bin/memhook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Zero-dependency ANSI styling for the interactive CLI commands
3
+ * (`memhook init`, `memhook tail`).
4
+ *
5
+ * This module is NOT used by the hook entrypoint (`memhook run`) — the hook's
6
+ * stdout is reserved for the JSON envelope and must stay byte-clean. Colour is
7
+ * only ever emitted to a human-facing TTY.
8
+ *
9
+ * Colour is disabled (every styler becomes the identity function) when:
10
+ * - `NO_COLOR` is set (any value) — https://no-color.org/
11
+ * - `MEMHOOK_NO_COLOR` is set (memhook-specific opt-out)
12
+ * - `TERM=dumb`
13
+ * - the target stream is not a TTY (piped to a file / another process)
14
+ * `FORCE_COLOR` (any value) overrides all of the above and forces colour on,
15
+ * which is what the test-suite and `| less -R` rely on.
16
+ */
17
+ export interface AnsiOptions {
18
+ /** Whether the destination is an interactive terminal. */
19
+ isTTY: boolean;
20
+ env: NodeJS.ProcessEnv;
21
+ }
22
+ /** Decide once whether colour should be emitted for a given stream + env. */
23
+ export declare function colorEnabled({ isTTY, env }: AnsiOptions): boolean;
24
+ /** SGR codes used by the CLI. Kept tiny on purpose. */
25
+ declare const CODES: {
26
+ readonly bold: 1;
27
+ readonly dim: 2;
28
+ readonly italic: 3;
29
+ readonly underline: 4;
30
+ readonly red: 31;
31
+ readonly green: 32;
32
+ readonly yellow: 33;
33
+ readonly blue: 34;
34
+ readonly magenta: 35;
35
+ readonly cyan: 36;
36
+ readonly white: 37;
37
+ readonly gray: 90;
38
+ readonly brightGreen: 92;
39
+ readonly brightCyan: 96;
40
+ };
41
+ export type AnsiColor = keyof typeof CODES;
42
+ export interface Ansi {
43
+ readonly enabled: boolean;
44
+ /** Wrap `s` in the SGR code for `name`, or return it untouched if disabled. */
45
+ style(name: AnsiColor, s: string): string;
46
+ bold(s: string): string;
47
+ dim(s: string): string;
48
+ red(s: string): string;
49
+ green(s: string): string;
50
+ yellow(s: string): string;
51
+ cyan(s: string): string;
52
+ gray(s: string): string;
53
+ }
54
+ /**
55
+ * Build an `Ansi` styler. When colour is disabled every method is the identity
56
+ * function, so call-sites never branch on `enabled` themselves.
57
+ */
58
+ export declare function makeAnsi(opts: AnsiOptions): Ansi;
59
+ /**
60
+ * Visible width of a string with ANSI escapes stripped — used to pad/align
61
+ * columns correctly even when the cell contains colour codes.
62
+ */
63
+ export declare function visibleWidth(s: string): number;
64
+ /** Left-pad `s` to `width` visible columns (right-align). */
65
+ export declare function padStart(s: string, width: number): string;
66
+ /** Right-pad `s` to `width` visible columns (left-align). */
67
+ export declare function padEnd(s: string, width: number): string;
68
+ /** Truncate to `max` chars, appending `…` when cut. Callers pass plain text. */
69
+ export declare function truncate(s: string, max: number): string;
70
+ export {};
71
+ //# sourceMappingURL=ansi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.d.ts","sourceRoot":"","sources":["../../src/ansi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,MAAM,WAAW,WAAW;IAC1B,0DAA0D;IAC1D,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACxB;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,WAAW,GAAG,OAAO,CAMjE;AAED,uDAAuD;AACvD,QAAA,MAAM,KAAK;;;;;;;;;;;;;;;CAeD,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,KAAK,CAAC;AAE3C,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,+EAA+E;IAC/E,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAE1C,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAehD;AAKD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,6DAA6D;AAC7D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED,6DAA6D;AAC7D,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,gFAAgF;AAChF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIvD"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Zero-dependency ANSI styling for the interactive CLI commands
3
+ * (`memhook init`, `memhook tail`).
4
+ *
5
+ * This module is NOT used by the hook entrypoint (`memhook run`) — the hook's
6
+ * stdout is reserved for the JSON envelope and must stay byte-clean. Colour is
7
+ * only ever emitted to a human-facing TTY.
8
+ *
9
+ * Colour is disabled (every styler becomes the identity function) when:
10
+ * - `NO_COLOR` is set (any value) — https://no-color.org/
11
+ * - `MEMHOOK_NO_COLOR` is set (memhook-specific opt-out)
12
+ * - `TERM=dumb`
13
+ * - the target stream is not a TTY (piped to a file / another process)
14
+ * `FORCE_COLOR` (any value) overrides all of the above and forces colour on,
15
+ * which is what the test-suite and `| less -R` rely on.
16
+ */
17
+ /**
18
+ * The ANSI escape introducer (0x1b). Built from a char code so the source file
19
+ * stays printable ASCII — no raw control byte to be mangled by an editor,
20
+ * `git`, or a copy/paste.
21
+ */
22
+ const ESC = String.fromCharCode(27);
23
+ const CSI = `${ESC}[`;
24
+ const RESET = `${CSI}0m`;
25
+ /** Decide once whether colour should be emitted for a given stream + env. */
26
+ export function colorEnabled({ isTTY, env }) {
27
+ if (env["FORCE_COLOR"] !== undefined && env["FORCE_COLOR"] !== "")
28
+ return true;
29
+ if (env["NO_COLOR"] !== undefined)
30
+ return false;
31
+ if (env["MEMHOOK_NO_COLOR"] !== undefined)
32
+ return false;
33
+ if (env["TERM"] === "dumb")
34
+ return false;
35
+ return isTTY;
36
+ }
37
+ /** SGR codes used by the CLI. Kept tiny on purpose. */
38
+ const CODES = {
39
+ bold: 1,
40
+ dim: 2,
41
+ italic: 3,
42
+ underline: 4,
43
+ red: 31,
44
+ green: 32,
45
+ yellow: 33,
46
+ blue: 34,
47
+ magenta: 35,
48
+ cyan: 36,
49
+ white: 37,
50
+ gray: 90,
51
+ brightGreen: 92,
52
+ brightCyan: 96,
53
+ };
54
+ /**
55
+ * Build an `Ansi` styler. When colour is disabled every method is the identity
56
+ * function, so call-sites never branch on `enabled` themselves.
57
+ */
58
+ export function makeAnsi(opts) {
59
+ const enabled = colorEnabled(opts);
60
+ const wrap = (name, s) => enabled ? `${CSI}${CODES[name]}m${s}${RESET}` : s;
61
+ return {
62
+ enabled,
63
+ style: wrap,
64
+ bold: (s) => wrap("bold", s),
65
+ dim: (s) => wrap("dim", s),
66
+ red: (s) => wrap("red", s),
67
+ green: (s) => wrap("green", s),
68
+ yellow: (s) => wrap("yellow", s),
69
+ cyan: (s) => wrap("cyan", s),
70
+ gray: (s) => wrap("gray", s),
71
+ };
72
+ }
73
+ /** Matches any SGR escape sequence, e.g. "ESC[32m" or "ESC[0m". */
74
+ const SGR_RE = new RegExp(`${ESC}\\[[0-9;]*m`, "g");
75
+ /**
76
+ * Visible width of a string with ANSI escapes stripped — used to pad/align
77
+ * columns correctly even when the cell contains colour codes.
78
+ */
79
+ export function visibleWidth(s) {
80
+ return s.replace(SGR_RE, "").length;
81
+ }
82
+ /** Left-pad `s` to `width` visible columns (right-align). */
83
+ export function padStart(s, width) {
84
+ const gap = width - visibleWidth(s);
85
+ return gap > 0 ? " ".repeat(gap) + s : s;
86
+ }
87
+ /** Right-pad `s` to `width` visible columns (left-align). */
88
+ export function padEnd(s, width) {
89
+ const gap = width - visibleWidth(s);
90
+ return gap > 0 ? s + " ".repeat(gap) : s;
91
+ }
92
+ /** Truncate to `max` chars, appending `…` when cut. Callers pass plain text. */
93
+ export function truncate(s, max) {
94
+ if (s.length <= max)
95
+ return s;
96
+ if (max <= 1)
97
+ return s.slice(0, max);
98
+ return s.slice(0, max - 1) + "…";
99
+ }
100
+ //# sourceMappingURL=ansi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.js","sourceRoot":"","sources":["../../src/ansi.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAFZ,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM;IAKvC,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM;IAKjC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAiB/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKrC,UAAU,IAAI,MAAM;CAwBrB"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAgBH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,UAAU;IAEnB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY;gBAFZ,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM;IAKvC,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM;IAKjC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAuB/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAKrC,UAAU,IAAI,MAAM;CAwBrB"}
package/dist/src/cache.js CHANGED
@@ -10,7 +10,7 @@
10
10
  * filesystem mtime check.
11
11
  */
12
12
  import { createHash } from "node:crypto";
13
- import { mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
13
+ import { closeSync, fstatSync, mkdirSync, openSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
14
14
  import { join } from "node:path";
15
15
  export class LocalCache {
16
16
  dir;
@@ -28,22 +28,29 @@ export class LocalCache {
28
28
  }
29
29
  get(key) {
30
30
  const file = join(this.dir, `${key}.json`);
31
- let stat;
31
+ // Open once, then `fstat` (for the TTL check) and read from the SAME fd.
32
+ // A single handle — rather than statSync-then-readFileSync on the path —
33
+ // closes a check-then-use window (CodeQL js/file-system-race). Any failure
34
+ // (missing, unreadable, vanished mid-read) is treated as a cache miss.
35
+ let fd;
32
36
  try {
33
- stat = statSync(file);
37
+ fd = openSync(file, "r");
34
38
  }
35
39
  catch {
36
40
  return null;
37
41
  }
38
- const ageMs = Date.now() - stat.mtimeMs;
39
- if (ageMs > this.ttlMin * 60_000)
40
- return null;
41
42
  try {
42
- return readFileSync(file, "utf8");
43
+ const ageMs = Date.now() - fstatSync(fd).mtimeMs;
44
+ if (ageMs > this.ttlMin * 60_000)
45
+ return null;
46
+ return readFileSync(fd, "utf8");
43
47
  }
44
48
  catch {
45
49
  return null;
46
50
  }
51
+ finally {
52
+ closeSync(fd);
53
+ }
47
54
  }
48
55
  put(key, value) {
49
56
  const file = join(this.dir, `${key}.json`);
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,10 @@ export { LocalCache, type CacheKeyInput } from "./cache.js";
13
13
  export { PreFilter } from "./preFilter.js";
14
14
  export { MEMHOOK_VERSION } from "./version.js";
15
15
  export { createProvider } from "./providers/factory.js";
16
+ export { addHooks, removeHooks, memhookSubcommand, MEMHOOK_HOOKS, type Settings, type HookEvent, type AddResult, type RemoveResult, } from "./install.js";
17
+ export { runInit, runUninstall, buildConfigObject, backupPath, type InitOptions, type UninstallOptions, } from "./init.js";
18
+ export { runTail, parseLogLine, formatRow, formatHeader, formatFooter, summarize, emptyStats, accumulate, tailLines, type LogRow, type Stats, type TailOptions, } from "./tail.js";
19
+ export { makeAnsi, colorEnabled, visibleWidth, type Ansi, type AnsiOptions } from "./ansi.js";
16
20
  export { AnthropicProvider, type AnthropicProviderOptions } from "./providers/anthropic.js";
17
21
  export { OpenAIProvider } from "./providers/openai.js";
18
22
  export { OllamaProvider } from "./providers/ollama.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxF,OAAO,EAAE,KAAK,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EACV,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,GACf,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxF,OAAO,EAAE,KAAK,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EACL,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,OAAO,EACP,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,gBAAgB,GACtB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,OAAO,EACP,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,EACT,KAAK,MAAM,EACX,KAAK,KAAK,EACV,KAAK,WAAW,GACjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,YAAY,EACV,QAAQ,EACR,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,GACf,MAAM,sBAAsB,CAAC"}
package/dist/src/index.js CHANGED
@@ -13,6 +13,10 @@ export { LocalCache } from "./cache.js";
13
13
  export { PreFilter } from "./preFilter.js";
14
14
  export { MEMHOOK_VERSION } from "./version.js";
15
15
  export { createProvider } from "./providers/factory.js";
16
+ export { addHooks, removeHooks, memhookSubcommand, MEMHOOK_HOOKS, } from "./install.js";
17
+ export { runInit, runUninstall, buildConfigObject, backupPath, } from "./init.js";
18
+ export { runTail, parseLogLine, formatRow, formatHeader, formatFooter, summarize, emptyStats, accumulate, tailLines, } from "./tail.js";
19
+ export { makeAnsi, colorEnabled, visibleWidth } from "./ansi.js";
16
20
  export { AnthropicProvider } from "./providers/anthropic.js";
17
21
  export { OpenAIProvider } from "./providers/openai.js";
18
22
  export { OllamaProvider } from "./providers/ollama.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAyC,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAsB,MAAM,iBAAiB,CAAC;AACxF,OAAO,EAAE,KAAK,EAAmC,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,YAAY,EAA4B,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,UAAU,EAAsB,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAiC,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAyC,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAsB,MAAM,iBAAiB,CAAC;AACxF,OAAO,EAAE,KAAK,EAAmC,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,YAAY,EAA4B,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,UAAU,EAAsB,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EACL,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,aAAa,GAKd,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,OAAO,EACP,YAAY,EACZ,iBAAiB,EACjB,UAAU,GAGX,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,OAAO,EACP,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,SAAS,EACT,UAAU,EACV,UAAU,EACV,SAAS,GAIV,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAA+B,MAAM,WAAW,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAiC,MAAM,0BAA0B,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * `memhook init` / `memhook uninstall` orchestration.
3
+ *
4
+ * These are INTERACTIVE, user-invoked commands — NOT the hook entrypoint. They
5
+ * are allowed to prompt, own the TTY, print to stdout, and exit non-zero on
6
+ * user error (docs/SPECIFICATION.md §9: "memhook run is the only command that
7
+ * must obey the fail-soft contract"). The one hard rule: they must never
8
+ * corrupt `~/.claude/settings.json`. So:
9
+ * - the merge itself is pure + unit-tested (src/install.ts),
10
+ * - an unparseable settings file aborts rather than being overwritten,
11
+ * - every write is preceded by a timestamped backup,
12
+ * - `--dry-run` prints the plan and writes nothing.
13
+ *
14
+ * All file I/O lives here; `install.ts` stays pure.
15
+ */
16
+ import { type ProviderType } from "./config.js";
17
+ export interface InitOptions {
18
+ yes: boolean;
19
+ dryRun: boolean;
20
+ provider?: ProviderType | undefined;
21
+ apiKeyEnv?: string | undefined;
22
+ model?: string | undefined;
23
+ bin: string;
24
+ settingsPath?: string | undefined;
25
+ noCatalog?: boolean | undefined;
26
+ }
27
+ export interface UninstallOptions {
28
+ yes: boolean;
29
+ dryRun: boolean;
30
+ settingsPath?: string | undefined;
31
+ purge?: boolean | undefined;
32
+ }
33
+ /** A backup path next to `path`, stamped so successive runs never collide. */
34
+ export declare function backupPath(path: string, stamp: string): string;
35
+ /**
36
+ * Build the minimal YAML config object for the chosen provider — only keys that
37
+ * differ from the built-in defaults are emitted, so the file stays small and
38
+ * the anthropic-default install writes no config at all.
39
+ */
40
+ export declare function buildConfigObject(opts: {
41
+ provider: ProviderType;
42
+ model?: string | undefined;
43
+ apiKeyEnv?: string | undefined;
44
+ }): Record<string, unknown> | null;
45
+ export declare function runInit(opts: InitOptions, env?: NodeJS.ProcessEnv): Promise<number>;
46
+ export declare function runUninstall(opts: UninstallOptions, env?: NodeJS.ProcessEnv): Promise<number>;
47
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAU5D,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,YAAY,GAAG,SAAS,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9D;AAiBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAQjC;AAmDD,wBAAsB,OAAO,CAC3B,IAAI,EAAE,WAAW,EACjB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,MAAM,CAAC,CAqIjB;AAID,wBAAsB,YAAY,CAChC,IAAI,EAAE,gBAAgB,EACtB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,OAAO,CAAC,MAAM,CAAC,CA+DjB"}
@@ -0,0 +1,283 @@
1
+ /**
2
+ * `memhook init` / `memhook uninstall` orchestration.
3
+ *
4
+ * These are INTERACTIVE, user-invoked commands — NOT the hook entrypoint. They
5
+ * are allowed to prompt, own the TTY, print to stdout, and exit non-zero on
6
+ * user error (docs/SPECIFICATION.md §9: "memhook run is the only command that
7
+ * must obey the fail-soft contract"). The one hard rule: they must never
8
+ * corrupt `~/.claude/settings.json`. So:
9
+ * - the merge itself is pure + unit-tested (src/install.ts),
10
+ * - an unparseable settings file aborts rather than being overwritten,
11
+ * - every write is preceded by a timestamped backup,
12
+ * - `--dry-run` prints the plan and writes nothing.
13
+ *
14
+ * All file I/O lives here; `install.ts` stays pure.
15
+ */
16
+ import { copyFileSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17
+ import { homedir } from "node:os";
18
+ import { dirname, join } from "node:path";
19
+ import { createInterface } from "node:readline/promises";
20
+ import { stringify as yamlStringify } from "yaml";
21
+ import { addHooks, removeHooks } from "./install.js";
22
+ import { buildCatalog } from "./catalog.js";
23
+ import { loadConfig } from "./config.js";
24
+ import { makeAnsi } from "./ansi.js";
25
+ const PROVIDERS = ["anthropic", "openai", "ollama"];
26
+ const DEFAULT_KEY_ENV = {
27
+ anthropic: "ANTHROPIC_API_KEY",
28
+ openai: "OPENAI_API_KEY",
29
+ ollama: undefined,
30
+ };
31
+ /** A backup path next to `path`, stamped so successive runs never collide. */
32
+ export function backupPath(path, stamp) {
33
+ return `${path}.bak-${stamp}`;
34
+ }
35
+ function stampNow() {
36
+ return new Date()
37
+ .toISOString()
38
+ .replace(/[:.]/g, "-")
39
+ .replace(/-(\d{3})Z$/, "Z");
40
+ }
41
+ function defaultSettingsPath() {
42
+ return join(homedir(), ".claude", "settings.json");
43
+ }
44
+ function configYamlPath() {
45
+ return join(homedir(), ".config", "memhook", "config.yaml");
46
+ }
47
+ /**
48
+ * Build the minimal YAML config object for the chosen provider — only keys that
49
+ * differ from the built-in defaults are emitted, so the file stays small and
50
+ * the anthropic-default install writes no config at all.
51
+ */
52
+ export function buildConfigObject(opts) {
53
+ const provider = {};
54
+ if (opts.provider !== "anthropic")
55
+ provider["type"] = opts.provider;
56
+ if (opts.model)
57
+ provider["model"] = opts.model;
58
+ if (opts.apiKeyEnv && opts.apiKeyEnv !== DEFAULT_KEY_ENV[opts.provider]) {
59
+ provider["apiKeyEnv"] = opts.apiKeyEnv;
60
+ }
61
+ return Object.keys(provider).length > 0 ? { provider } : null;
62
+ }
63
+ function makeIo(env) {
64
+ const ansi = makeAnsi({ isTTY: Boolean(process.stdout.isTTY), env });
65
+ return { out: (s) => process.stdout.write(s + "\n"), ansi };
66
+ }
67
+ /**
68
+ * Read + JSON-parse settings; returns `{}` for a missing/empty file, throws for
69
+ * invalid JSON. Reads-then-handles-ENOENT rather than checking existence first,
70
+ * which avoids a check-then-use race (CodeQL js/file-system-race).
71
+ */
72
+ function readSettings(path) {
73
+ let text;
74
+ try {
75
+ text = readFileSync(path, "utf8");
76
+ }
77
+ catch (err) {
78
+ if (err.code === "ENOENT")
79
+ return {};
80
+ throw err;
81
+ }
82
+ if (text.trim() === "")
83
+ return {};
84
+ return JSON.parse(text);
85
+ }
86
+ function writeJson(path, value) {
87
+ mkdirSync(dirname(path), { recursive: true });
88
+ writeFileSync(path, JSON.stringify(value, null, 2) + "\n", "utf8");
89
+ }
90
+ /**
91
+ * Copy `path` to its timestamped backup; returns false when there was nothing
92
+ * to back up (file absent). Copy-then-handle-ENOENT — not an `existsSync`
93
+ * pre-check — avoids a check-then-use race and is robust if the file vanishes.
94
+ */
95
+ function backupFile(path, stamp) {
96
+ try {
97
+ copyFileSync(path, backupPath(path, stamp));
98
+ return true;
99
+ }
100
+ catch (err) {
101
+ if (err.code === "ENOENT")
102
+ return false;
103
+ throw err;
104
+ }
105
+ }
106
+ // ── memhook init ──────────────────────────────────────────────────────────
107
+ export async function runInit(opts, env = process.env) {
108
+ const io = makeIo(env);
109
+ const { ansi } = io;
110
+ const settingsPath = opts.settingsPath ?? defaultSettingsPath();
111
+ const interactive = !opts.yes && Boolean(process.stdin.isTTY) && !opts.dryRun;
112
+ io.out(ansi.bold("memhook init") + ansi.dim(" — wire memhook into Claude Code\n"));
113
+ // 1. Provider / key / model — flags win, then prompts, then defaults.
114
+ let provider = opts.provider ?? "anthropic";
115
+ const model = opts.model;
116
+ let apiKeyEnv = opts.apiKeyEnv;
117
+ if (interactive) {
118
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
119
+ try {
120
+ const p = (await rl.question(`Provider ${ansi.dim("[anthropic]")} / openai / ollama: `))
121
+ .trim()
122
+ .toLowerCase();
123
+ if (p && PROVIDERS.includes(p))
124
+ provider = p;
125
+ if (provider !== "ollama") {
126
+ const defKey = DEFAULT_KEY_ENV[provider];
127
+ const k = (await rl.question(`API key env var ${ansi.dim(`[${defKey}]`)}: `)).trim();
128
+ apiKeyEnv = k || apiKeyEnv || defKey;
129
+ }
130
+ }
131
+ finally {
132
+ rl.close();
133
+ }
134
+ }
135
+ else {
136
+ apiKeyEnv = apiKeyEnv ?? DEFAULT_KEY_ENV[provider];
137
+ }
138
+ // 2. Compute the settings.json merge (pure).
139
+ let existing;
140
+ try {
141
+ existing = readSettings(settingsPath);
142
+ }
143
+ catch {
144
+ io.out(ansi.red("✗ ") +
145
+ `${settingsPath} is not valid JSON. Refusing to overwrite it.\n` +
146
+ ansi.dim(" Fix or move the file, then re-run `memhook init`."));
147
+ return 1;
148
+ }
149
+ const merge = addHooks(existing, opts.bin);
150
+ const configObj = buildConfigObject({ provider, model, apiKeyEnv });
151
+ // 3. Plan summary.
152
+ io.out(ansi.bold("\nPlan"));
153
+ if (merge.added.length > 0) {
154
+ io.out(` ${ansi.green("+")} hook ${merge.added.join(" + ")} → ${settingsPath}`);
155
+ io.out(` ${ansi.dim(`backup → ${backupPath(settingsPath, "<timestamp>")}`)}`);
156
+ }
157
+ for (const ev of merge.alreadyPresent) {
158
+ io.out(` ${ansi.dim("·")} hook ${ev} already wired ${ansi.dim("(skip)")}`);
159
+ }
160
+ if (configObj) {
161
+ io.out(` ${ansi.green("+")} config → ${configYamlPath()} ${ansi.dim(`(provider: ${provider})`)}`);
162
+ }
163
+ else {
164
+ io.out(` ${ansi.dim("·")} provider anthropic (default) ${ansi.dim("— no config file needed")}`);
165
+ }
166
+ if (!opts.noCatalog)
167
+ io.out(` ${ansi.green("+")} build catalog`);
168
+ // 4. API-key heads-up (never blocks; just warns).
169
+ if (provider !== "ollama" && apiKeyEnv && !env[apiKeyEnv]) {
170
+ io.out(`\n ${ansi.yellow("!")} ${apiKeyEnv} is not set in this shell — ` +
171
+ ansi.dim(`export it before memhook can route (the hook fails soft until then).`));
172
+ }
173
+ if (opts.dryRun) {
174
+ io.out(ansi.dim("\n(dry run — nothing written)"));
175
+ return 0;
176
+ }
177
+ if (interactive) {
178
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
179
+ try {
180
+ const go = (await rl.question(`\n${ansi.bold("Proceed?")} ${ansi.dim("[Y/n]")} `))
181
+ .trim()
182
+ .toLowerCase();
183
+ if (go === "n" || go === "no") {
184
+ io.out(ansi.dim("Aborted. Nothing written."));
185
+ return 0;
186
+ }
187
+ }
188
+ finally {
189
+ rl.close();
190
+ }
191
+ }
192
+ // 5. Write (settings first, with backup).
193
+ const stamp = stampNow();
194
+ if (merge.added.length > 0) {
195
+ backupFile(settingsPath, stamp);
196
+ writeJson(settingsPath, merge.settings);
197
+ io.out(`${ansi.green("✓")} wired ${merge.added.join(" + ")} into ${settingsPath}`);
198
+ }
199
+ if (configObj) {
200
+ const cfgPath = configYamlPath();
201
+ backupFile(cfgPath, stamp);
202
+ mkdirSync(dirname(cfgPath), { recursive: true });
203
+ writeFileSync(cfgPath, yamlStringify(configObj), "utf8");
204
+ io.out(`${ansi.green("✓")} wrote ${cfgPath}`);
205
+ }
206
+ // 6. Bootstrap memory dirs so build-catalog + the router have somewhere to look.
207
+ for (const d of [join(homedir(), ".claude", "rules"), join(homedir(), ".claude", "projects")]) {
208
+ mkdirSync(d, { recursive: true });
209
+ }
210
+ // 7. Seed the catalog.
211
+ if (!opts.noCatalog) {
212
+ try {
213
+ const config = loadConfig(env);
214
+ const res = buildCatalog({ cwd: process.cwd(), outputPath: config.catalog.path });
215
+ io.out(`${ansi.green("✓")} catalog ${config.catalog.path} ${ansi.dim(`(${res.lines}L)`)}`);
216
+ }
217
+ catch {
218
+ io.out(ansi.yellow("! ") + "catalog build skipped (run `memhook build-catalog` later)");
219
+ }
220
+ }
221
+ io.out(`\n${ansi.green("Done.")} Restart Claude Code, then watch it live with ` +
222
+ ansi.bold("memhook tail") +
223
+ ".");
224
+ return 0;
225
+ }
226
+ // ── memhook uninstall ───────────────────────────────────────────────────────
227
+ export async function runUninstall(opts, env = process.env) {
228
+ const io = makeIo(env);
229
+ const { ansi } = io;
230
+ const settingsPath = opts.settingsPath ?? defaultSettingsPath();
231
+ const interactive = !opts.yes && Boolean(process.stdin.isTTY) && !opts.dryRun;
232
+ io.out(ansi.bold("memhook uninstall") + ansi.dim(" — remove memhook hooks\n"));
233
+ let existing;
234
+ try {
235
+ existing = readSettings(settingsPath);
236
+ }
237
+ catch {
238
+ io.out(ansi.red("✗ ") + `${settingsPath} is not valid JSON. Refusing to touch it.`);
239
+ return 1;
240
+ }
241
+ const result = removeHooks(existing);
242
+ if (result.removed === 0) {
243
+ io.out(ansi.dim("No memhook hooks found. Nothing to do."));
244
+ return 0;
245
+ }
246
+ io.out(ansi.bold("Plan"));
247
+ io.out(` ${ansi.red("-")} ${result.removed} memhook hook(s) from ${result.removedEvents.join(" + ")}`);
248
+ io.out(` ${ansi.dim(`backup → ${backupPath(settingsPath, "<timestamp>")}`)}`);
249
+ if (opts.purge)
250
+ io.out(` ${ansi.red("-")} purge cache + log`);
251
+ if (opts.dryRun) {
252
+ io.out(ansi.dim("\n(dry run — nothing written)"));
253
+ return 0;
254
+ }
255
+ if (interactive) {
256
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
257
+ try {
258
+ const go = (await rl.question(`\n${ansi.bold("Proceed?")} ${ansi.dim("[y/N]")} `))
259
+ .trim()
260
+ .toLowerCase();
261
+ if (go !== "y" && go !== "yes") {
262
+ io.out(ansi.dim("Aborted. Nothing written."));
263
+ return 0;
264
+ }
265
+ }
266
+ finally {
267
+ rl.close();
268
+ }
269
+ }
270
+ const stamp = stampNow();
271
+ backupFile(settingsPath, stamp);
272
+ writeJson(settingsPath, result.settings);
273
+ io.out(`${ansi.green("✓")} removed ${result.removed} hook(s) from ${settingsPath}`);
274
+ if (opts.purge) {
275
+ const config = loadConfig(env);
276
+ for (const target of [config.cache.dir, config.logging.jsonlPath]) {
277
+ io.out(ansi.dim(` (left in place: ${target} — remove manually if desired)`));
278
+ }
279
+ }
280
+ io.out(`\n${ansi.green("Done.")} Restart Claude Code to drop the hooks.`);
281
+ return 0;
282
+ }
283
+ //# sourceMappingURL=init.js.map