@tznthou/ccrecall 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +290 -0
  3. package/README_ZH.md +288 -0
  4. package/dist/api/routes.d.ts +13 -0
  5. package/dist/api/routes.d.ts.map +1 -0
  6. package/dist/api/routes.js +428 -0
  7. package/dist/api/routes.js.map +1 -0
  8. package/dist/api/server.d.ts +7 -0
  9. package/dist/api/server.d.ts.map +1 -0
  10. package/dist/api/server.js +44 -0
  11. package/dist/api/server.js.map +1 -0
  12. package/dist/cli/daemon.d.ts +35 -0
  13. package/dist/cli/daemon.d.ts.map +1 -0
  14. package/dist/cli/daemon.js +163 -0
  15. package/dist/cli/daemon.js.map +1 -0
  16. package/dist/core/compression.d.ts +37 -0
  17. package/dist/core/compression.d.ts.map +1 -0
  18. package/dist/core/compression.js +129 -0
  19. package/dist/core/compression.js.map +1 -0
  20. package/dist/core/database.d.ts +219 -0
  21. package/dist/core/database.d.ts.map +1 -0
  22. package/dist/core/database.js +1513 -0
  23. package/dist/core/database.js.map +1 -0
  24. package/dist/core/index.d.ts +9 -0
  25. package/dist/core/index.d.ts.map +1 -0
  26. package/dist/core/index.js +8 -0
  27. package/dist/core/index.js.map +1 -0
  28. package/dist/core/indexer.d.ts +15 -0
  29. package/dist/core/indexer.d.ts.map +1 -0
  30. package/dist/core/indexer.js +224 -0
  31. package/dist/core/indexer.js.map +1 -0
  32. package/dist/core/lint.d.ts +17 -0
  33. package/dist/core/lint.d.ts.map +1 -0
  34. package/dist/core/lint.js +51 -0
  35. package/dist/core/lint.js.map +1 -0
  36. package/dist/core/log-safe.d.ts +6 -0
  37. package/dist/core/log-safe.d.ts.map +1 -0
  38. package/dist/core/log-safe.js +11 -0
  39. package/dist/core/log-safe.js.map +1 -0
  40. package/dist/core/maintenance-coordinator.d.ts +26 -0
  41. package/dist/core/maintenance-coordinator.d.ts.map +1 -0
  42. package/dist/core/maintenance-coordinator.js +67 -0
  43. package/dist/core/maintenance-coordinator.js.map +1 -0
  44. package/dist/core/memory-service.d.ts +31 -0
  45. package/dist/core/memory-service.d.ts.map +1 -0
  46. package/dist/core/memory-service.js +47 -0
  47. package/dist/core/memory-service.js.map +1 -0
  48. package/dist/core/parser.d.ts +17 -0
  49. package/dist/core/parser.d.ts.map +1 -0
  50. package/dist/core/parser.js +205 -0
  51. package/dist/core/parser.js.map +1 -0
  52. package/dist/core/scanner.d.ts +8 -0
  53. package/dist/core/scanner.d.ts.map +1 -0
  54. package/dist/core/scanner.js +123 -0
  55. package/dist/core/scanner.js.map +1 -0
  56. package/dist/core/summarizer.d.ts +12 -0
  57. package/dist/core/summarizer.d.ts.map +1 -0
  58. package/dist/core/summarizer.js +430 -0
  59. package/dist/core/summarizer.js.map +1 -0
  60. package/dist/core/topic-extractor.d.ts +11 -0
  61. package/dist/core/topic-extractor.d.ts.map +1 -0
  62. package/dist/core/topic-extractor.js +51 -0
  63. package/dist/core/topic-extractor.js.map +1 -0
  64. package/dist/core/types.d.ts +329 -0
  65. package/dist/core/types.d.ts.map +1 -0
  66. package/dist/core/types.js +12 -0
  67. package/dist/core/types.js.map +1 -0
  68. package/dist/core/watcher.d.ts +30 -0
  69. package/dist/core/watcher.d.ts.map +1 -0
  70. package/dist/core/watcher.js +146 -0
  71. package/dist/core/watcher.js.map +1 -0
  72. package/dist/index.d.ts +3 -0
  73. package/dist/index.d.ts.map +1 -0
  74. package/dist/index.js +116 -0
  75. package/dist/index.js.map +1 -0
  76. package/dist/mcp/server.d.ts +3 -0
  77. package/dist/mcp/server.d.ts.map +1 -0
  78. package/dist/mcp/server.js +32 -0
  79. package/dist/mcp/server.js.map +1 -0
  80. package/dist/mcp/tools.d.ts +39 -0
  81. package/dist/mcp/tools.d.ts.map +1 -0
  82. package/dist/mcp/tools.js +215 -0
  83. package/dist/mcp/tools.js.map +1 -0
  84. package/hooks/README.md +78 -0
  85. package/hooks/session-end.mjs +92 -0
  86. package/hooks/session-start.mjs +112 -0
  87. package/package.json +54 -0
@@ -0,0 +1,329 @@
1
+ /** 單行 JSONL 解析結果 */
2
+ export interface ParsedLine {
3
+ type: string;
4
+ uuid: string | null;
5
+ parentUuid: string | null;
6
+ sessionId: string | null;
7
+ timestamp: string | null;
8
+ role: 'user' | 'assistant' | null;
9
+ contentText: string | null;
10
+ contentJson: string | null;
11
+ hasToolUse: boolean;
12
+ hasToolResult: boolean;
13
+ toolNames: string[];
14
+ rawJson: string;
15
+ inputTokens: number | null;
16
+ outputTokens: number | null;
17
+ cacheReadTokens: number | null;
18
+ cacheCreationTokens: number | null;
19
+ model: string | null;
20
+ requestId: string | null;
21
+ }
22
+ /** 整個 session 解析結果 */
23
+ export interface ParsedSession {
24
+ sessionId: string;
25
+ title: string | null;
26
+ messages: ParsedLine[];
27
+ startedAt: string | null;
28
+ endedAt: string | null;
29
+ skippedLines: number;
30
+ totalLines: number;
31
+ }
32
+ /** 掃描到的 session 檔案資訊 */
33
+ export interface ScannedSession {
34
+ filePath: string;
35
+ fileSize: number;
36
+ fileMtime: string;
37
+ sessionId: string;
38
+ }
39
+ /** 掃描到的 subagent 檔案資訊 */
40
+ export interface ScannedSubagent {
41
+ filePath: string;
42
+ fileSize: number;
43
+ fileMtime: string;
44
+ subagentId: string;
45
+ parentSessionId: string;
46
+ agentType: string | null;
47
+ }
48
+ /** 掃描到的專案資訊 */
49
+ export interface ScannedProject {
50
+ projectId: string;
51
+ displayName: string;
52
+ sessions: ScannedSession[];
53
+ }
54
+ /** 專案 */
55
+ export interface Project {
56
+ id: string;
57
+ displayName: string;
58
+ sessionCount: number;
59
+ lastActivityAt: string | null;
60
+ }
61
+ /** session_files 操作類型 */
62
+ export type FileOperation = 'read' | 'edit' | 'write' | 'discovery';
63
+ /** session_files 表記錄 */
64
+ export interface SessionFile {
65
+ sessionId: string;
66
+ filePath: string;
67
+ operation: FileOperation;
68
+ count: number;
69
+ firstSeenSeq: number;
70
+ lastSeenSeq: number;
71
+ }
72
+ /** session_files 寫入用型別(解決 summarizer ↔ database 循環依賴) */
73
+ export interface SessionFileInput {
74
+ filePath: string;
75
+ operation: FileOperation;
76
+ count: number;
77
+ firstSeenSeq: number;
78
+ lastSeenSeq: number;
79
+ }
80
+ /** Session 摘要 */
81
+ export interface SessionMeta {
82
+ id: string;
83
+ projectId: string;
84
+ title: string | null;
85
+ messageCount: number;
86
+ startedAt: string | null;
87
+ endedAt: string | null;
88
+ archived: boolean;
89
+ summaryText: string | null;
90
+ intentText: string | null;
91
+ outcomeStatus: OutcomeStatus;
92
+ durationSeconds: number | null;
93
+ activeDurationSeconds: number | null;
94
+ summaryVersion: number | null;
95
+ tags: string | null;
96
+ filesTouched: string | null;
97
+ toolsUsed: string | null;
98
+ totalInputTokens: number | null;
99
+ totalOutputTokens: number | null;
100
+ }
101
+ /** 訊息 */
102
+ export interface Message {
103
+ id: number;
104
+ sessionId: string;
105
+ type: 'user' | 'assistant' | 'queue-operation' | 'last-prompt';
106
+ role: 'user' | 'assistant' | null;
107
+ contentText: string | null;
108
+ contentJson: string | null;
109
+ hasToolUse: boolean;
110
+ hasToolResult: boolean;
111
+ toolNames: string[] | null;
112
+ timestamp: string | null;
113
+ sequence: number;
114
+ inputTokens: number | null;
115
+ outputTokens: number | null;
116
+ cacheReadTokens: number | null;
117
+ cacheCreationTokens: number | null;
118
+ model: string | null;
119
+ }
120
+ /** Session Token 統計 */
121
+ export interface SessionTokenStats {
122
+ totalInputTokens: number;
123
+ totalOutputTokens: number;
124
+ totalCacheReadTokens: number;
125
+ totalCacheCreationTokens: number;
126
+ cacheHitRate: number;
127
+ models: string[];
128
+ primaryModel: string | null;
129
+ turns: Array<{
130
+ sequence: number;
131
+ timestamp: string | null;
132
+ inputTokens: number;
133
+ outputTokens: number;
134
+ cacheReadTokens: number;
135
+ cacheCreationTokens: number;
136
+ contextTotal: number;
137
+ hasToolUse: boolean;
138
+ toolNames: string[];
139
+ model: string | null;
140
+ }>;
141
+ }
142
+ /** 訊息上下文(搜尋結果預覽用) */
143
+ export interface MessageContext {
144
+ target: Message | null;
145
+ before: Message[];
146
+ after: Message[];
147
+ }
148
+ /** 搜尋範圍 */
149
+ export type SearchScope = 'messages' | 'sessions';
150
+ export type SearchSortBy = 'rank' | 'date';
151
+ /** 搜尋選項 */
152
+ export interface SearchOptions {
153
+ dateFrom?: string;
154
+ dateTo?: string;
155
+ sortBy?: SearchSortBy;
156
+ }
157
+ /** 搜尋結果 */
158
+ export interface SearchResult {
159
+ sessionId: string;
160
+ sessionTitle: string | null;
161
+ projectId: string;
162
+ projectName: string;
163
+ messageId: number;
164
+ snippet: string;
165
+ timestamp: string | null;
166
+ sessionStartedAt: string | null;
167
+ }
168
+ /** 搜尋分頁回應 */
169
+ export interface SearchPage {
170
+ results: SearchResult[];
171
+ offset: number;
172
+ hasMore: boolean;
173
+ }
174
+ /** Session 層級搜尋結果 */
175
+ export interface SessionSearchResult {
176
+ sessionId: string;
177
+ sessionTitle: string | null;
178
+ projectId: string;
179
+ projectName: string;
180
+ tags: string | null;
181
+ filesTouched: string | null;
182
+ snippet: string;
183
+ startedAt: string | null;
184
+ outcomeStatus: OutcomeStatus;
185
+ }
186
+ /** Session 搜尋分頁回應 */
187
+ export interface SessionSearchPage {
188
+ results: SessionSearchResult[];
189
+ offset: number;
190
+ hasMore: boolean;
191
+ }
192
+ /** 檔案歷史條目 */
193
+ export interface FileHistoryEntry {
194
+ sessionId: string;
195
+ sessionTitle: string | null;
196
+ projectId: string;
197
+ projectName: string;
198
+ operation: FileOperation;
199
+ count: number;
200
+ startedAt: string | null;
201
+ }
202
+ /** Subagent session */
203
+ export interface SubagentSession {
204
+ id: string;
205
+ parentSessionId: string;
206
+ agentType: string | null;
207
+ filePath: string;
208
+ fileSize: number | null;
209
+ fileMtime: string | null;
210
+ messageCount: number;
211
+ startedAt: string | null;
212
+ endedAt: string | null;
213
+ createdAt: string;
214
+ }
215
+ /** 索引進度 */
216
+ export interface IndexerStatus {
217
+ phase: 'scanning' | 'parsing' | 'indexing' | 'done';
218
+ progress: number;
219
+ total: number;
220
+ current: number;
221
+ }
222
+ export type OutcomeStatus = 'committed' | 'tested' | 'in-progress' | 'quick-qa' | null;
223
+ export interface OutcomeSignals {
224
+ gitCommitInvoked: boolean;
225
+ testCommandRan: boolean;
226
+ endedWithEdits: boolean;
227
+ isQuickQA: boolean;
228
+ }
229
+ export interface SessionSummary {
230
+ intentText: string;
231
+ activityText: string;
232
+ outcomeStatus: OutcomeStatus;
233
+ outcomeSignals: OutcomeSignals;
234
+ summaryText: string;
235
+ tags: string;
236
+ filesTouched: string;
237
+ toolsUsed: string;
238
+ summaryVersion: number;
239
+ durationSeconds: number | null;
240
+ activeDurationSeconds: number | null;
241
+ }
242
+ /** 記憶類型 */
243
+ export type MemoryType = 'decision' | 'discovery' | 'preference' | 'pattern' | 'feedback';
244
+ /** 記憶條目 */
245
+ export interface Memory {
246
+ id: number;
247
+ sessionId: string | null;
248
+ messageId: string | null;
249
+ content: string;
250
+ type: MemoryType;
251
+ confidence: number;
252
+ createdAt: string;
253
+ }
254
+ /** 知識深度(由 mention_count 衍生:>=5 deep, >=2 medium, >=1 shallow, else none) */
255
+ export type KnowledgeDepth = 'deep' | 'medium' | 'shallow' | 'none';
256
+ export declare function deriveDepth(mentionCount: number): KnowledgeDepth;
257
+ /** knowledge_map 條目(DB row) */
258
+ export interface Topic {
259
+ topicKey: string;
260
+ projectId: string;
261
+ mentionCount: number;
262
+ lastTouched: string;
263
+ }
264
+ /** knowledge_map 查詢用(含衍生 depth) */
265
+ export interface TopicWithDepth extends Topic {
266
+ depth: KnowledgeDepth;
267
+ }
268
+ /** session 中途快照 */
269
+ export interface SessionCheckpoint {
270
+ id: number;
271
+ sessionId: string;
272
+ projectId: string;
273
+ snapshotText: string;
274
+ createdAt: string;
275
+ }
276
+ /** /memory/query 回應 */
277
+ export interface MemoryQueryResult {
278
+ memories: Array<{
279
+ content: string;
280
+ source: string;
281
+ confidence: number;
282
+ depth: KnowledgeDepth | null;
283
+ }>;
284
+ totalTokenEstimate: number;
285
+ }
286
+ /** /memory/context 回應 */
287
+ export interface SessionContextResult {
288
+ summary: string | null;
289
+ decisions: string[];
290
+ filesTouched: string[];
291
+ }
292
+ /** /metacognition/check summary 模式回應 */
293
+ export interface MetacognitionSummary {
294
+ projectId: string;
295
+ topTopics: TopicWithDepth[];
296
+ recentTopics: TopicWithDepth[];
297
+ staleTopics: TopicWithDepth[];
298
+ counts: {
299
+ totalTopics: number;
300
+ totalMemories: number;
301
+ totalSessions: number;
302
+ };
303
+ }
304
+ /** /metacognition/check detail 模式回應 */
305
+ export interface TopicDetail {
306
+ topicKey: string;
307
+ projectId: string;
308
+ mentionCount: number;
309
+ lastTouched: string;
310
+ depth: KnowledgeDepth;
311
+ memories: Memory[];
312
+ relatedTopics: string[];
313
+ }
314
+ /** /session/checkpoint 回應 */
315
+ export interface CheckpointResult {
316
+ ok: boolean;
317
+ checkpointId: number;
318
+ }
319
+ /** /health 回應 */
320
+ export interface HealthResult {
321
+ status: 'ok' | 'error';
322
+ version: string;
323
+ dbPath: string;
324
+ sessionCount: number;
325
+ memoryCount: number;
326
+ topicCount: number;
327
+ uptime: number;
328
+ }
329
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // ── 從 ccRewind 抽取:JSONL Parser 型別 ──
3
+ export function deriveDepth(mentionCount) {
4
+ if (mentionCount >= 5)
5
+ return 'deep';
6
+ if (mentionCount >= 2)
7
+ return 'medium';
8
+ if (mentionCount >= 1)
9
+ return 'shallow';
10
+ return 'none';
11
+ }
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC,sCAAsC;AAuStC,MAAM,UAAU,WAAW,CAAC,YAAoB;IAC9C,IAAI,YAAY,IAAI,CAAC;QAAE,OAAO,MAAM,CAAA;IACpC,IAAI,YAAY,IAAI,CAAC;QAAE,OAAO,QAAQ,CAAA;IACtC,IAAI,YAAY,IAAI,CAAC;QAAE,OAAO,SAAS,CAAA;IACvC,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { Database } from './database.js';
2
+ export type RunIndexerFn = (db: Database) => Promise<void>;
3
+ export interface JsonlWatcherOptions {
4
+ baseDir?: string;
5
+ debounceMs?: number;
6
+ fullResyncMs?: number;
7
+ runIndexer?: RunIndexerFn;
8
+ }
9
+ export declare class JsonlWatcher {
10
+ private readonly db;
11
+ private readonly baseDir;
12
+ private readonly debounceMs;
13
+ private readonly fullResyncMs;
14
+ private readonly runIndexerFn;
15
+ private watcher;
16
+ private debounceTimer;
17
+ private fullResyncTimer;
18
+ private inflight;
19
+ private dirty;
20
+ constructor(db: Database, opts?: JsonlWatcherOptions);
21
+ isRunning(): boolean;
22
+ start(): Promise<void>;
23
+ stop(): Promise<void>;
24
+ private scheduleIncrementalScan;
25
+ private runScan;
26
+ /** Trigger a scan immediately, bypassing debounce. Intended for /session/end
27
+ * rescue and integration tests. */
28
+ runNow(): Promise<void>;
29
+ }
30
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/core/watcher.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAoB7C,MAAM,MAAM,YAAY,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAE1D,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,UAAU,CAAC,EAAE,YAAY,CAAA;CAC1B;AAYD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAU;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAQ;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAE3C,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,KAAK,CAAQ;gBAET,EAAE,EAAE,QAAQ,EAAE,IAAI,GAAE,mBAAwB;IAQxD,SAAS,IAAI,OAAO;IAId,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0CtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB3B,OAAO,CAAC,uBAAuB;YASjB,OAAO;IAqBrB;wCACoC;IAC9B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAO9B"}
@@ -0,0 +1,146 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ import chokidar from 'chokidar';
5
+ import { runIndexer } from './indexer.js';
6
+ import { scrubErrorMessage } from './log-safe.js';
7
+ /**
8
+ * Phase 4e: JSONL watch mode.
9
+ *
10
+ * Watches ~/.claude/projects for JSONL changes so fresh-session harvests don't
11
+ * race the SessionEnd hook (the JSONL may not exist when hook fires; chokidar
12
+ * add event triggers an incremental reindex so /session/end finds the row on
13
+ * retry). Single-flight — at most one runIndexer inflight; events arriving
14
+ * during a scan mark dirty, triggering one follow-up scan after completion.
15
+ * Periodic full-resync backstop covers events chokidar might miss on some
16
+ * platforms (APFS rename edge cases, NFS, etc).
17
+ */
18
+ const DEFAULT_BASE_DIR = path.join(os.homedir(), '.claude', 'projects');
19
+ const DEFAULT_DEBOUNCE_MS = 2000;
20
+ const DEFAULT_FULL_RESYNC_MS = 10 * 60 * 1000;
21
+ function isJsonlFileOrDir(p, stats) {
22
+ // chokidar 5 ignored() returns true to skip. Keep dirs (need recursion) and
23
+ // .jsonl files; drop everything else. stats is undefined during directory
24
+ // enumeration of an entry we haven't stat'd yet — treat as "keep" and let
25
+ // the recursive walk re-check with stats.
26
+ if (!stats)
27
+ return false;
28
+ if (stats.isDirectory())
29
+ return false;
30
+ return !p.endsWith('.jsonl');
31
+ }
32
+ export class JsonlWatcher {
33
+ db;
34
+ baseDir;
35
+ debounceMs;
36
+ fullResyncMs;
37
+ runIndexerFn;
38
+ watcher = null;
39
+ debounceTimer = null;
40
+ fullResyncTimer = null;
41
+ inflight = false;
42
+ dirty = false;
43
+ constructor(db, opts = {}) {
44
+ this.db = db;
45
+ this.baseDir = opts.baseDir ?? DEFAULT_BASE_DIR;
46
+ this.debounceMs = opts.debounceMs ?? DEFAULT_DEBOUNCE_MS;
47
+ this.fullResyncMs = opts.fullResyncMs ?? DEFAULT_FULL_RESYNC_MS;
48
+ this.runIndexerFn = opts.runIndexer ?? runIndexer;
49
+ }
50
+ isRunning() {
51
+ return this.watcher !== null;
52
+ }
53
+ async start() {
54
+ if (this.watcher)
55
+ return;
56
+ const watcher = chokidar.watch(this.baseDir, {
57
+ persistent: true,
58
+ // runIndexer ran before watcher.start() — skip the add spam from the
59
+ // initial tree walk.
60
+ ignoreInitial: true,
61
+ ignored: isJsonlFileOrDir,
62
+ awaitWriteFinish: {
63
+ stabilityThreshold: 500,
64
+ pollInterval: 100,
65
+ },
66
+ });
67
+ const onEvent = () => { this.scheduleIncrementalScan(); };
68
+ watcher.on('add', onEvent);
69
+ watcher.on('change', onEvent);
70
+ watcher.on('unlink', onEvent);
71
+ watcher.on('error', (err) => {
72
+ console.warn('[watcher] chokidar error:', scrubErrorMessage(err));
73
+ });
74
+ // Await ready so callers (daemon bootstrap) can be sure ignoreInitial has
75
+ // settled before advertising the service. Without this, a JSONL written
76
+ // between scanProjects() and chokidar's ready event would be missed by
77
+ // both paths until the backstop or a rescue fires.
78
+ await new Promise((resolve) => {
79
+ watcher.once('ready', () => resolve());
80
+ });
81
+ this.watcher = watcher;
82
+ this.fullResyncTimer = setInterval(() => {
83
+ // Bypass scheduleIncrementalScan so steady JSONL churn cannot keep
84
+ // pushing the debounce out and starve the backstop. runScan's single-
85
+ // flight guard still prevents overlapping work.
86
+ void this.runScan();
87
+ }, this.fullResyncMs);
88
+ this.fullResyncTimer.unref?.();
89
+ }
90
+ async stop() {
91
+ if (this.debounceTimer) {
92
+ clearTimeout(this.debounceTimer);
93
+ this.debounceTimer = null;
94
+ }
95
+ if (this.fullResyncTimer) {
96
+ clearInterval(this.fullResyncTimer);
97
+ this.fullResyncTimer = null;
98
+ }
99
+ if (this.watcher) {
100
+ const w = this.watcher;
101
+ this.watcher = null;
102
+ await w.close();
103
+ }
104
+ }
105
+ scheduleIncrementalScan() {
106
+ if (this.debounceTimer)
107
+ clearTimeout(this.debounceTimer);
108
+ this.debounceTimer = setTimeout(() => {
109
+ this.debounceTimer = null;
110
+ void this.runScan();
111
+ }, this.debounceMs);
112
+ this.debounceTimer.unref?.();
113
+ }
114
+ async runScan() {
115
+ if (this.inflight) {
116
+ this.dirty = true;
117
+ return;
118
+ }
119
+ this.inflight = true;
120
+ this.dirty = false;
121
+ try {
122
+ await this.runIndexerFn(this.db);
123
+ }
124
+ catch (err) {
125
+ console.warn('[watcher] incremental scan error:', scrubErrorMessage(err));
126
+ }
127
+ finally {
128
+ this.inflight = false;
129
+ if (this.dirty) {
130
+ // Events that arrived during the inflight scan bumped dirty; kick the
131
+ // debounce again so the follow-up scan sees them.
132
+ this.scheduleIncrementalScan();
133
+ }
134
+ }
135
+ }
136
+ /** Trigger a scan immediately, bypassing debounce. Intended for /session/end
137
+ * rescue and integration tests. */
138
+ async runNow() {
139
+ if (this.debounceTimer) {
140
+ clearTimeout(this.debounceTimer);
141
+ this.debounceTimer = null;
142
+ }
143
+ await this.runScan();
144
+ }
145
+ }
146
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/core/watcher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import path from 'node:path';
4
+ import os from 'node:os';
5
+ import { createServer } from './api/server.js';
6
+ import { Database } from './core/database.js';
7
+ import { runIndexer } from './core/indexer.js';
8
+ import { MemoryService } from './core/memory-service.js';
9
+ import { MaintenanceCoordinator } from './core/maintenance-coordinator.js';
10
+ import { JsonlWatcher } from './core/watcher.js';
11
+ import { installDaemon, uninstallDaemon } from './cli/daemon.js';
12
+ const subcommand = process.argv[2];
13
+ if (subcommand === 'install-daemon' || subcommand === 'uninstall-daemon') {
14
+ const dryRun = process.argv.includes('--dry-run');
15
+ // Propagate the current daemon's runtime config into the plist so the
16
+ // auto-started instance uses the same port/db as the user's interactive
17
+ // session. Without this the LaunchAgent silently resets to defaults after
18
+ // the next login, which looks like memory loss or a port mismatch.
19
+ const envPort = process.env.CCRECALL_PORT;
20
+ const parsedPort = envPort ? parseInt(envPort, 10) : NaN;
21
+ // Cap at 65535 so a typo like CCRECALL_PORT=70000 doesn't get baked into
22
+ // the plist and crash-loop launchd (ERR_SOCKET_BAD_PORT on every restart).
23
+ const port = Number.isFinite(parsedPort) && parsedPort > 0 && parsedPort <= 65535
24
+ ? parsedPort
25
+ : undefined;
26
+ const dbPath = process.env.CCRECALL_DB_PATH;
27
+ const action = subcommand === 'install-daemon'
28
+ ? () => installDaemon({ dryRun, port, dbPath })
29
+ : () => uninstallDaemon();
30
+ action().then(() => process.exit(0)).catch((err) => {
31
+ console.error(`[ccmem ${subcommand}] ${err.message}`);
32
+ process.exit(1);
33
+ });
34
+ }
35
+ else if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help') {
36
+ printHelp();
37
+ process.exit(0);
38
+ }
39
+ else {
40
+ startDaemon().catch((err) => {
41
+ console.error(`[ccmem] fatal: ${err.message}`);
42
+ process.exit(1);
43
+ });
44
+ }
45
+ function printHelp() {
46
+ const port = process.env.CCRECALL_PORT ?? '7749';
47
+ console.log(`ccRecall — AI memory service for Claude Code
48
+
49
+ Usage:
50
+ ccmem Run the daemon (HTTP API on :${port})
51
+ ccmem install-daemon Install macOS LaunchAgent for auto-start
52
+ ccmem install-daemon --dry-run Print plist without installing
53
+ ccmem uninstall-daemon Remove LaunchAgent and stop auto-start
54
+
55
+ Environment:
56
+ CCRECALL_PORT HTTP port (default: 7749)
57
+ CCRECALL_DB_PATH SQLite path (default: ~/.ccrecall/ccrecall.db)
58
+
59
+ See docs/launchd.md for manual install and troubleshooting.`);
60
+ }
61
+ async function startDaemon() {
62
+ const rawPort = parseInt(process.env.CCRECALL_PORT ?? '7749', 10);
63
+ const PORT = Number.isFinite(rawPort) && rawPort > 0 && rawPort <= 65535 ? rawPort : 7749;
64
+ const DB_PATH = process.env.CCRECALL_DB_PATH ?? path.join(os.homedir(), '.ccrecall', 'ccrecall.db');
65
+ const db = new Database(DB_PATH);
66
+ console.log(`Database initialized at ${DB_PATH}`);
67
+ // Await the initial index so the watcher (which uses ignoreInitial=true)
68
+ // doesn't start before scanProjects has observed the tree. A JSONL written
69
+ // between these two phases would otherwise be invisible until the 10-minute
70
+ // backstop or a /session/end rescue.
71
+ console.log('Running indexer...');
72
+ try {
73
+ await runIndexer(db);
74
+ console.log('Indexer complete.');
75
+ }
76
+ catch (err) {
77
+ console.error('Indexer error:', err);
78
+ }
79
+ // Phase 4d: start background compression scheduler. unref'd so it never blocks
80
+ // process exit — HTTP server keep-alive is authoritative.
81
+ const memoryService = new MemoryService(db);
82
+ const coordinator = new MaintenanceCoordinator(db, memoryService);
83
+ coordinator.start();
84
+ console.log('Maintenance coordinator started.');
85
+ // Phase 4e: JSONL watch mode — incremental reindex when Claude Code writes new
86
+ // session files. Complements the hook path; covers resumed sessions too.
87
+ // start() resolves only after chokidar's `ready` event, so ignoreInitial
88
+ // has settled before we advertise the service.
89
+ const watcher = new JsonlWatcher(db);
90
+ try {
91
+ await watcher.start();
92
+ console.log('JSONL watcher started.');
93
+ }
94
+ catch (err) {
95
+ console.error('Watcher start error:', err);
96
+ }
97
+ // Rescue reindex uses runIndexer directly (not watcher.runNow) so /session/end
98
+ // gets deterministic execution instead of being dropped by watcher's single-
99
+ // flight guard when a scheduled scan is already inflight.
100
+ const server = createServer(db, {
101
+ rescueReindex: () => runIndexer(db),
102
+ });
103
+ server.listen(PORT, '127.0.0.1', () => {
104
+ console.log(`ccRecall listening on http://127.0.0.1:${PORT}`);
105
+ });
106
+ for (const signal of ['SIGINT', 'SIGTERM']) {
107
+ process.on(signal, () => {
108
+ console.log(`\nReceived ${signal}, shutting down...`);
109
+ coordinator.stop();
110
+ watcher.stop().catch(() => { });
111
+ db.close();
112
+ server.close(() => process.exit(0));
113
+ });
114
+ }
115
+ }
116
+ //# sourceMappingURL=index.js.map