@clauderecallhq/cli 0.0.1 → 0.12.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 (279) hide show
  1. package/LICENSE +33 -0
  2. package/README.md +543 -3
  3. package/README.public.md +523 -0
  4. package/dist/cli.js +362 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/activate.js +69 -0
  7. package/dist/commands/activate.js.map +1 -0
  8. package/dist/commands/audit-secrets.js +103 -0
  9. package/dist/commands/audit-secrets.js.map +1 -0
  10. package/dist/commands/blame.js +35 -0
  11. package/dist/commands/blame.js.map +1 -0
  12. package/dist/commands/config-verification.js +18 -0
  13. package/dist/commands/config-verification.js.map +1 -0
  14. package/dist/commands/context.js +144 -0
  15. package/dist/commands/context.js.map +1 -0
  16. package/dist/commands/correlate.js +70 -0
  17. package/dist/commands/correlate.js.map +1 -0
  18. package/dist/commands/digest.js +78 -0
  19. package/dist/commands/digest.js.map +1 -0
  20. package/dist/commands/health.js +62 -0
  21. package/dist/commands/health.js.map +1 -0
  22. package/dist/commands/index.js +247 -0
  23. package/dist/commands/index.js.map +1 -0
  24. package/dist/commands/install-extension.js +138 -0
  25. package/dist/commands/install-extension.js.map +1 -0
  26. package/dist/commands/installs.js +128 -0
  27. package/dist/commands/installs.js.map +1 -0
  28. package/dist/commands/license.js +39 -0
  29. package/dist/commands/license.js.map +1 -0
  30. package/dist/commands/list.js +47 -0
  31. package/dist/commands/list.js.map +1 -0
  32. package/dist/commands/mcp.js +29 -0
  33. package/dist/commands/mcp.js.map +1 -0
  34. package/dist/commands/open.js +28 -0
  35. package/dist/commands/open.js.map +1 -0
  36. package/dist/commands/paste.js +154 -0
  37. package/dist/commands/paste.js.map +1 -0
  38. package/dist/commands/projects.js +36 -0
  39. package/dist/commands/projects.js.map +1 -0
  40. package/dist/commands/search.js +67 -0
  41. package/dist/commands/search.js.map +1 -0
  42. package/dist/commands/semantic.js +173 -0
  43. package/dist/commands/semantic.js.map +1 -0
  44. package/dist/commands/show.js +121 -0
  45. package/dist/commands/show.js.map +1 -0
  46. package/dist/commands/start.js +47 -0
  47. package/dist/commands/start.js.map +1 -0
  48. package/dist/commands/stats.js +133 -0
  49. package/dist/commands/stats.js.map +1 -0
  50. package/dist/commands/status.js +45 -0
  51. package/dist/commands/status.js.map +1 -0
  52. package/dist/commands/stop.js +29 -0
  53. package/dist/commands/stop.js.map +1 -0
  54. package/dist/commands/thread.js +396 -0
  55. package/dist/commands/thread.js.map +1 -0
  56. package/dist/context/formatter.js +103 -0
  57. package/dist/context/formatter.js.map +1 -0
  58. package/dist/daemon/auto-tag-config.js +103 -0
  59. package/dist/daemon/auto-tag-config.js.map +1 -0
  60. package/dist/daemon/auto-tag-config.test.js +72 -0
  61. package/dist/daemon/auto-tag-config.test.js.map +1 -0
  62. package/dist/daemon/auto-title-config.js +70 -0
  63. package/dist/daemon/auto-title-config.js.map +1 -0
  64. package/dist/daemon/bulk-title-jobs.js +170 -0
  65. package/dist/daemon/bulk-title-jobs.js.map +1 -0
  66. package/dist/daemon/correlator.js +320 -0
  67. package/dist/daemon/correlator.js.map +1 -0
  68. package/dist/daemon/discover.js +316 -0
  69. package/dist/daemon/discover.js.map +1 -0
  70. package/dist/daemon/editor-detection.js +186 -0
  71. package/dist/daemon/editor-detection.js.map +1 -0
  72. package/dist/daemon/entrypoint.js +55 -0
  73. package/dist/daemon/entrypoint.js.map +1 -0
  74. package/dist/daemon/git-correlator.js +256 -0
  75. package/dist/daemon/git-correlator.js.map +1 -0
  76. package/dist/daemon/mcp-installer.js +108 -0
  77. package/dist/daemon/mcp-installer.js.map +1 -0
  78. package/dist/daemon/onboarding-state.js +140 -0
  79. package/dist/daemon/onboarding-state.js.map +1 -0
  80. package/dist/daemon/pidfile.js +57 -0
  81. package/dist/daemon/pidfile.js.map +1 -0
  82. package/dist/daemon/ports.js +48 -0
  83. package/dist/daemon/ports.js.map +1 -0
  84. package/dist/daemon/scanProgressRegistry.js +62 -0
  85. package/dist/daemon/scanProgressRegistry.js.map +1 -0
  86. package/dist/daemon/server.js +2010 -0
  87. package/dist/daemon/server.js.map +1 -0
  88. package/dist/daemon/tag-scanner/anthropic-client.js +40 -0
  89. package/dist/daemon/tag-scanner/anthropic-client.js.map +1 -0
  90. package/dist/daemon/tag-scanner/autopilot.js +131 -0
  91. package/dist/daemon/tag-scanner/autopilot.js.map +1 -0
  92. package/dist/daemon/tag-scanner/claude-cli-driver.js +250 -0
  93. package/dist/daemon/tag-scanner/claude-cli-driver.js.map +1 -0
  94. package/dist/daemon/tag-scanner/orchestrator.js +88 -0
  95. package/dist/daemon/tag-scanner/orchestrator.js.map +1 -0
  96. package/dist/daemon/tag-scanner/prompt.js +46 -0
  97. package/dist/daemon/tag-scanner/prompt.js.map +1 -0
  98. package/dist/daemon/tag-scanner/prompt.test.js +48 -0
  99. package/dist/daemon/tag-scanner/prompt.test.js.map +1 -0
  100. package/dist/daemon/tag-scanner/scan-state.js +49 -0
  101. package/dist/daemon/tag-scanner/scan-state.js.map +1 -0
  102. package/dist/daemon/tag-scanner/session-fetcher.js +82 -0
  103. package/dist/daemon/tag-scanner/session-fetcher.js.map +1 -0
  104. package/dist/daemon/tag-scanner/session-fetcher.test.js +34 -0
  105. package/dist/daemon/tag-scanner/session-fetcher.test.js.map +1 -0
  106. package/dist/daemon/tag-scanner/validator.js +50 -0
  107. package/dist/daemon/tag-scanner/validator.js.map +1 -0
  108. package/dist/daemon/tag-scanner/validator.test.js +41 -0
  109. package/dist/daemon/tag-scanner/validator.test.js.map +1 -0
  110. package/dist/daemon/terminal-registry.js +443 -0
  111. package/dist/daemon/terminal-registry.js.map +1 -0
  112. package/dist/daemon/ui.js +64 -0
  113. package/dist/daemon/ui.js.map +1 -0
  114. package/dist/daemon/watcher.js +256 -0
  115. package/dist/daemon/watcher.js.map +1 -0
  116. package/dist/db/client.js +22 -0
  117. package/dist/db/client.js.map +1 -0
  118. package/dist/db/schema.js +496 -0
  119. package/dist/db/schema.js.map +1 -0
  120. package/dist/license/api-base.js +13 -0
  121. package/dist/license/api-base.js.map +1 -0
  122. package/dist/license/manager.js +43 -0
  123. package/dist/license/manager.js.map +1 -0
  124. package/dist/license/public-key.js +19 -0
  125. package/dist/license/public-key.js.map +1 -0
  126. package/dist/license/storage.js +27 -0
  127. package/dist/license/storage.js.map +1 -0
  128. package/dist/license/verify.js +23 -0
  129. package/dist/license/verify.js.map +1 -0
  130. package/dist/mcp/audit.js +126 -0
  131. package/dist/mcp/audit.js.map +1 -0
  132. package/dist/mcp/prompts.js +180 -0
  133. package/dist/mcp/prompts.js.map +1 -0
  134. package/dist/mcp/server.js +502 -0
  135. package/dist/mcp/server.js.map +1 -0
  136. package/dist/mcp/thread-tools.js +363 -0
  137. package/dist/mcp/thread-tools.js.map +1 -0
  138. package/dist/mcp/write-tools.js +239 -0
  139. package/dist/mcp/write-tools.js.map +1 -0
  140. package/dist/parser/jsonl.js +150 -0
  141. package/dist/parser/jsonl.js.map +1 -0
  142. package/dist/semantic/chunker.js +47 -0
  143. package/dist/semantic/chunker.js.map +1 -0
  144. package/dist/semantic/config.js +74 -0
  145. package/dist/semantic/config.js.map +1 -0
  146. package/dist/semantic/embedder.js +54 -0
  147. package/dist/semantic/embedder.js.map +1 -0
  148. package/dist/semantic/fusion.js +38 -0
  149. package/dist/semantic/fusion.js.map +1 -0
  150. package/dist/semantic/model-download.js +69 -0
  151. package/dist/semantic/model-download.js.map +1 -0
  152. package/dist/semantic/pipeline.js +375 -0
  153. package/dist/semantic/pipeline.js.map +1 -0
  154. package/dist/semantic/query.js +42 -0
  155. package/dist/semantic/query.js.map +1 -0
  156. package/dist/semantic/worker.js +78 -0
  157. package/dist/semantic/worker.js.map +1 -0
  158. package/dist/stats/backfill.js +151 -0
  159. package/dist/stats/backfill.js.map +1 -0
  160. package/dist/stats/health.js +102 -0
  161. package/dist/stats/health.js.map +1 -0
  162. package/dist/stats/query.js +385 -0
  163. package/dist/stats/query.js.map +1 -0
  164. package/dist/utils/aliases.js +107 -0
  165. package/dist/utils/aliases.js.map +1 -0
  166. package/dist/utils/autoCollections.js +635 -0
  167. package/dist/utils/autoCollections.js.map +1 -0
  168. package/dist/utils/autoTitle.js +348 -0
  169. package/dist/utils/autoTitle.js.map +1 -0
  170. package/dist/utils/collections.js +446 -0
  171. package/dist/utils/collections.js.map +1 -0
  172. package/dist/utils/format.js +46 -0
  173. package/dist/utils/format.js.map +1 -0
  174. package/dist/utils/notes.js +270 -0
  175. package/dist/utils/notes.js.map +1 -0
  176. package/dist/utils/paths.js +50 -0
  177. package/dist/utils/paths.js.map +1 -0
  178. package/dist/utils/pricing.js +257 -0
  179. package/dist/utils/pricing.js.map +1 -0
  180. package/dist/utils/secret-scanner.js +166 -0
  181. package/dist/utils/secret-scanner.js.map +1 -0
  182. package/dist/utils/sessionLabel.js +64 -0
  183. package/dist/utils/sessionLabel.js.map +1 -0
  184. package/dist/utils/tags.js +97 -0
  185. package/dist/utils/tags.js.map +1 -0
  186. package/dist/utils/thread-context.js +129 -0
  187. package/dist/utils/thread-context.js.map +1 -0
  188. package/dist/utils/threadFilter.js +18 -0
  189. package/dist/utils/threadFilter.js.map +1 -0
  190. package/dist/utils/threads-titler.js +298 -0
  191. package/dist/utils/threads-titler.js.map +1 -0
  192. package/dist/utils/threads.js +383 -0
  193. package/dist/utils/threads.js.map +1 -0
  194. package/dist/utils/usage.js +76 -0
  195. package/dist/utils/usage.js.map +1 -0
  196. package/dist/verification/compute.js +88 -0
  197. package/dist/verification/compute.js.map +1 -0
  198. package/dist/verification/config.js +34 -0
  199. package/dist/verification/config.js.map +1 -0
  200. package/dist/web/assets/index-CIr6J4Fw.js +1201 -0
  201. package/dist/web/assets/index-Ctc8g9Jw.css +1 -0
  202. package/dist/web/assets/inter-cyrillic-400-normal-HOLc17fK.woff +0 -0
  203. package/dist/web/assets/inter-cyrillic-400-normal-obahsSVq.woff2 +0 -0
  204. package/dist/web/assets/inter-cyrillic-500-normal-BasfLYem.woff2 +0 -0
  205. package/dist/web/assets/inter-cyrillic-500-normal-CxZf_p3X.woff +0 -0
  206. package/dist/web/assets/inter-cyrillic-600-normal-4D_pXhcN.woff +0 -0
  207. package/dist/web/assets/inter-cyrillic-600-normal-CWCymEST.woff2 +0 -0
  208. package/dist/web/assets/inter-cyrillic-700-normal-CjBOestx.woff2 +0 -0
  209. package/dist/web/assets/inter-cyrillic-700-normal-DrXBdSj3.woff +0 -0
  210. package/dist/web/assets/inter-cyrillic-ext-400-normal-BQZuk6qB.woff2 +0 -0
  211. package/dist/web/assets/inter-cyrillic-ext-400-normal-DQukG94-.woff +0 -0
  212. package/dist/web/assets/inter-cyrillic-ext-500-normal-B0yAr1jD.woff2 +0 -0
  213. package/dist/web/assets/inter-cyrillic-ext-500-normal-BmqWE9Dz.woff +0 -0
  214. package/dist/web/assets/inter-cyrillic-ext-600-normal-Bcila6Z-.woff +0 -0
  215. package/dist/web/assets/inter-cyrillic-ext-600-normal-Dfes3d0z.woff2 +0 -0
  216. package/dist/web/assets/inter-cyrillic-ext-700-normal-BjwYoWNd.woff2 +0 -0
  217. package/dist/web/assets/inter-cyrillic-ext-700-normal-LO58E6JB.woff +0 -0
  218. package/dist/web/assets/inter-greek-400-normal-B4URO6DV.woff2 +0 -0
  219. package/dist/web/assets/inter-greek-400-normal-q2sYcFCs.woff +0 -0
  220. package/dist/web/assets/inter-greek-500-normal-BIZE56-Y.woff2 +0 -0
  221. package/dist/web/assets/inter-greek-500-normal-Xzm54t5V.woff +0 -0
  222. package/dist/web/assets/inter-greek-600-normal-BZpKdvQh.woff +0 -0
  223. package/dist/web/assets/inter-greek-600-normal-plRanbMR.woff2 +0 -0
  224. package/dist/web/assets/inter-greek-700-normal-BUv2fZ6O.woff +0 -0
  225. package/dist/web/assets/inter-greek-700-normal-C3JjAnD8.woff2 +0 -0
  226. package/dist/web/assets/inter-greek-ext-400-normal-DGGRlc-M.woff2 +0 -0
  227. package/dist/web/assets/inter-greek-ext-400-normal-KugGGMne.woff +0 -0
  228. package/dist/web/assets/inter-greek-ext-500-normal-2j5mBUwD.woff +0 -0
  229. package/dist/web/assets/inter-greek-ext-500-normal-C4iEst2y.woff2 +0 -0
  230. package/dist/web/assets/inter-greek-ext-600-normal-B8X0CLgF.woff +0 -0
  231. package/dist/web/assets/inter-greek-ext-600-normal-DRtmH8MT.woff2 +0 -0
  232. package/dist/web/assets/inter-greek-ext-700-normal-BoQ6DsYi.woff +0 -0
  233. package/dist/web/assets/inter-greek-ext-700-normal-qfdV9bQt.woff2 +0 -0
  234. package/dist/web/assets/inter-latin-400-normal-C38fXH4l.woff2 +0 -0
  235. package/dist/web/assets/inter-latin-400-normal-CyCys3Eg.woff +0 -0
  236. package/dist/web/assets/inter-latin-500-normal-BL9OpVg8.woff +0 -0
  237. package/dist/web/assets/inter-latin-500-normal-Cerq10X2.woff2 +0 -0
  238. package/dist/web/assets/inter-latin-600-normal-CiBQ2DWP.woff +0 -0
  239. package/dist/web/assets/inter-latin-600-normal-LgqL8muc.woff2 +0 -0
  240. package/dist/web/assets/inter-latin-700-normal-BLAVimhd.woff +0 -0
  241. package/dist/web/assets/inter-latin-700-normal-Yt3aPRUw.woff2 +0 -0
  242. package/dist/web/assets/inter-latin-ext-400-normal-77YHD8bZ.woff +0 -0
  243. package/dist/web/assets/inter-latin-ext-400-normal-C1nco2VV.woff2 +0 -0
  244. package/dist/web/assets/inter-latin-ext-500-normal-BxGbmqWO.woff +0 -0
  245. package/dist/web/assets/inter-latin-ext-500-normal-CV4jyFjo.woff2 +0 -0
  246. package/dist/web/assets/inter-latin-ext-600-normal-CIVaiw4L.woff +0 -0
  247. package/dist/web/assets/inter-latin-ext-600-normal-D2bJ5OIk.woff2 +0 -0
  248. package/dist/web/assets/inter-latin-ext-700-normal-Ca8adRJv.woff2 +0 -0
  249. package/dist/web/assets/inter-latin-ext-700-normal-TidjK2hL.woff +0 -0
  250. package/dist/web/assets/inter-vietnamese-400-normal-Bbgyi5SW.woff +0 -0
  251. package/dist/web/assets/inter-vietnamese-400-normal-DMkecbls.woff2 +0 -0
  252. package/dist/web/assets/inter-vietnamese-500-normal-DOriooB6.woff2 +0 -0
  253. package/dist/web/assets/inter-vietnamese-500-normal-mJboJaSs.woff +0 -0
  254. package/dist/web/assets/inter-vietnamese-600-normal-BuLX-rYi.woff +0 -0
  255. package/dist/web/assets/inter-vietnamese-600-normal-Cc8MFFhd.woff2 +0 -0
  256. package/dist/web/assets/inter-vietnamese-700-normal-BZaoP0fm.woff +0 -0
  257. package/dist/web/assets/inter-vietnamese-700-normal-DlLaEgI2.woff2 +0 -0
  258. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
  259. package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
  260. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
  261. package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
  262. package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
  263. package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
  264. package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
  265. package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
  266. package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  267. package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  268. package/dist/web/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  269. package/dist/web/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  270. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  271. package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
  272. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  273. package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
  274. package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
  275. package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
  276. package/dist/web/favicon.svg +9 -0
  277. package/dist/web/index.html +15 -0
  278. package/package.json +80 -9
  279. package/bin/cli.js +0 -12
@@ -0,0 +1,103 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { z } from 'zod';
5
+ /**
6
+ * Auto-tag scanner config. Persisted as `{ autoTag: {...} }` inside
7
+ * `$RECALL_HOME/config.json` (default `~/.recall/config.json`).
8
+ *
9
+ * The file is written with mode 0600 because it may contain an Anthropic
10
+ * API key. Reads are tolerant: a missing or malformed file yields defaults
11
+ * rather than throwing — the daemon must start even if config is broken.
12
+ *
13
+ * Path resolution is lazy (re-reads `process.env.RECALL_HOME` on every call)
14
+ * so tests can redirect the data root per-test without a module-cache dance.
15
+ * `RECALL_HOME` in `src/utils/paths.ts` is captured at module-load and stays
16
+ * authoritative for every other consumer; this module intentionally uses its
17
+ * own lazy lookup so it stays independent and test-friendly.
18
+ */
19
+ function recallHome() {
20
+ return process.env.RECALL_HOME ?? join(homedir(), '.recall');
21
+ }
22
+ function ensureHome() {
23
+ const home = recallHome();
24
+ if (!existsSync(home))
25
+ mkdirSync(home, { recursive: true });
26
+ }
27
+ function configFile() {
28
+ return join(recallHome(), 'config.json');
29
+ }
30
+ export const AutoTagConfigSchema = z.object({
31
+ enabled: z.boolean().default(false),
32
+ backend: z.enum(['api', 'mcp']).default('api'),
33
+ apiKey: z.string().optional(),
34
+ model: z.string().default('claude-opus-4-7'),
35
+ maxTagsPerSession: z.number().int().min(1).max(10).default(4),
36
+ minTagsPerSession: z.number().int().min(1).max(10).default(2),
37
+ /**
38
+ * When true, daemon background-scans untagged sessions (existing + new)
39
+ * and applies tags automatically — no modal, no review. Only takes effect
40
+ * when backend='api' and an apiKey is set; MCP mode can't be driven by
41
+ * the daemon because it needs the user's agent to make tool calls.
42
+ */
43
+ autopilot: z.boolean().default(false),
44
+ });
45
+ const DEFAULTS = {
46
+ enabled: false,
47
+ backend: 'api',
48
+ model: 'claude-opus-4-7',
49
+ maxTagsPerSession: 4,
50
+ minTagsPerSession: 2,
51
+ autopilot: false,
52
+ };
53
+ function readFileShape() {
54
+ const path = configFile();
55
+ if (!existsSync(path))
56
+ return {};
57
+ try {
58
+ return JSON.parse(readFileSync(path, 'utf8'));
59
+ }
60
+ catch (err) {
61
+ console.error('[auto-tag-config] failed to parse config.json, using defaults:', err);
62
+ return {};
63
+ }
64
+ }
65
+ export function readAutoTagConfig() {
66
+ const raw = readFileShape().autoTag;
67
+ if (!raw)
68
+ return { ...DEFAULTS };
69
+ const parsed = AutoTagConfigSchema.safeParse({ ...DEFAULTS, ...raw });
70
+ return parsed.success ? parsed.data : { ...DEFAULTS };
71
+ }
72
+ export function writeAutoTagConfig(patch) {
73
+ ensureHome();
74
+ const existing = readFileShape();
75
+ const merged = AutoTagConfigSchema.parse({
76
+ ...DEFAULTS,
77
+ ...(existing.autoTag ?? {}),
78
+ ...patch,
79
+ });
80
+ const next = { ...existing, autoTag: merged };
81
+ const path = configFile();
82
+ writeFileSync(path, JSON.stringify(next, null, 2));
83
+ try {
84
+ chmodSync(path, 0o600);
85
+ }
86
+ catch (err) {
87
+ console.error('[auto-tag-config] chmod 0600 failed (continuing):', err);
88
+ }
89
+ return merged;
90
+ }
91
+ /**
92
+ * Shape for HTTP `GET /api/config/auto-tag`. Never exposes the raw apiKey over
93
+ * the wire; instead emits a marker and a `hasApiKey` boolean.
94
+ */
95
+ export function redactForApi(cfg) {
96
+ const { apiKey, ...rest } = cfg;
97
+ return {
98
+ ...rest,
99
+ apiKey: apiKey ? 'sk-ant-…' : null,
100
+ hasApiKey: Boolean(apiKey),
101
+ };
102
+ }
103
+ //# sourceMappingURL=auto-tag-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-tag-config.js","sourceRoot":"","sources":["../../src/daemon/auto-tag-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { describe, it, beforeEach, afterEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtempSync, rmSync, existsSync, statSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ describe('auto-tag-config', () => {
7
+ let tmpHome;
8
+ let prevHome;
9
+ beforeEach(() => {
10
+ tmpHome = mkdtempSync(join(tmpdir(), 'recall-cfg-'));
11
+ prevHome = process.env.RECALL_HOME;
12
+ process.env.RECALL_HOME = tmpHome;
13
+ });
14
+ afterEach(() => {
15
+ if (prevHome)
16
+ process.env.RECALL_HOME = prevHome;
17
+ else
18
+ delete process.env.RECALL_HOME;
19
+ rmSync(tmpHome, { recursive: true, force: true });
20
+ });
21
+ it('returns defaults when config file does not exist', async () => {
22
+ const { readAutoTagConfig } = await import('./auto-tag-config.js');
23
+ const cfg = readAutoTagConfig();
24
+ assert.equal(cfg.enabled, false);
25
+ assert.equal(cfg.backend, 'api');
26
+ assert.equal(cfg.model, 'claude-opus-4-7');
27
+ });
28
+ it('round-trips writes', async () => {
29
+ const { readAutoTagConfig, writeAutoTagConfig } = await import('./auto-tag-config.js');
30
+ writeAutoTagConfig({
31
+ enabled: true,
32
+ backend: 'api',
33
+ apiKey: 'sk-ant-test',
34
+ model: 'claude-opus-4-7',
35
+ maxTagsPerSession: 4,
36
+ minTagsPerSession: 2,
37
+ });
38
+ const got = readAutoTagConfig();
39
+ assert.equal(got.enabled, true);
40
+ assert.equal(got.apiKey, 'sk-ant-test');
41
+ });
42
+ it('writes with 0600 file mode', async () => {
43
+ const { writeAutoTagConfig } = await import('./auto-tag-config.js');
44
+ writeAutoTagConfig({
45
+ enabled: true,
46
+ backend: 'api',
47
+ apiKey: 'secret',
48
+ model: 'claude-opus-4-7',
49
+ maxTagsPerSession: 4,
50
+ minTagsPerSession: 2,
51
+ });
52
+ const path = join(tmpHome, 'config.json');
53
+ assert.equal(existsSync(path), true);
54
+ const mode = statSync(path).mode & 0o777;
55
+ assert.equal(mode, 0o600);
56
+ });
57
+ it('redacts apiKey via redactForApi', async () => {
58
+ const { redactForApi } = await import('./auto-tag-config.js');
59
+ const redacted = redactForApi({
60
+ enabled: true,
61
+ backend: 'api',
62
+ apiKey: 'sk-ant-XXXXX',
63
+ model: 'claude-opus-4-7',
64
+ maxTagsPerSession: 4,
65
+ minTagsPerSession: 2,
66
+ autopilot: false,
67
+ });
68
+ assert.equal(redacted.apiKey, 'sk-ant-…');
69
+ assert.equal(redacted.hasApiKey, true);
70
+ });
71
+ });
72
+ //# sourceMappingURL=auto-tag-config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-tag-config.test.js","sourceRoot":"","sources":["../../src/daemon/auto-tag-config.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { z } from 'zod';
5
+ /**
6
+ * v0.14b (T5) — auto-title config, persisted as `{ autoTitle: {...} }` inside
7
+ * `$RECALL_HOME/config.json` alongside the existing `autoTag` block.
8
+ *
9
+ * Two booleans:
10
+ * - `heuristicEnabled` (default true) — indexer-time heuristic pass.
11
+ * - `agentEnabled` (default false) — `claude -p` summarisation path.
12
+ * OFF by default because it spawns a subprocess and uses the user's
13
+ * subscription. The UI's ✨ Generate title button flips this per-click
14
+ * after an explicit confirmation, identical to the auto-tag opt-in.
15
+ *
16
+ * Path resolution mirrors `auto-tag-config.ts`: lazy re-read of
17
+ * `process.env.RECALL_HOME` on every call so tests can redirect the data
18
+ * root per-test without a module-cache dance.
19
+ */
20
+ function recallHome() {
21
+ return process.env.RECALL_HOME ?? join(homedir(), '.recall');
22
+ }
23
+ function ensureHome() {
24
+ const home = recallHome();
25
+ if (!existsSync(home))
26
+ mkdirSync(home, { recursive: true });
27
+ }
28
+ function configFile() {
29
+ return join(recallHome(), 'config.json');
30
+ }
31
+ export const AutoTitleConfigSchema = z.object({
32
+ heuristicEnabled: z.boolean().default(true),
33
+ agentEnabled: z.boolean().default(false),
34
+ });
35
+ const DEFAULTS = {
36
+ heuristicEnabled: true,
37
+ agentEnabled: false,
38
+ };
39
+ function readFileShape() {
40
+ const path = configFile();
41
+ if (!existsSync(path))
42
+ return {};
43
+ try {
44
+ return JSON.parse(readFileSync(path, 'utf8'));
45
+ }
46
+ catch (err) {
47
+ console.error('[auto-title-config] failed to parse config.json, using defaults:', err);
48
+ return {};
49
+ }
50
+ }
51
+ export function readAutoTitleConfig() {
52
+ const raw = readFileShape().autoTitle;
53
+ if (!raw)
54
+ return { ...DEFAULTS };
55
+ const parsed = AutoTitleConfigSchema.safeParse({ ...DEFAULTS, ...raw });
56
+ return parsed.success ? parsed.data : { ...DEFAULTS };
57
+ }
58
+ export function writeAutoTitleConfig(patch) {
59
+ ensureHome();
60
+ const existing = readFileShape();
61
+ const merged = AutoTitleConfigSchema.parse({
62
+ ...DEFAULTS,
63
+ ...(existing.autoTitle ?? {}),
64
+ ...patch,
65
+ });
66
+ const next = { ...existing, autoTitle: merged };
67
+ writeFileSync(configFile(), JSON.stringify(next, null, 2));
68
+ return merged;
69
+ }
70
+ //# sourceMappingURL=auto-title-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-title-config.js","sourceRoot":"","sources":["../../src/daemon/auto-title-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;GAcG;AAEH,SAAS,UAAU;IACjB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAC/D,CAAC;AAED,SAAS,UAAU;IACjB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,IAAI,CAAC,UAAU,EAAE,EAAE,aAAa,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3C,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;CACzC,CAAC,CAAC;AAIH,MAAM,QAAQ,GAAoB;IAChC,gBAAgB,EAAE,IAAI;IACtB,YAAY,EAAE,KAAK;CACpB,CAAC;AAOF,SAAS,aAAa;IACpB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAc,CAAC;IAC7D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,kEAAkE,EAAE,GAAG,CAAC,CAAC;QACvF,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC,SAAS,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC;IACxE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAA+B;IAClE,UAAU,EAAE,CAAC;IACb,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,qBAAqB,CAAC,KAAK,CAAC;QACzC,GAAG,QAAQ;QACX,GAAG,CAAC,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC;QAC7B,GAAG,KAAK;KACT,CAAC,CAAC;IACH,MAAM,IAAI,GAAc,EAAE,GAAG,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAC3D,aAAa,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3D,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,170 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { generateThreadTitles, } from '../utils/threads-titler.js';
3
+ const REGISTRY = new Map();
4
+ const TTL_AFTER_DONE_MS = 5 * 60 * 1000;
5
+ function pushEvent(state, kind, data) {
6
+ const id = state.events.length + 1;
7
+ state.events.push({ id, kind, data });
8
+ for (const w of state.waiters)
9
+ w.resolve();
10
+ state.waiters.clear();
11
+ }
12
+ function scheduleCleanup(state) {
13
+ if (state.cleanupTimer)
14
+ clearTimeout(state.cleanupTimer);
15
+ state.cleanupTimer = setTimeout(() => {
16
+ REGISTRY.delete(state.jobId);
17
+ }, TTL_AFTER_DONE_MS);
18
+ }
19
+ function snapshot(state) {
20
+ let done = 0;
21
+ let skipped = 0;
22
+ let failed = 0;
23
+ for (const e of state.events) {
24
+ if (e.kind === 'progress')
25
+ done += 1;
26
+ if (e.kind === 'skipped')
27
+ skipped += 1;
28
+ if (e.kind === 'error')
29
+ failed += 1;
30
+ }
31
+ return {
32
+ jobId: state.jobId,
33
+ threadId: state.threadId,
34
+ status: state.status,
35
+ startedAt: state.startedAt,
36
+ endedAt: state.endedAt,
37
+ total: state.total,
38
+ done,
39
+ skipped,
40
+ failed,
41
+ result: state.result,
42
+ };
43
+ }
44
+ /**
45
+ * Spawn a detached bulk-title walk. Returns the jobId immediately. The walk
46
+ * runs on the daemon's event loop and pushes progress events into the
47
+ * registry; SSE subscribers pull from there.
48
+ */
49
+ export function startBulkTitleJob(input) {
50
+ const jobId = randomUUID();
51
+ const controller = new AbortController();
52
+ const state = {
53
+ jobId,
54
+ threadId: input.threadId,
55
+ status: 'running',
56
+ startedAt: new Date().toISOString(),
57
+ endedAt: null,
58
+ total: 0,
59
+ events: [],
60
+ waiters: new Set(),
61
+ controller,
62
+ result: null,
63
+ cleanupTimer: null,
64
+ };
65
+ REGISTRY.set(jobId, state);
66
+ // Detached: the walk runs to completion or until cancelled. Errors thrown
67
+ // by generateThreadTitles itself (thread not found, fatal db error) end up
68
+ // as a synthetic 'done' event with failed entries so subscribers always
69
+ // get a terminal event.
70
+ //
71
+ // The leading `await Promise.resolve()` yields once so the caller has time
72
+ // to bind the returned jobId before the walk starts. Without it, the spawn
73
+ // override could fire (and call cancelJob with a stale jobId) inside the
74
+ // same sync stack as `startBulkTitleJob(...)`, which is what the cancel
75
+ // test exercises.
76
+ void (async () => {
77
+ await Promise.resolve();
78
+ try {
79
+ const result = await generateThreadTitles(input.threadId, {
80
+ force: input.force ?? false,
81
+ signal: controller.signal,
82
+ model: input.model,
83
+ onProgress: (ev) => {
84
+ state.total = ev.total;
85
+ pushEvent(state, 'progress', ev);
86
+ },
87
+ onSkipped: (ev) => {
88
+ pushEvent(state, 'skipped', ev);
89
+ },
90
+ onFailed: (ev) => {
91
+ pushEvent(state, 'error', ev);
92
+ },
93
+ });
94
+ state.result = result;
95
+ state.status = controller.signal.aborted ? 'cancelled' : 'done';
96
+ state.endedAt = new Date().toISOString();
97
+ pushEvent(state, 'done', result);
98
+ }
99
+ catch (err) {
100
+ const message = err instanceof Error ? err.message : String(err ?? 'unknown error');
101
+ state.result = {
102
+ generated: [],
103
+ skipped: [],
104
+ failed: [{ sessionId: input.threadId, error: message }],
105
+ };
106
+ state.status = 'failed';
107
+ state.endedAt = new Date().toISOString();
108
+ pushEvent(state, 'done', state.result);
109
+ }
110
+ finally {
111
+ scheduleCleanup(state);
112
+ }
113
+ })();
114
+ return jobId;
115
+ }
116
+ /**
117
+ * Async iterator over a job's events. Resumes from `afterId` if provided
118
+ * (use `Last-Event-ID` from the SSE protocol). Yields buffered events first,
119
+ * then awaits new ones until the job emits `done`. The consumer is expected
120
+ * to break on the `done` event; the iterator itself does not exit early so
121
+ * heartbeats can interleave.
122
+ */
123
+ export async function* subscribeJob(jobId, afterId = 0) {
124
+ const state = REGISTRY.get(jobId);
125
+ if (!state)
126
+ return;
127
+ let cursor = afterId;
128
+ while (true) {
129
+ while (cursor < state.events.length) {
130
+ const ev = state.events[cursor];
131
+ cursor += 1;
132
+ yield ev;
133
+ if (ev.kind === 'done')
134
+ return;
135
+ }
136
+ if (state.endedAt)
137
+ return;
138
+ await new Promise((resolve) => {
139
+ state.waiters.add({ resolve });
140
+ });
141
+ }
142
+ }
143
+ export function cancelJob(jobId) {
144
+ const state = REGISTRY.get(jobId);
145
+ if (!state)
146
+ return false;
147
+ if (state.status !== 'running')
148
+ return false;
149
+ state.controller.abort();
150
+ return true;
151
+ }
152
+ export function getJobSnapshot(jobId) {
153
+ const state = REGISTRY.get(jobId);
154
+ if (!state)
155
+ return null;
156
+ return snapshot(state);
157
+ }
158
+ /**
159
+ * Test-only helper: clear every job (useful between tests so registry
160
+ * residue from one test does not leak into the next). Not exported from a
161
+ * module index; tests reach in directly.
162
+ */
163
+ export function __resetForTests() {
164
+ for (const state of REGISTRY.values()) {
165
+ if (state.cleanupTimer)
166
+ clearTimeout(state.cleanupTimer);
167
+ }
168
+ REGISTRY.clear();
169
+ }
170
+ //# sourceMappingURL=bulk-title-jobs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-title-jobs.js","sourceRoot":"","sources":["../../src/daemon/bulk-title-jobs.ts"],"names":[],"mappings":"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"}