@memoire-ai/collector 0.1.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 (97) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-test.log +45 -0
  3. package/cursor-hooks/README.md +119 -0
  4. package/cursor-hooks/context-inject.sh +118 -0
  5. package/cursor-hooks/hooks.json +39 -0
  6. package/cursor-hooks/save-file-edit.sh +130 -0
  7. package/cursor-hooks/save-observation.sh +116 -0
  8. package/cursor-hooks/save-shell-execution.sh +121 -0
  9. package/cursor-hooks/session-summary.sh +142 -0
  10. package/dist/capture.d.ts +111 -0
  11. package/dist/capture.d.ts.map +1 -0
  12. package/dist/capture.integration.d.ts +2 -0
  13. package/dist/capture.integration.d.ts.map +1 -0
  14. package/dist/capture.integration.js +67 -0
  15. package/dist/capture.integration.js.map +1 -0
  16. package/dist/capture.js +264 -0
  17. package/dist/capture.js.map +1 -0
  18. package/dist/client-summarizer.d.ts +59 -0
  19. package/dist/client-summarizer.d.ts.map +1 -0
  20. package/dist/client-summarizer.js +211 -0
  21. package/dist/client-summarizer.js.map +1 -0
  22. package/dist/client-summarizer.test.d.ts +2 -0
  23. package/dist/client-summarizer.test.d.ts.map +1 -0
  24. package/dist/client-summarizer.test.js +127 -0
  25. package/dist/client-summarizer.test.js.map +1 -0
  26. package/dist/config.d.ts +13 -0
  27. package/dist/config.d.ts.map +1 -0
  28. package/dist/config.js +131 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/config.test.d.ts +2 -0
  31. package/dist/config.test.d.ts.map +1 -0
  32. package/dist/config.test.js +182 -0
  33. package/dist/config.test.js.map +1 -0
  34. package/dist/cursor-hooks.d.ts +46 -0
  35. package/dist/cursor-hooks.d.ts.map +1 -0
  36. package/dist/cursor-hooks.js +251 -0
  37. package/dist/cursor-hooks.js.map +1 -0
  38. package/dist/cursor-rules.d.ts +42 -0
  39. package/dist/cursor-rules.d.ts.map +1 -0
  40. package/dist/cursor-rules.js +229 -0
  41. package/dist/cursor-rules.js.map +1 -0
  42. package/dist/cursor-rules.test.d.ts +2 -0
  43. package/dist/cursor-rules.test.d.ts.map +1 -0
  44. package/dist/cursor-rules.test.js +55 -0
  45. package/dist/cursor-rules.test.js.map +1 -0
  46. package/dist/dedup.d.ts +22 -0
  47. package/dist/dedup.d.ts.map +1 -0
  48. package/dist/dedup.js +60 -0
  49. package/dist/dedup.js.map +1 -0
  50. package/dist/dedup.test.d.ts +2 -0
  51. package/dist/dedup.test.d.ts.map +1 -0
  52. package/dist/dedup.test.js +83 -0
  53. package/dist/dedup.test.js.map +1 -0
  54. package/dist/hooks/index.d.ts +52 -0
  55. package/dist/hooks/index.d.ts.map +1 -0
  56. package/dist/hooks/index.js +136 -0
  57. package/dist/hooks/index.js.map +1 -0
  58. package/dist/hooks.test.d.ts +2 -0
  59. package/dist/hooks.test.d.ts.map +1 -0
  60. package/dist/hooks.test.js +94 -0
  61. package/dist/hooks.test.js.map +1 -0
  62. package/dist/index.d.ts +9 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +9 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/strip-private.d.ts +12 -0
  67. package/dist/strip-private.d.ts.map +1 -0
  68. package/dist/strip-private.js +28 -0
  69. package/dist/strip-private.js.map +1 -0
  70. package/dist/strip-private.test.d.ts +2 -0
  71. package/dist/strip-private.test.d.ts.map +1 -0
  72. package/dist/strip-private.test.js +37 -0
  73. package/dist/strip-private.test.js.map +1 -0
  74. package/dist/utils.d.ts +3 -0
  75. package/dist/utils.d.ts.map +1 -0
  76. package/dist/utils.js +11 -0
  77. package/dist/utils.js.map +1 -0
  78. package/package.json +28 -0
  79. package/src/capture.integration.ts +98 -0
  80. package/src/capture.ts +352 -0
  81. package/src/client-summarizer.test.ts +144 -0
  82. package/src/client-summarizer.ts +338 -0
  83. package/src/config.test.ts +211 -0
  84. package/src/config.ts +157 -0
  85. package/src/cursor-hooks.ts +309 -0
  86. package/src/cursor-rules.test.ts +63 -0
  87. package/src/cursor-rules.ts +313 -0
  88. package/src/dedup.test.ts +84 -0
  89. package/src/dedup.ts +67 -0
  90. package/src/hooks/index.ts +226 -0
  91. package/src/hooks.test.ts +111 -0
  92. package/src/index.ts +32 -0
  93. package/src/strip-private.test.ts +57 -0
  94. package/src/strip-private.ts +34 -0
  95. package/src/utils.ts +10 -0
  96. package/tsconfig.json +12 -0
  97. package/tsconfig.tsbuildinfo +1 -0
package/dist/dedup.js ADDED
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Client-side content deduplication using a hash-based sliding window.
3
+ * Prevents noisy hooks from flooding the API with duplicate observations
4
+ * within a short time window (default 30 seconds).
5
+ *
6
+ * Mirrors claude-mem's 30-second content-hash dedup at the edge.
7
+ */
8
+ /** Simple FNV-1a hash for fast string fingerprinting */
9
+ function fnv1a(str) {
10
+ let hash = 0x811c9dc5;
11
+ for (let i = 0; i < str.length; i++) {
12
+ hash ^= str.charCodeAt(i);
13
+ hash = (hash * 0x01000193) >>> 0;
14
+ }
15
+ return hash;
16
+ }
17
+ export class ContentDedup {
18
+ seen = new Map(); // hash → timestamp
19
+ windowMs;
20
+ cleanupTimer;
21
+ constructor(windowMs = 30_000) {
22
+ this.windowMs = windowMs;
23
+ // Periodic cleanup to prevent unbounded growth
24
+ this.cleanupTimer = setInterval(() => this.evict(), this.windowMs);
25
+ if (this.cleanupTimer.unref) {
26
+ this.cleanupTimer.unref(); // Don't keep process alive
27
+ }
28
+ }
29
+ /**
30
+ * Returns true if content is a duplicate (already seen within the window).
31
+ * Returns false if content is new — and records it.
32
+ */
33
+ isDuplicate(content) {
34
+ const hash = fnv1a(content);
35
+ const now = Date.now();
36
+ const lastSeen = this.seen.get(hash);
37
+ if (lastSeen !== undefined && now - lastSeen < this.windowMs) {
38
+ return true;
39
+ }
40
+ this.seen.set(hash, now);
41
+ return false;
42
+ }
43
+ /** Remove entries older than the window */
44
+ evict() {
45
+ const cutoff = Date.now() - this.windowMs;
46
+ for (const [hash, timestamp] of this.seen) {
47
+ if (timestamp < cutoff) {
48
+ this.seen.delete(hash);
49
+ }
50
+ }
51
+ }
52
+ destroy() {
53
+ if (this.cleanupTimer) {
54
+ clearInterval(this.cleanupTimer);
55
+ this.cleanupTimer = undefined;
56
+ }
57
+ this.seen.clear();
58
+ }
59
+ }
60
+ //# sourceMappingURL=dedup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedup.js","sourceRoot":"","sources":["../src/dedup.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wDAAwD;AACxD,SAAS,KAAK,CAAC,GAAW;IACxB,IAAI,IAAI,GAAG,UAAU,CAAC;IACtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,IAAI,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,GAAG,CAAC,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,OAAO,YAAY;IACf,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,mBAAmB;IACrD,QAAQ,CAAS;IACjB,YAAY,CAA6C;IAEjE,YAAY,QAAQ,GAAG,MAAM;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,+CAA+C;QAC/C,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnE,IAAI,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;YAC5B,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,2BAA2B;QACxD,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,OAAe;QACzB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAErC,IAAI,QAAQ,KAAK,SAAS,IAAI,GAAG,GAAG,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,2CAA2C;IACnC,KAAK;QACX,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC1C,IAAI,SAAS,GAAG,MAAM,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACjC,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;QAChC,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dedup.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedup.test.d.ts","sourceRoot":"","sources":["../src/dedup.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,83 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { ContentDedup } from './dedup.js';
4
+ test('ContentDedup allows first occurrence', () => {
5
+ const dedup = new ContentDedup(30_000);
6
+ try {
7
+ assert.equal(dedup.isDuplicate('hello world'), false);
8
+ }
9
+ finally {
10
+ dedup.destroy();
11
+ }
12
+ });
13
+ test('ContentDedup blocks duplicate within window', () => {
14
+ const dedup = new ContentDedup(30_000);
15
+ try {
16
+ assert.equal(dedup.isDuplicate('same content'), false);
17
+ assert.equal(dedup.isDuplicate('same content'), true);
18
+ assert.equal(dedup.isDuplicate('same content'), true);
19
+ }
20
+ finally {
21
+ dedup.destroy();
22
+ }
23
+ });
24
+ test('ContentDedup allows different content', () => {
25
+ const dedup = new ContentDedup(30_000);
26
+ try {
27
+ assert.equal(dedup.isDuplicate('content A'), false);
28
+ assert.equal(dedup.isDuplicate('content B'), false);
29
+ assert.equal(dedup.isDuplicate('content C'), false);
30
+ }
31
+ finally {
32
+ dedup.destroy();
33
+ }
34
+ });
35
+ test('ContentDedup allows content after window expires', async () => {
36
+ const dedup = new ContentDedup(50); // 50ms window for testing
37
+ try {
38
+ assert.equal(dedup.isDuplicate('expiring'), false);
39
+ assert.equal(dedup.isDuplicate('expiring'), true);
40
+ await new Promise((r) => setTimeout(r, 80));
41
+ assert.equal(dedup.isDuplicate('expiring'), false);
42
+ }
43
+ finally {
44
+ dedup.destroy();
45
+ }
46
+ });
47
+ test('ContentDedup disabled when windowMs is 0', () => {
48
+ const dedup = new ContentDedup(0);
49
+ try {
50
+ assert.equal(dedup.isDuplicate('repeat'), false);
51
+ assert.equal(dedup.isDuplicate('repeat'), false);
52
+ assert.equal(dedup.isDuplicate('repeat'), false);
53
+ }
54
+ finally {
55
+ dedup.destroy();
56
+ }
57
+ });
58
+ test('ContentDedup distinguishes same content with different keys', () => {
59
+ const dedup = new ContentDedup(30_000);
60
+ try {
61
+ // Simulates event_type\0content keying used by Collector.emit()
62
+ assert.equal(dedup.isDuplicate('observation\0file changed'), false);
63
+ assert.equal(dedup.isDuplicate('decision\0file changed'), false); // different type = not a dup
64
+ assert.equal(dedup.isDuplicate('observation\0file changed'), true); // same type+content = dup
65
+ }
66
+ finally {
67
+ dedup.destroy();
68
+ }
69
+ });
70
+ test('ContentDedup.destroy cleans up', () => {
71
+ const dedup = new ContentDedup(30_000);
72
+ dedup.isDuplicate('data');
73
+ dedup.destroy();
74
+ // After destroy, new instance should not see old data
75
+ const dedup2 = new ContentDedup(30_000);
76
+ try {
77
+ assert.equal(dedup2.isDuplicate('data'), false);
78
+ }
79
+ finally {
80
+ dedup2.destroy();
81
+ }
82
+ });
83
+ //# sourceMappingURL=dedup.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dedup.test.js","sourceRoot":"","sources":["../src/dedup.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import type { EventType } from '@memoire-ai/shared';
2
+ import type { Collector } from '../capture.js';
3
+ export interface HookEvent {
4
+ type: 'session_start' | 'prompt' | 'tool_use' | 'session_end';
5
+ content?: string;
6
+ files_read?: string[];
7
+ files_modified?: string[];
8
+ branch_name?: string;
9
+ }
10
+ export interface HookHandlers {
11
+ onSessionStart: () => Promise<void>;
12
+ onPrompt: (prompt: string) => Promise<void>;
13
+ onToolUse: (content: string, context?: ToolUseContext) => Promise<void>;
14
+ onSessionEnd: (summary?: string) => Promise<void>;
15
+ }
16
+ export interface ToolUseContext {
17
+ read?: string[];
18
+ modified?: string[];
19
+ branchName?: string;
20
+ toolName?: string;
21
+ status?: 'success' | 'error';
22
+ error?: string;
23
+ concepts?: string[];
24
+ eventTypeHint?: Exclude<EventType, 'prompt' | 'session_summary'>;
25
+ }
26
+ interface HookCollector {
27
+ sessionStart(): Promise<void>;
28
+ prompt(content: string, opts?: CaptureOptions): Promise<void>;
29
+ observation(content: string, opts?: CaptureOptions): Promise<void>;
30
+ decision(content: string, opts?: CaptureOptions): Promise<void>;
31
+ attempt(content: string, opts?: CaptureOptions): Promise<void>;
32
+ convention(content: string, opts?: CaptureOptions): Promise<void>;
33
+ branchEvent(content: string, opts?: CaptureOptions): Promise<void>;
34
+ sessionEnd(summary: string): Promise<void>;
35
+ }
36
+ interface CaptureOptions {
37
+ filesRead?: string[];
38
+ filesModified?: string[];
39
+ concepts?: string[];
40
+ branchName?: string;
41
+ }
42
+ /**
43
+ * Create hook handlers that bridge IDE lifecycle events
44
+ * to the Memoire collector.
45
+ *
46
+ * Follows the claude-mem 5-hook lifecycle:
47
+ * SessionStart → UserPromptSubmit → PostToolUse → Summary → SessionEnd
48
+ */
49
+ export declare function createHooks(collector: HookCollector | Collector): HookHandlers;
50
+ export declare function inferToolUseEventType(content: string, context?: ToolUseContext): Exclude<EventType, 'prompt' | 'session_summary'>;
51
+ export {};
52
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,eAAe,GAAG,QAAQ,GAAG,UAAU,GAAG,aAAa,CAAC;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,GAAG,iBAAiB,CAAC,CAAC;CAClE;AAED,UAAU,aAAa;IACrB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C;AAED,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,aAAa,GAAG,SAAS,GAAG,YAAY,CAiD9E;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,SAAS,EAAE,QAAQ,GAAG,iBAAiB,CAAC,CAwBjI"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Create hook handlers that bridge IDE lifecycle events
3
+ * to the Memoire collector.
4
+ *
5
+ * Follows the claude-mem 5-hook lifecycle:
6
+ * SessionStart → UserPromptSubmit → PostToolUse → Summary → SessionEnd
7
+ */
8
+ export function createHooks(collector) {
9
+ return {
10
+ async onSessionStart() {
11
+ await collector.sessionStart();
12
+ },
13
+ async onPrompt(prompt) {
14
+ await collector.prompt(normalizePrompt(prompt), {
15
+ concepts: extractPromptConcepts(prompt),
16
+ });
17
+ },
18
+ async onToolUse(content, context) {
19
+ const eventType = inferToolUseEventType(content, context);
20
+ const captureContent = formatToolUseContent(content, context);
21
+ const captureOptions = {
22
+ filesRead: context?.read,
23
+ filesModified: context?.modified,
24
+ branchName: context?.branchName,
25
+ concepts: mergeConcepts(context?.concepts, context?.toolName, context?.branchName, eventType, content),
26
+ };
27
+ if (eventType === 'decision') {
28
+ await collector.decision(captureContent, captureOptions);
29
+ return;
30
+ }
31
+ if (eventType === 'attempt') {
32
+ await collector.attempt(captureContent, captureOptions);
33
+ return;
34
+ }
35
+ if (eventType === 'convention') {
36
+ await collector.convention(captureContent, captureOptions);
37
+ return;
38
+ }
39
+ if (eventType === 'branch_event') {
40
+ await collector.branchEvent(captureContent, captureOptions);
41
+ return;
42
+ }
43
+ await collector.observation(captureContent, captureOptions);
44
+ },
45
+ async onSessionEnd(summary) {
46
+ await collector.sessionEnd(summary ?? 'Session ended');
47
+ },
48
+ };
49
+ }
50
+ export function inferToolUseEventType(content, context) {
51
+ if (context?.eventTypeHint) {
52
+ return context.eventTypeHint;
53
+ }
54
+ const normalized = normalizeText(`${context?.toolName ?? ''} ${context?.error ?? ''} ${content}`);
55
+ if (isBranchEvent(normalized, context?.branchName)) {
56
+ return 'branch_event';
57
+ }
58
+ if (isConvention(normalized)) {
59
+ return 'convention';
60
+ }
61
+ if (context?.status === 'error' || isFailedAttempt(normalized)) {
62
+ return 'attempt';
63
+ }
64
+ if (isDecision(normalized)) {
65
+ return 'decision';
66
+ }
67
+ return 'observation';
68
+ }
69
+ function isBranchEvent(content, branchName) {
70
+ return Boolean(branchName && /\b(branch|merge|merged|rebase|rebased|checkout|checked out|abandon|abandoned|cherry-pick|pr)\b/i.test(content));
71
+ }
72
+ function isConvention(content) {
73
+ return (/\b(convention|standard|guideline|pattern|policy)\b/i.test(content)
74
+ || /\b(always|never|must|should)\b/i.test(content) && /\b(use|keep|store|put|place|route|name)\b/i.test(content)
75
+ || /\ball\s+[a-z0-9_/-]+\s+(routes|files|tests|components|handlers|schemas)\b/i.test(content));
76
+ }
77
+ function isFailedAttempt(content) {
78
+ return (/\b(tried|attempted|attempt|failed|failure|broke|broken|reverted|rollback|rolled back|did not work|error|exception|timeout)\b/i.test(content)
79
+ || /\bfix attempt\b/i.test(content));
80
+ }
81
+ function isDecision(content) {
82
+ return /\b(decided|decision|choose|chose|picked|standardized|settled on|adopted|switched to|going with|moved to)\b/i.test(content);
83
+ }
84
+ function formatToolUseContent(content, context) {
85
+ const normalizedContent = normalizeText(content);
86
+ if (!normalizedContent) {
87
+ return context?.toolName ? `Tool ${context.toolName} executed.` : 'Tool executed.';
88
+ }
89
+ const toolPrefix = context?.toolName && !normalizedContent.toLowerCase().includes(context.toolName.toLowerCase())
90
+ ? `${context.toolName}: `
91
+ : '';
92
+ const errorSuffix = context?.status === 'error' && context.error
93
+ ? ` Error: ${normalizeText(context.error)}`
94
+ : '';
95
+ return `${toolPrefix}${normalizedContent}${errorSuffix}`.trim();
96
+ }
97
+ function normalizePrompt(prompt) {
98
+ return `User request: ${normalizeText(prompt)}`;
99
+ }
100
+ function extractPromptConcepts(prompt) {
101
+ return normalizeText(prompt)
102
+ .toLowerCase()
103
+ .split(/[^a-z0-9_.-]+/)
104
+ .filter((token) => token.length >= 4)
105
+ .slice(0, 8);
106
+ }
107
+ function mergeConcepts(concepts, toolName, branchName, eventType, content) {
108
+ const merged = new Set();
109
+ for (const concept of concepts ?? []) {
110
+ maybeAddConcept(merged, concept);
111
+ }
112
+ maybeAddConcept(merged, toolName);
113
+ maybeAddConcept(merged, branchName);
114
+ maybeAddConcept(merged, eventType);
115
+ for (const token of normalizeText(content).split(/[^a-zA-Z0-9_.-]+/)) {
116
+ maybeAddConcept(merged, token);
117
+ if (merged.size >= 8) {
118
+ break;
119
+ }
120
+ }
121
+ return merged.size > 0 ? Array.from(merged).slice(0, 8) : undefined;
122
+ }
123
+ function maybeAddConcept(target, token) {
124
+ if (!token) {
125
+ return;
126
+ }
127
+ const normalized = token.toLowerCase();
128
+ if (normalized.length < 4 || /^\d+$/.test(normalized)) {
129
+ return;
130
+ }
131
+ target.add(normalized);
132
+ }
133
+ function normalizeText(value) {
134
+ return value.replace(/\s+/g, ' ').trim();
135
+ }
136
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hooks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.test.d.ts","sourceRoot":"","sources":["../src/hooks.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,94 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { createHooks, inferToolUseEventType } from './hooks/index.js';
4
+ function createMockCollector() {
5
+ const calls = [];
6
+ return {
7
+ calls,
8
+ collector: {
9
+ async sessionStart() {
10
+ calls.push({ method: 'sessionStart' });
11
+ },
12
+ async prompt(content, opts) {
13
+ calls.push({ method: 'prompt', content, opts });
14
+ },
15
+ async observation(content, opts) {
16
+ calls.push({ method: 'observation', content, opts });
17
+ },
18
+ async decision(content, opts) {
19
+ calls.push({ method: 'decision', content, opts });
20
+ },
21
+ async attempt(content, opts) {
22
+ calls.push({ method: 'attempt', content, opts });
23
+ },
24
+ async convention(content, opts) {
25
+ calls.push({ method: 'convention', content, opts });
26
+ },
27
+ async branchEvent(content, opts) {
28
+ calls.push({ method: 'branchEvent', content, opts });
29
+ },
30
+ async sessionEnd(summary) {
31
+ calls.push({ method: 'sessionEnd', content: summary });
32
+ },
33
+ },
34
+ };
35
+ }
36
+ test('hooks send user prompts as prompt events with request framing', async () => {
37
+ const mock = createMockCollector();
38
+ const hooks = createHooks(mock.collector);
39
+ await hooks.onPrompt('Add WorkOS auth to the dashboard');
40
+ assert.deepEqual(mock.calls, [
41
+ {
42
+ method: 'prompt',
43
+ content: 'User request: Add WorkOS auth to the dashboard',
44
+ opts: {
45
+ concepts: ['workos', 'auth', 'dashboard'],
46
+ },
47
+ },
48
+ ]);
49
+ });
50
+ test('hooks classify decisions, attempts, conventions, and branch events', async () => {
51
+ const mock = createMockCollector();
52
+ const hooks = createHooks(mock.collector);
53
+ await hooks.onToolUse('Decided to use Postgres for auth sessions because cold starts were hurting latency.', {
54
+ toolName: 'Write',
55
+ modified: ['src/auth/session.ts'],
56
+ });
57
+ await hooks.onToolUse('Tried middleware auth redirects and reverted after it broke streaming responses.', {
58
+ toolName: 'Edit',
59
+ status: 'error',
60
+ error: 'Streaming response aborted',
61
+ modified: ['src/auth/middleware.ts'],
62
+ });
63
+ await hooks.onToolUse('All API routes should live in src/api and use the Hono router.', {
64
+ toolName: 'Read',
65
+ });
66
+ await hooks.onToolUse('Merged feature/oauth into main after the PR passed.', {
67
+ toolName: 'Bash',
68
+ branchName: 'feature/oauth',
69
+ });
70
+ assert.equal(mock.calls[0]?.method, 'decision');
71
+ assert.match(mock.calls[0]?.content ?? '', /^Write: Decided to use Postgres/);
72
+ assert.equal(mock.calls[1]?.method, 'attempt');
73
+ assert.match(mock.calls[1]?.content ?? '', /Error: Streaming response aborted/);
74
+ assert.equal(mock.calls[2]?.method, 'convention');
75
+ assert.equal(mock.calls[3]?.method, 'branchEvent');
76
+ assert.deepEqual(mock.calls[3]?.opts, {
77
+ filesRead: undefined,
78
+ filesModified: undefined,
79
+ branchName: 'feature/oauth',
80
+ concepts: ['bash', 'feature/oauth', 'branch_event', 'merged', 'feature', 'oauth', 'into', 'main'],
81
+ });
82
+ });
83
+ test('inferToolUseEventType respects hints and failure signals', () => {
84
+ assert.equal(inferToolUseEventType('This would normally look like a decision.', {
85
+ eventTypeHint: 'attempt',
86
+ }), 'attempt');
87
+ assert.equal(inferToolUseEventType('Updated oauth callback flow.', {
88
+ status: 'error',
89
+ }), 'attempt');
90
+ assert.equal(inferToolUseEventType('Observed the auth config and test layout.', {
91
+ toolName: 'Read',
92
+ }), 'observation');
93
+ });
94
+ //# sourceMappingURL=hooks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.test.js","sourceRoot":"","sources":["../src/hooks.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEtE,SAAS,mBAAmB;IAC1B,MAAM,KAAK,GAAgF,EAAE,CAAC;IAE9F,OAAO;QACL,KAAK;QACL,SAAS,EAAE;YACT,KAAK,CAAC,YAAY;gBAChB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,CAAC;YACzC,CAAC;YACD,KAAK,CAAC,MAAM,CAAC,OAAe,EAAE,IAA8B;gBAC1D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,CAAC;YACD,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,IAA8B;gBAC/D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACvD,CAAC;YACD,KAAK,CAAC,QAAQ,CAAC,OAAe,EAAE,IAA8B;gBAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,CAAC;YACD,KAAK,CAAC,OAAO,CAAC,OAAe,EAAE,IAA8B;gBAC3D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACnD,CAAC;YACD,KAAK,CAAC,UAAU,CAAC,OAAe,EAAE,IAA8B;gBAC9D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACtD,CAAC;YACD,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,IAA8B;gBAC/D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACvD,CAAC;YACD,KAAK,CAAC,UAAU,CAAC,OAAe;gBAC9B,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;YACzD,CAAC;SACF;KACF,CAAC;AACJ,CAAC;AAED,IAAI,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;IAC/E,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAE1C,MAAM,KAAK,CAAC,QAAQ,CAAC,kCAAkC,CAAC,CAAC;IAEzD,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE;QAC3B;YACE,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,gDAAgD;YACzD,IAAI,EAAE;gBACJ,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC;aAC1C;SACF;KACF,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;IACpF,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAE1C,MAAM,KAAK,CAAC,SAAS,CAAC,qFAAqF,EAAE;QAC3G,QAAQ,EAAE,OAAO;QACjB,QAAQ,EAAE,CAAC,qBAAqB,CAAC;KAClC,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,SAAS,CAAC,kFAAkF,EAAE;QACxG,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,4BAA4B;QACnC,QAAQ,EAAE,CAAC,wBAAwB,CAAC;KACrC,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,SAAS,CAAC,gEAAgE,EAAE;QACtF,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAC;IACH,MAAM,KAAK,CAAC,SAAS,CAAC,qDAAqD,EAAE;QAC3E,QAAQ,EAAE,MAAM;QAChB,UAAU,EAAE,eAAe;KAC5B,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAChD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,EAAE,iCAAiC,CAAC,CAAC;IAC9E,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC/C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,EAAE,EAAE,mCAAmC,CAAC,CAAC;IAChF,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;IACnD,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE;QACpC,SAAS,EAAE,SAAS;QACpB,aAAa,EAAE,SAAS;QACxB,UAAU,EAAE,eAAe;QAC3B,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC;KAClG,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACpE,MAAM,CAAC,KAAK,CACV,qBAAqB,CAAC,2CAA2C,EAAE;QACjE,aAAa,EAAE,SAAS;KACzB,CAAC,EACF,SAAS,CACV,CAAC;IACF,MAAM,CAAC,KAAK,CACV,qBAAqB,CAAC,8BAA8B,EAAE;QACpD,MAAM,EAAE,OAAO;KAChB,CAAC,EACF,SAAS,CACV,CAAC;IACF,MAAM,CAAC,KAAK,CACV,qBAAqB,CAAC,2CAA2C,EAAE;QACjE,QAAQ,EAAE,MAAM;KACjB,CAAC,EACF,aAAa,CACd,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { Collector, type SessionSummary } from './capture.js';
2
+ export { createHooks, type HookEvent, type HookHandlers, type ToolUseContext, } from './hooks/index.js';
3
+ export { buildCursorRulesContent, estimateCursorRulesTokens, writeCursorRules, readCursorRules, removeCursorRules, DEFAULT_CURSOR_RULES_TOKEN_BUDGET, type CursorRulesContext, } from './cursor-rules.js';
4
+ export { createCollectorFromConfig, getMemoireConfigPath, readMemoireConfig, type ConfigPathOptions, type CollectorFromConfigOptions, } from './config.js';
5
+ export { generateHooksJson, generateSetupInstructions, installCursorHooks, type CursorHooksConfig, } from './cursor-hooks.js';
6
+ export { stripPrivateTags, stripPrivateFromOptions } from './strip-private.js';
7
+ export { ContentDedup } from './dedup.js';
8
+ export { ClientSummarizer, type ClientSummarizerConfig, type ClientSummaryResult } from './client-summarizer.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,EACL,WAAW,EACX,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,cAAc,GACpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,uBAAuB,EACvB,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,iCAAiC,EACjC,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,GAChC,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,KAAK,sBAAsB,EAAE,KAAK,mBAAmB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export { Collector } from './capture.js';
2
+ export { createHooks, } from './hooks/index.js';
3
+ export { buildCursorRulesContent, estimateCursorRulesTokens, writeCursorRules, readCursorRules, removeCursorRules, DEFAULT_CURSOR_RULES_TOKEN_BUDGET, } from './cursor-rules.js';
4
+ export { createCollectorFromConfig, getMemoireConfigPath, readMemoireConfig, } from './config.js';
5
+ export { generateHooksJson, generateSetupInstructions, installCursorHooks, } from './cursor-hooks.js';
6
+ export { stripPrivateTags, stripPrivateFromOptions } from './strip-private.js';
7
+ export { ContentDedup } from './dedup.js';
8
+ export { ClientSummarizer } from './client-summarizer.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAuB,MAAM,cAAc,CAAC;AAC9D,OAAO,EACL,WAAW,GAIZ,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,uBAAuB,EACvB,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,iCAAiC,GAElC,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,iBAAiB,GAGlB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,kBAAkB,GAEnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAyD,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Strip <private>...</private> tags and their content from text
3
+ * before it reaches storage. Prevents sensitive data (credentials,
4
+ * tokens, PII) from being persisted in the memory layer.
5
+ *
6
+ * Mirrors claude-mem's tag-stripping.ts edge processing.
7
+ */
8
+ /** Remove all <private>…</private> blocks from content */
9
+ export declare function stripPrivateTags(content: string): string;
10
+ /** Strip private tags from all string fields in an options bag */
11
+ export declare function stripPrivateFromOptions<T extends Record<string, unknown>>(opts: T | undefined): T | undefined;
12
+ //# sourceMappingURL=strip-private.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip-private.d.ts","sourceRoot":"","sources":["../src/strip-private.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,0DAA0D;AAC1D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvE,IAAI,EAAE,CAAC,GAAG,SAAS,GAClB,CAAC,GAAG,SAAS,CAef"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Strip <private>...</private> tags and their content from text
3
+ * before it reaches storage. Prevents sensitive data (credentials,
4
+ * tokens, PII) from being persisted in the memory layer.
5
+ *
6
+ * Mirrors claude-mem's tag-stripping.ts edge processing.
7
+ */
8
+ const PRIVATE_TAG_RE = /<private>[\s\S]*?<\/private>/gi;
9
+ /** Remove all <private>…</private> blocks from content */
10
+ export function stripPrivateTags(content) {
11
+ return content.replace(PRIVATE_TAG_RE, '').replace(/\s{2,}/g, ' ').trim();
12
+ }
13
+ /** Strip private tags from all string fields in an options bag */
14
+ export function stripPrivateFromOptions(opts) {
15
+ if (!opts)
16
+ return opts;
17
+ const cleaned = { ...opts };
18
+ for (const [key, value] of Object.entries(cleaned)) {
19
+ if (typeof value === 'string') {
20
+ cleaned[key] = stripPrivateTags(value);
21
+ }
22
+ if (Array.isArray(value)) {
23
+ cleaned[key] = value.map((v) => typeof v === 'string' ? stripPrivateTags(v) : v);
24
+ }
25
+ }
26
+ return cleaned;
27
+ }
28
+ //# sourceMappingURL=strip-private.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip-private.js","sourceRoot":"","sources":["../src/strip-private.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,cAAc,GAAG,gCAAgC,CAAC;AAExD,0DAA0D;AAC1D,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAC5E,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,uBAAuB,CACrC,IAAmB;IAEnB,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,OAAO,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAmC,CAAC,GAAG,CAAC,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;QACtE,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,OAAmC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC1D,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAChD,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=strip-private.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip-private.test.d.ts","sourceRoot":"","sources":["../src/strip-private.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { stripPrivateTags, stripPrivateFromOptions } from './strip-private.js';
4
+ test('stripPrivateTags removes single private block', () => {
5
+ assert.equal(stripPrivateTags('before <private>secret token abc123</private> after'), 'before after');
6
+ });
7
+ test('stripPrivateTags removes multiple private blocks', () => {
8
+ assert.equal(stripPrivateTags('a <private>x</private> b <private>y</private> c'), 'a b c');
9
+ });
10
+ test('stripPrivateTags handles multiline private content', () => {
11
+ const input = `config loaded\n<private>\nAPI_KEY=sk-123\nDB_PASS=hunter2\n</private>\nproceeding`;
12
+ assert.equal(stripPrivateTags(input), 'config loaded proceeding');
13
+ });
14
+ test('stripPrivateTags is case-insensitive', () => {
15
+ assert.equal(stripPrivateTags('x <PRIVATE>secret</PRIVATE> y'), 'x y');
16
+ assert.equal(stripPrivateTags('x <Private>secret</Private> y'), 'x y');
17
+ });
18
+ test('stripPrivateTags returns content unchanged when no tags present', () => {
19
+ assert.equal(stripPrivateTags('no secrets here'), 'no secrets here');
20
+ });
21
+ test('stripPrivateTags returns empty string for fully private content', () => {
22
+ assert.equal(stripPrivateTags('<private>everything is secret</private>'), '');
23
+ });
24
+ test('stripPrivateFromOptions strips strings and string arrays', () => {
25
+ const result = stripPrivateFromOptions({
26
+ content: 'visible <private>hidden</private> text',
27
+ files: ['path/<private>secret</private>/file.ts', 'clean.ts'],
28
+ count: 42,
29
+ });
30
+ assert.equal(result?.content, 'visible text');
31
+ assert.deepEqual(result?.files, ['path//file.ts', 'clean.ts']);
32
+ assert.equal(result?.count, 42);
33
+ });
34
+ test('stripPrivateFromOptions returns undefined for undefined input', () => {
35
+ assert.equal(stripPrivateFromOptions(undefined), undefined);
36
+ });
37
+ //# sourceMappingURL=strip-private.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strip-private.test.js","sourceRoot":"","sources":["../src/strip-private.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE/E,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,CAAC,KAAK,CACV,gBAAgB,CAAC,qDAAqD,CAAC,EACvE,cAAc,CACf,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,GAAG,EAAE;IAC5D,MAAM,CAAC,KAAK,CACV,gBAAgB,CAAC,iDAAiD,CAAC,EACnE,OAAO,CACR,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAC9D,MAAM,KAAK,GAAG,mFAAmF,CAAC;IAClG,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,0BAA0B,CAAC,CAAC;AACpE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sCAAsC,EAAE,GAAG,EAAE;IAChD,MAAM,CAAC,KAAK,CACV,gBAAgB,CAAC,+BAA+B,CAAC,EACjD,KAAK,CACN,CAAC;IACF,MAAM,CAAC,KAAK,CACV,gBAAgB,CAAC,+BAA+B,CAAC,EACjD,KAAK,CACN,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;IAC3E,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC,CAAC;AACvE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;IAC3E,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,yCAAyC,CAAC,EAAE,EAAE,CAAC,CAAC;AAChF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACpE,MAAM,MAAM,GAAG,uBAAuB,CAAC;QACrC,OAAO,EAAE,wCAAwC;QACjD,KAAK,EAAE,CAAC,wCAAwC,EAAE,UAAU,CAAC;QAC7D,KAAK,EAAE,EAAE;KACV,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;IAC9C,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC,CAAC;IAC/D,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+DAA+D,EAAE,GAAG,EAAE;IACzE,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Simple nanoid-like ID generator (no dependencies) */
2
+ export declare function nanoid(size?: number): string;
3
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,wBAAgB,MAAM,CAAC,IAAI,SAAK,GAAG,MAAM,CAQxC"}
package/dist/utils.js ADDED
@@ -0,0 +1,11 @@
1
+ /** Simple nanoid-like ID generator (no dependencies) */
2
+ export function nanoid(size = 21) {
3
+ const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
4
+ const bytes = crypto.getRandomValues(new Uint8Array(size));
5
+ let id = '';
6
+ for (const byte of bytes) {
7
+ id += alphabet[byte % alphabet.length];
8
+ }
9
+ return id;
10
+ }
11
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,MAAM,UAAU,MAAM,CAAC,IAAI,GAAG,EAAE;IAC9B,MAAM,QAAQ,GAAG,gEAAgE,CAAC;IAClF,MAAM,KAAK,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3D,IAAI,EAAE,GAAG,EAAE,CAAC;IACZ,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,EAAE,IAAI,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}