@chude/memory 4.0.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 (184) hide show
  1. package/README.md +294 -0
  2. package/dist/application/index.d.ts +6 -0
  3. package/dist/application/services/ambient-context-service.d.ts +90 -0
  4. package/dist/application/services/backfill-service.d.ts +71 -0
  5. package/dist/application/services/budget-allocator.d.ts +57 -0
  6. package/dist/application/services/embedding-service.d.ts +131 -0
  7. package/dist/application/services/export-service.d.ts +225 -0
  8. package/dist/application/services/extraction-pipeline.d.ts +50 -0
  9. package/dist/application/services/friction-service.d.ts +148 -0
  10. package/dist/application/services/fts-sanitizer.d.ts +25 -0
  11. package/dist/application/services/index.d.ts +34 -0
  12. package/dist/application/services/llm-extractor.d.ts +96 -0
  13. package/dist/application/services/memory-file-sync-service.d.ts +58 -0
  14. package/dist/application/services/pattern-extractor.d.ts +95 -0
  15. package/dist/application/services/recovery-service.d.ts +81 -0
  16. package/dist/application/services/rrf-fusion.d.ts +53 -0
  17. package/dist/application/services/smart-context-service.d.ts +126 -0
  18. package/dist/application/services/sync-service.d.ts +157 -0
  19. package/dist/application/services/temporal-decay.d.ts +62 -0
  20. package/dist/domain/entities/backfill-state.d.ts +56 -0
  21. package/dist/domain/entities/entity.d.ts +131 -0
  22. package/dist/domain/entities/extraction-state.d.ts +128 -0
  23. package/dist/domain/entities/fact.d.ts +59 -0
  24. package/dist/domain/entities/friction-entry.d.ts +84 -0
  25. package/dist/domain/entities/index.d.ts +15 -0
  26. package/dist/domain/entities/link.d.ts +74 -0
  27. package/dist/domain/entities/memory-file.d.ts +78 -0
  28. package/dist/domain/entities/message.d.ts +70 -0
  29. package/dist/domain/entities/session.d.ts +93 -0
  30. package/dist/domain/entities/tool-use.d.ts +85 -0
  31. package/dist/domain/errors/error-codes.d.ts +37 -0
  32. package/dist/domain/errors/index.d.ts +8 -0
  33. package/dist/domain/errors/memory-error.d.ts +52 -0
  34. package/dist/domain/errors/unknown-error.d.ts +9 -0
  35. package/dist/domain/index.d.ts +11 -0
  36. package/dist/domain/ports/embedding.d.ts +96 -0
  37. package/dist/domain/ports/extraction.d.ts +13 -0
  38. package/dist/domain/ports/index.d.ts +14 -0
  39. package/dist/domain/ports/redactor.d.ts +17 -0
  40. package/dist/domain/ports/repositories.d.ts +658 -0
  41. package/dist/domain/ports/services.d.ts +180 -0
  42. package/dist/domain/ports/signals.d.ts +82 -0
  43. package/dist/domain/ports/sources.d.ts +122 -0
  44. package/dist/domain/ports/types.d.ts +150 -0
  45. package/dist/domain/services/content-extractor.d.ts +61 -0
  46. package/dist/domain/services/index.d.ts +8 -0
  47. package/dist/domain/services/path-decoder.d.ts +56 -0
  48. package/dist/domain/services/query-parser.d.ts +47 -0
  49. package/dist/domain/value-objects/embedding-config.d.ts +56 -0
  50. package/dist/domain/value-objects/embedding-result.d.ts +46 -0
  51. package/dist/domain/value-objects/index.d.ts +10 -0
  52. package/dist/domain/value-objects/project-path.d.ts +92 -0
  53. package/dist/domain/value-objects/search-query.d.ts +30 -0
  54. package/dist/domain/value-objects/search-result.d.ts +92 -0
  55. package/dist/index.d.ts +15 -0
  56. package/dist/index.js +1548 -0
  57. package/dist/infrastructure/database/connection.d.ts +161 -0
  58. package/dist/infrastructure/database/event-log.d.ts +22 -0
  59. package/dist/infrastructure/database/health-checker.d.ts +248 -0
  60. package/dist/infrastructure/database/index.d.ts +11 -0
  61. package/dist/infrastructure/database/repositories/backfill-state-repository.d.ts +19 -0
  62. package/dist/infrastructure/database/repositories/embedding-repository.d.ts +121 -0
  63. package/dist/infrastructure/database/repositories/entity-repository.d.ts +73 -0
  64. package/dist/infrastructure/database/repositories/extraction-log-repository.d.ts +17 -0
  65. package/dist/infrastructure/database/repositories/extraction-state-repository.d.ts +52 -0
  66. package/dist/infrastructure/database/repositories/fact-repository.d.ts +25 -0
  67. package/dist/infrastructure/database/repositories/friction-repository.d.ts +41 -0
  68. package/dist/infrastructure/database/repositories/index.d.ts +17 -0
  69. package/dist/infrastructure/database/repositories/link-repository.d.ts +64 -0
  70. package/dist/infrastructure/database/repositories/memory-file-repository.d.ts +28 -0
  71. package/dist/infrastructure/database/repositories/message-repository.d.ts +87 -0
  72. package/dist/infrastructure/database/repositories/session-repository.d.ts +125 -0
  73. package/dist/infrastructure/database/repositories/tool-use-repository.d.ts +72 -0
  74. package/dist/infrastructure/database/schema.d.ts +203 -0
  75. package/dist/infrastructure/database/services/context-service.d.ts +93 -0
  76. package/dist/infrastructure/database/services/hybrid-search-service.d.ts +156 -0
  77. package/dist/infrastructure/database/services/index.d.ts +10 -0
  78. package/dist/infrastructure/database/services/search-service.d.ts +57 -0
  79. package/dist/infrastructure/database/services/stats-service.d.ts +36 -0
  80. package/dist/infrastructure/embedding/background-embedder.d.ts +125 -0
  81. package/dist/infrastructure/embedding/embedding-provider-factory.d.ts +44 -0
  82. package/dist/infrastructure/embedding/index.d.ts +5 -0
  83. package/dist/infrastructure/embedding/ollama-provider.d.ts +41 -0
  84. package/dist/infrastructure/embedding/openai-provider.d.ts +38 -0
  85. package/dist/infrastructure/embedding/transformers-js-provider.d.ts +34 -0
  86. package/dist/infrastructure/external/index.d.ts +7 -0
  87. package/dist/infrastructure/external/qmd-runner.d.ts +36 -0
  88. package/dist/infrastructure/hooks/auto-memory-writer.d.ts +52 -0
  89. package/dist/infrastructure/hooks/config-manager.d.ts +237 -0
  90. package/dist/infrastructure/hooks/git-syncer.d.ts +44 -0
  91. package/dist/infrastructure/hooks/hook-runner.d.ts +126 -0
  92. package/dist/infrastructure/hooks/index.d.ts +12 -0
  93. package/dist/infrastructure/hooks/log-writer.d.ts +106 -0
  94. package/dist/infrastructure/hooks/settings-manager.d.ts +163 -0
  95. package/dist/infrastructure/hooks/sync-hook-script.d.ts +83 -0
  96. package/dist/infrastructure/hooks/sync-logger-adapter.d.ts +17 -0
  97. package/dist/infrastructure/index.d.ts +11 -0
  98. package/dist/infrastructure/llm/anthropic-extractor.d.ts +20 -0
  99. package/dist/infrastructure/llm/claude-cli-extractor.d.ts +14 -0
  100. package/dist/infrastructure/llm/claude-summary-generator.d.ts +14 -0
  101. package/dist/infrastructure/llm/extraction-helper.d.ts +16 -0
  102. package/dist/infrastructure/llm/ollama-extractor.d.ts +20 -0
  103. package/dist/infrastructure/llm/openai-extractor.d.ts +23 -0
  104. package/dist/infrastructure/migration.d.ts +103 -0
  105. package/dist/infrastructure/parsers/event-classifier.d.ts +111 -0
  106. package/dist/infrastructure/parsers/index.d.ts +8 -0
  107. package/dist/infrastructure/parsers/jsonl-parser.d.ts +25 -0
  108. package/dist/infrastructure/parsers/timestamp.d.ts +18 -0
  109. package/dist/infrastructure/paths.d.ts +129 -0
  110. package/dist/infrastructure/providers/provider-defaults.d.ts +11 -0
  111. package/dist/infrastructure/providers/provider-registry.d.ts +28 -0
  112. package/dist/infrastructure/security/pattern-redactor.d.ts +6 -0
  113. package/dist/infrastructure/signals/adapters.d.ts +27 -0
  114. package/dist/infrastructure/signals/checkpoint-manager.d.ts +83 -0
  115. package/dist/infrastructure/signals/index.d.ts +8 -0
  116. package/dist/infrastructure/signals/signal-handler.d.ts +113 -0
  117. package/dist/infrastructure/sources/index.d.ts +8 -0
  118. package/dist/infrastructure/sources/memory-file-scanner.d.ts +23 -0
  119. package/dist/infrastructure/sources/project-name-resolver.d.ts +67 -0
  120. package/dist/infrastructure/sources/session-source.d.ts +70 -0
  121. package/dist/presentation/cli/command-result.d.ts +10 -0
  122. package/dist/presentation/cli/commands/_helpers/capture-json.d.ts +36 -0
  123. package/dist/presentation/cli/commands/_helpers/deprecation-warning.d.ts +41 -0
  124. package/dist/presentation/cli/commands/backfill.d.ts +56 -0
  125. package/dist/presentation/cli/commands/browse.d.ts +55 -0
  126. package/dist/presentation/cli/commands/completion.d.ts +61 -0
  127. package/dist/presentation/cli/commands/context.d.ts +53 -0
  128. package/dist/presentation/cli/commands/doctor.d.ts +55 -0
  129. package/dist/presentation/cli/commands/export.d.ts +36 -0
  130. package/dist/presentation/cli/commands/extract.d.ts +40 -0
  131. package/dist/presentation/cli/commands/facts.d.ts +17 -0
  132. package/dist/presentation/cli/commands/friction/dashboard.d.ts +18 -0
  133. package/dist/presentation/cli/commands/friction/index.d.ts +12 -0
  134. package/dist/presentation/cli/commands/friction/list.d.ts +12 -0
  135. package/dist/presentation/cli/commands/friction/log.d.ts +12 -0
  136. package/dist/presentation/cli/commands/friction/purge.d.ts +12 -0
  137. package/dist/presentation/cli/commands/friction/resolve.d.ts +12 -0
  138. package/dist/presentation/cli/commands/friction/types.d.ts +86 -0
  139. package/dist/presentation/cli/commands/friction/wontfix.d.ts +12 -0
  140. package/dist/presentation/cli/commands/import.d.ts +38 -0
  141. package/dist/presentation/cli/commands/index.d.ts +51 -0
  142. package/dist/presentation/cli/commands/install.d.ts +67 -0
  143. package/dist/presentation/cli/commands/list.d.ts +61 -0
  144. package/dist/presentation/cli/commands/migrate.d.ts +36 -0
  145. package/dist/presentation/cli/commands/purge.d.ts +100 -0
  146. package/dist/presentation/cli/commands/query.d.ts +51 -0
  147. package/dist/presentation/cli/commands/related.d.ts +47 -0
  148. package/dist/presentation/cli/commands/remote.d.ts +36 -0
  149. package/dist/presentation/cli/commands/search.d.ts +100 -0
  150. package/dist/presentation/cli/commands/show.d.ts +51 -0
  151. package/dist/presentation/cli/commands/stats.d.ts +38 -0
  152. package/dist/presentation/cli/commands/status.d.ts +152 -0
  153. package/dist/presentation/cli/commands/sync/ambient.d.ts +22 -0
  154. package/dist/presentation/cli/commands/sync/background.d.ts +23 -0
  155. package/dist/presentation/cli/commands/sync/embedding-pass.d.ts +32 -0
  156. package/dist/presentation/cli/commands/sync/helpers.d.ts +25 -0
  157. package/dist/presentation/cli/commands/sync/index.d.ts +17 -0
  158. package/dist/presentation/cli/commands/sync/memory-files.d.ts +26 -0
  159. package/dist/presentation/cli/commands/sync/types.d.ts +163 -0
  160. package/dist/presentation/cli/commands/uninstall.d.ts +44 -0
  161. package/dist/presentation/cli/db-startup.d.ts +61 -0
  162. package/dist/presentation/cli/formatters/ai-formatter.d.ts +38 -0
  163. package/dist/presentation/cli/formatters/color.d.ts +82 -0
  164. package/dist/presentation/cli/formatters/context-formatter.d.ts +55 -0
  165. package/dist/presentation/cli/formatters/dto-helpers.d.ts +176 -0
  166. package/dist/presentation/cli/formatters/envelope.d.ts +136 -0
  167. package/dist/presentation/cli/formatters/error-formatter.d.ts +41 -0
  168. package/dist/presentation/cli/formatters/friction-dashboard.d.ts +46 -0
  169. package/dist/presentation/cli/formatters/index.d.ts +17 -0
  170. package/dist/presentation/cli/formatters/list-formatter.d.ts +48 -0
  171. package/dist/presentation/cli/formatters/output-formatter.d.ts +98 -0
  172. package/dist/presentation/cli/formatters/related-formatter.d.ts +57 -0
  173. package/dist/presentation/cli/formatters/show-formatter.d.ts +63 -0
  174. package/dist/presentation/cli/formatters/stats-formatter.d.ts +54 -0
  175. package/dist/presentation/cli/formatters/text-width.d.ts +37 -0
  176. package/dist/presentation/cli/formatters/timestamp-formatter.d.ts +38 -0
  177. package/dist/presentation/cli/index.d.ts +10 -0
  178. package/dist/presentation/cli/index.js +1664 -0
  179. package/dist/presentation/cli/parsers/date-parser.d.ts +28 -0
  180. package/dist/presentation/cli/parsers/index.d.ts +6 -0
  181. package/dist/presentation/cli/pickers/index.d.ts +6 -0
  182. package/dist/presentation/cli/pickers/session-picker.d.ts +59 -0
  183. package/dist/presentation/cli/progress-reporter.d.ts +199 -0
  184. package/package.json +94 -0
package/README.md ADDED
@@ -0,0 +1,294 @@
1
+ # @chude/memory
2
+
3
+ Cross-project context persistence for Claude Code sessions.
4
+
5
+ ## Problem
6
+
7
+ Claude Code sessions are per-directory and deleted after 30 days. Context does not transfer between projects. Knowledge gained in one project is invisible to work in another.
8
+
9
+ ## Solution
10
+
11
+ Extract session JSONL files into a searchable SQLite database accessible from any project via the `memory` CLI.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ bun add -g @chude/memory
17
+ ```
18
+
19
+ ## Setup
20
+
21
+ Install Claude Code hooks for automatic session sync:
22
+
23
+ ```bash
24
+ memory install
25
+ ```
26
+
27
+ Verify installation:
28
+
29
+ ```bash
30
+ memory doctor
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ ```bash
36
+ # Sync sessions to database
37
+ memory sync
38
+
39
+ # Search across all sessions
40
+ memory search "authentication patterns"
41
+
42
+ # Get context for a specific project
43
+ memory context wow-system
44
+
45
+ # List recent sessions
46
+ memory list
47
+
48
+ # Show session details
49
+ memory show <session-id>
50
+
51
+ # Find related sessions
52
+ memory related <session-id>
53
+
54
+ # Browse sessions interactively
55
+ memory browse
56
+ ```
57
+
58
+ ## How It Works
59
+
60
+ 1. Claude Code stores sessions as JSONL files in `~/.claude/projects/`
61
+ 2. `memory sync` extracts messages, topics, and entities into a SQLite database with FTS5
62
+ 3. Claude Code hooks trigger background sync automatically on session end
63
+ 4. `memory search` and `memory context` query the database from any project directory
64
+
65
+ ## Data Paths
66
+
67
+ | Purpose | Path | Override |
68
+ |---------|------|----------|
69
+ | Config | `~/.config/memory/config.json` | `XDG_CONFIG_HOME` |
70
+ | Database | `~/.local/share/memory/memory.db` | `XDG_DATA_HOME` |
71
+ | Logs | `~/.local/share/memory/logs/` | `XDG_DATA_HOME` |
72
+ | Legacy memory files | `~/.memory/` | `MEMORY_HOME` |
73
+
74
+ Tool-managed paths follow the XDG Base Directory Specification. Override with `XDG_CONFIG_HOME` and `XDG_DATA_HOME`.
75
+
76
+ Legacy memory files are retained for compatibility with pre-v4 markdown sidecars. They are not read or written by default. To index them during sync, use `memory sync --include-memory-files`, set `MEMORY_LEGACY_MEMORY_FILES=1`, or set `legacyMemoryFiles.enabled=true` in config. To generate legacy daily logs with `memory backfill`, pass `--write-memory-files` or use the same env/config opt-in. `MEMORY_HOME` follows the `GNUPGHOME` / `JAVA_HOME` tradition: the value is the exact directory path, not a base directory under which a subdirectory is appended. Empty string is ignored; no `~` expansion.
77
+
78
+ ## Secret Handling
79
+
80
+ Remote provider credentials should come from environment injection, not plaintext config files. `embedding.apiKey` is retained only as deprecated compatibility input.
81
+
82
+ Use `apiKeyEnv` when a provider needs a runtime key:
83
+
84
+ ```json
85
+ {
86
+ "embedding": {
87
+ "provider": "openai",
88
+ "apiKeyEnv": "OPENAI_API_KEY"
89
+ }
90
+ }
91
+ ```
92
+
93
+ Use `apiKeyRef` only as opaque metadata for an external secret manager:
94
+
95
+ ```json
96
+ {
97
+ "embedding": {
98
+ "provider": "openai",
99
+ "apiKeyRef": "authkey://memory/openai-api-key"
100
+ }
101
+ }
102
+ ```
103
+
104
+ `memory` never resolves `apiKeyRef` to a raw secret and never calls `authkey get`. If you use authkey, inject secrets into the child process:
105
+
106
+ ```bash
107
+ authkey run --env memory -- memory sync --embed
108
+ authkey run --env memory -- memory extract memory-nexus
109
+ ```
110
+
111
+ Known secret patterns in newly synced transcript content, tool inputs/results, extraction payloads, embeddings, and CLI JSON exports are redacted before storage or provider egress. Raw JSON backups require explicit opt-in:
112
+
113
+ ```bash
114
+ memory export backup.json --include-sensitive
115
+ ```
116
+
117
+ ## Provider Support
118
+
119
+ Provider support is explicit and registry-backed. Built-in embedding providers are `local`, `openai`, `ollama`, and `openai-compatible`. Built-in extraction providers are `claude-cli`, `anthropic`, `openai`, `ollama`, and `openai-compatible`.
120
+
121
+ Use `openai-compatible` for gateways or providers that expose OpenAI-compatible APIs:
122
+
123
+ ```json
124
+ {
125
+ "embedding": {
126
+ "provider": "openai-compatible",
127
+ "baseUrl": "https://gateway.example.com/v1",
128
+ "apiKeyEnv": "MEMORY_GATEWAY_API_KEY"
129
+ }
130
+ }
131
+ ```
132
+
133
+ `LLM_PROVIDER` selects the extraction provider. `LLM_MODEL` overrides the extraction model without changing embedding configuration.
134
+
135
+ ## AI-First Design
136
+
137
+ This tool is designed for Claude to use via the Bash tool:
138
+
139
+ ```bash
140
+ # Claude runs these commands to access cross-project knowledge
141
+ memory context <project-name>
142
+ memory search "query" --limit 5
143
+ ```
144
+
145
+ Standard CLI output works for both humans and AI agents.
146
+
147
+ ## Programmatic API
148
+
149
+ Install as a dependency:
150
+
151
+ ```bash
152
+ bun add @chude/memory
153
+ ```
154
+
155
+ Import and call execute functions:
156
+
157
+ ```typescript
158
+ import {
159
+ executeSyncCommand,
160
+ executeSearchCommand,
161
+ executeContextCommand,
162
+ type CommandResult,
163
+ type SyncCommandOptions,
164
+ type SearchCommandOptions,
165
+ type SearchMode,
166
+ } from "@chude/memory";
167
+
168
+ // Sync sessions to database
169
+ const syncResult = await executeSyncCommand({ quiet: true });
170
+ // syncResult: { exitCode: 0 }
171
+
172
+ // Search sessions
173
+ const searchResult = await executeSearchCommand("authentication patterns", {
174
+ limit: "5",
175
+ json: true,
176
+ });
177
+
178
+ // Get project context
179
+ const contextResult = await executeContextCommand("my-project", {
180
+ json: true,
181
+ days: 7,
182
+ });
183
+ ```
184
+
185
+ ### Exported Functions
186
+
187
+ | Function | Parameters | Returns |
188
+ |----------|------------|---------|
189
+ | `executeSyncCommand` | `options: SyncCommandOptions` | `Promise<CommandResult>` |
190
+ | `executeSearchCommand` | `query: string, options: SearchCommandOptions` | `Promise<CommandResult>` |
191
+ | `executeListCommand` | `options: ListCommandOptions` | `Promise<CommandResult>` |
192
+ | `executeStatsCommand` | `options: StatsCommandOptions` | `Promise<CommandResult>` |
193
+ | `executeContextCommand` | `project: string, options: ContextCommandOptions` | `Promise<CommandResult>` |
194
+ | `executeRelatedCommand` | `sessionId: string, options: RelatedCommandOptions` | `Promise<CommandResult>` |
195
+ | `executeShowCommand` | `sessionId: string, options: ShowCommandOptions` | `Promise<CommandResult>` |
196
+ | `executeBrowseCommand` | `options: BrowseCommandOptions` | `Promise<CommandResult>` |
197
+ | `executeInstallCommand` | `options: InstallOptions` | `Promise<CommandResult>` |
198
+ | `executeUninstallCommand` | `options: UninstallOptions` | `Promise<CommandResult>` |
199
+ | `executeStatusCommand` | `options: StatusOptions` | `Promise<CommandResult>` |
200
+ | `executeDoctorCommand` | `options: DoctorOptions` | `Promise<CommandResult>` |
201
+ | `executePurgeCommand` | `options: PurgeCommandOptions` | `Promise<CommandResult>` |
202
+ | `executeExportCommand` | `outputPath: string, options: ExportOptions` | `Promise<CommandResult>` |
203
+ | `executeImportCommand` | `inputPath: string, options: ImportOptions` | `Promise<CommandResult>` |
204
+ | `executeCompletionCommand` | `shell: string` | `CommandResult` |
205
+
206
+ ### CommandResult
207
+
208
+ ```typescript
209
+ interface CommandResult {
210
+ exitCode: number; // 0 = success, 1 = error/not found
211
+ }
212
+ ```
213
+
214
+ All functions handle their own database initialization and teardown. They never call `process.exit()`.
215
+
216
+ ### Domain Types
217
+
218
+ The following domain types are exported for TypeScript consumers who need typed search and stats operations:
219
+
220
+ | Type | Description |
221
+ |------|-------------|
222
+ | `SearchMode` | Union type: `"auto" \| "fts" \| "vector" \| "hybrid"`. Controls search strategy. |
223
+ | `HybridSearchOptions` | Extends `SearchOptions` with `mode` and `noDecay` fields for hybrid search. |
224
+ | `IStatsService` | Port interface for database statistics queries. |
225
+ | `StatsResult` | Return type from `IStatsService.getStats()`: session/message/tool-use totals, database size, and per-project breakdown. |
226
+ | `ProjectStats` | Per-project statistics: `projectName`, `sessionCount`, `messageCount`. |
227
+
228
+ ```typescript
229
+ import type {
230
+ SearchMode,
231
+ HybridSearchOptions,
232
+ IStatsService,
233
+ StatsResult,
234
+ ProjectStats,
235
+ } from "@chude/memory";
236
+
237
+ // Typed search options
238
+ const opts: HybridSearchOptions = {
239
+ mode: "hybrid" satisfies SearchMode,
240
+ limit: 10,
241
+ };
242
+
243
+ // Typed stats result
244
+ function processStats(stats: StatsResult): void {
245
+ console.log(`${stats.totalSessions} sessions, ${stats.totalMessages} messages`);
246
+ stats.projectBreakdown.forEach((p: ProjectStats) => {
247
+ console.log(` ${p.projectName}: ${p.messageCount} messages`);
248
+ });
249
+ }
250
+ ```
251
+
252
+ ## Previously Published As
253
+
254
+ This package was previously published as `memory-nexus`. The old package name now installs a deprecation stub. See [MIGRATION.md](MIGRATION.md) for upgrade instructions.
255
+
256
+ ## Development
257
+
258
+ ### Quality gates
259
+
260
+ ```bash
261
+ bun run typecheck
262
+ bun run build
263
+ bun test --timeout 15000
264
+ bun run test:isolation
265
+ bun run test:coverage
266
+ bun audit
267
+ ```
268
+
269
+ `bun run quality` runs the release gate sequence. `bun run test:coverage` uses an Istanbul-backed Bun harness and is intentionally strict: statements, branches, functions, and lines must each be available and at least 95%. Missing metrics fail the gate.
270
+
271
+ ### Running tests on Windows
272
+
273
+ Current 2026-05-28 verification has `bun test --timeout 15000` passing on Windows 11 with Bun 1.3.5. A previous full-suite run crashed with Bun's `panic(main thread): integer overflow` signature at ~6.8GB peak memory pressure; keep the subdirectory workaround available if that upstream runtime crash returns.
274
+
275
+ Fallback workaround: run the suite by subdirectory.
276
+
277
+ ```bash
278
+ bun test src/infrastructure/
279
+ bun test src/presentation/
280
+ bun test src/application src/domain
281
+ bun test tests/helpers tests/generators tests/infrastructure tests/integration tests/smoke
282
+ ```
283
+
284
+ Or run a single file directly:
285
+
286
+ ```bash
287
+ bun test src/path/to/file.test.ts
288
+ ```
289
+
290
+ Linux and macOS contributors should run the full suite normally.
291
+
292
+ ## License
293
+
294
+ MIT
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Application Layer
3
+ *
4
+ * Use cases and application services that orchestrate domain logic.
5
+ */
6
+ export * from "./services/index.js";
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Ambient Context Service
3
+ *
4
+ * Application-layer service that composes SmartContextService and
5
+ * IAmbientContextWriter to generate ambient context files for
6
+ * Claude Code's auto memory directory.
7
+ *
8
+ * After a sync, this service:
9
+ * 1. Queries SmartContextService for structured project context
10
+ * 2. Formats context.md via an injected formatter
11
+ * 3. Builds a concise MEMORY.md summary block with counts
12
+ * 4. Writes both artifacts via IAmbientContextWriter
13
+ *
14
+ * Dependencies are injected via constructor (hexagonal architecture).
15
+ * Zero imports from infrastructure or presentation layers.
16
+ */
17
+ import type { IAmbientContextWriter } from "../../domain/ports/services.js";
18
+ import type { SmartContextService, SmartContextResult } from "./smart-context-service.js";
19
+ /**
20
+ * Options for ambient context generation.
21
+ */
22
+ export interface AmbientContextOptions {
23
+ /** Human-readable project name for SmartContextService lookup */
24
+ projectName: string;
25
+ /** Path to the auto memory directory (e.g., ~/.claude/projects/<encoded>/memory/) */
26
+ autoMemoryDir: string;
27
+ /** Token budget for context.md (from config) */
28
+ budget: number;
29
+ }
30
+ /**
31
+ * Result of ambient context generation.
32
+ */
33
+ export interface AmbientContextResult {
34
+ /** Whether context was successfully generated */
35
+ success: boolean;
36
+ /** Reason for failure (only when success=false) */
37
+ reason?: "project-not-found" | "no-context" | "error";
38
+ /** Estimated token count of context.md content */
39
+ contextTokens?: number;
40
+ }
41
+ /**
42
+ * Structural type for the formatter dependency.
43
+ * Accepts any object with a formatSmartContext method.
44
+ * This avoids importing from the presentation layer.
45
+ */
46
+ interface SmartContextFormatter {
47
+ formatSmartContext(result: SmartContextResult): string;
48
+ }
49
+ /**
50
+ * Ambient Context Service.
51
+ *
52
+ * Orchestrates SmartContextService, a formatter, and IAmbientContextWriter
53
+ * to produce context.md and update MEMORY.md in the auto memory directory.
54
+ */
55
+ export declare class AmbientContextService {
56
+ private readonly smartContext;
57
+ private readonly contextWriter;
58
+ private readonly formatter;
59
+ constructor(smartContext: SmartContextService, contextWriter: IAmbientContextWriter, formatter: SmartContextFormatter);
60
+ /**
61
+ * Generate ambient context for the given project.
62
+ *
63
+ * Queries SmartContextService for structured context, formats it
64
+ * for context.md, builds a summary block for MEMORY.md, and
65
+ * writes both artifacts.
66
+ *
67
+ * @param options Generation options
68
+ * @returns Result indicating success or failure with reason
69
+ */
70
+ generateAmbientContext(options: AmbientContextOptions): Promise<AmbientContextResult>;
71
+ /**
72
+ * Build a concise summary block for MEMORY.md.
73
+ *
74
+ * Extracts counts from SmartContextResult sections and produces
75
+ * a block under 10 lines matching the CONTEXT.md spec.
76
+ *
77
+ * @param result SmartContextResult with sections
78
+ * @returns Formatted summary block content
79
+ */
80
+ private buildSummaryBlock;
81
+ /**
82
+ * Count non-empty lines in a section by key.
83
+ *
84
+ * @param sections Context sections from SmartContextResult
85
+ * @param key Section key to look for
86
+ * @returns Number of non-empty lines, or 0 if section not found
87
+ */
88
+ private countSectionLines;
89
+ }
90
+ export {};
@@ -0,0 +1,71 @@
1
+ /**
2
+ * BackfillService
3
+ *
4
+ * Application service that orchestrates session backfilling:
5
+ * 1. Query sessions without backfill state (unprocessed)
6
+ * 2. Extract content from messages
7
+ * 3. Generate structured summary via ISummaryGenerator
8
+ * 4. Write daily log file via IDailyLogWriter
9
+ * 5. Save BackfillState record for idempotency
10
+ *
11
+ * Depends on domain ports only. Infrastructure is injected via constructor.
12
+ */
13
+ import type { ISessionRepository } from "../../domain/ports/repositories.js";
14
+ import type { IBackfillStateRepository } from "../../domain/ports/repositories.js";
15
+ import type { IMessageRepository } from "../../domain/ports/repositories.js";
16
+ import type { ISummaryGenerator } from "../../domain/ports/index.js";
17
+ export interface BackfillProgress {
18
+ current: number;
19
+ total: number;
20
+ sessionId: string;
21
+ action: "processing" | "skipped" | "error";
22
+ }
23
+ export interface BackfillResult {
24
+ sessionsProcessed: number;
25
+ sessionsFailed: number;
26
+ sessionsSkipped: number;
27
+ dailyLogsCreated: number;
28
+ dailyLogsUpdated: number;
29
+ errors: Array<{
30
+ sessionId: string;
31
+ error: string;
32
+ }>;
33
+ }
34
+ export interface DryRunResult {
35
+ unprocessedCount: number;
36
+ estimatedCost: number;
37
+ }
38
+ export interface BackfillOptions {
39
+ batch?: number | undefined;
40
+ project?: string | undefined;
41
+ onProgress?: ((progress: BackfillProgress) => void) | undefined;
42
+ }
43
+ /**
44
+ * File writer abstraction for daily log files.
45
+ *
46
+ * Allows the service to write daily log files without importing
47
+ * filesystem modules directly. Infrastructure provides the implementation.
48
+ */
49
+ export interface IDailyLogWriter {
50
+ /**
51
+ * Write or append content to a daily log file.
52
+ * Creates parent directories if needed.
53
+ *
54
+ * @param datePath Date-based path relative to memory dir (e.g., "daily/2026-03-08.md")
55
+ * @param content Markdown content to write or append
56
+ * @returns Whether the file was created (true) or appended to (false)
57
+ */
58
+ writeOrAppend(datePath: string, content: string): Promise<boolean>;
59
+ }
60
+ export declare class BackfillService {
61
+ private readonly sessionRepo;
62
+ private readonly messageRepo;
63
+ private readonly backfillStateRepo;
64
+ private readonly summaryGenerator;
65
+ private readonly dailyLogWriter;
66
+ constructor(sessionRepo: ISessionRepository, messageRepo: IMessageRepository, backfillStateRepo: IBackfillStateRepository, summaryGenerator: ISummaryGenerator, dailyLogWriter: IDailyLogWriter);
67
+ dryRun(options?: Pick<BackfillOptions, "project">): Promise<DryRunResult>;
68
+ backfill(options?: BackfillOptions): Promise<BackfillResult>;
69
+ private getUnprocessedSessions;
70
+ private extractContent;
71
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Budget Allocator
3
+ *
4
+ * Pure function that distributes a token budget across prioritized sections.
5
+ * Sections are filled in priority order (1 = highest). Lower-priority sections
6
+ * are truncated first when budget is exceeded.
7
+ *
8
+ * Token estimation: ~4 characters per token for English text.
9
+ */
10
+ /**
11
+ * A section of content with a priority for budget allocation.
12
+ */
13
+ export interface BudgetSection {
14
+ /** Section identifier (e.g., "decisions", "learnings") */
15
+ key: string;
16
+ /** Priority (1 = highest, filled first) */
17
+ priority: number;
18
+ /** Section content text */
19
+ content: string;
20
+ }
21
+ /**
22
+ * A section after budget allocation, with truncation info.
23
+ */
24
+ export interface AllocatedSection extends BudgetSection {
25
+ /** Content after budget truncation (prefix of original) */
26
+ truncatedContent: string;
27
+ /** Estimated tokens allocated to this section */
28
+ allocated: number;
29
+ /** Whether this section was truncated */
30
+ truncated: boolean;
31
+ }
32
+ /**
33
+ * Result of budget allocation across sections.
34
+ */
35
+ export interface BudgetAllocationResult {
36
+ /** Sections with allocation details */
37
+ sections: AllocatedSection[];
38
+ /** Total estimated tokens used across all sections */
39
+ totalTokensUsed: number;
40
+ /** Whether the total content exceeded the budget */
41
+ budgetExceeded: boolean;
42
+ }
43
+ /**
44
+ * Distribute a token budget across prioritized sections.
45
+ *
46
+ * Sections are sorted by priority (1 = highest) and allocated budget
47
+ * in order. Lower-priority sections are truncated or dropped first.
48
+ *
49
+ * When totalBudget is 0 or negative, no constraint is applied and all
50
+ * sections are returned untruncated.
51
+ *
52
+ * @param sections Array of content sections to allocate budget to
53
+ * @param totalBudget Maximum tokens allowed (0 or negative = no limit)
54
+ * @param charsPerToken Characters per token heuristic (default: 4)
55
+ * @returns Allocation result with sections, token usage, and overflow flag
56
+ */
57
+ export declare function allocateBudget(sections: BudgetSection[], totalBudget: number, charsPerToken?: number): BudgetAllocationResult;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Embedding Service
3
+ *
4
+ * Application layer service that orchestrates the embedding pipeline:
5
+ * check model hash -> query unembedded -> batch embed -> store results.
6
+ *
7
+ * Does NOT own the IEmbeddingProvider lifecycle -- the caller creates
8
+ * and initializes the provider, then passes it in via constructor.
9
+ *
10
+ * Sits between the presentation layer (sync command) and infrastructure
11
+ * (repository + provider). Respects hexagonal architecture boundaries.
12
+ */
13
+ import type { IEmbeddingProvider } from "../../domain/ports/embedding.js";
14
+ import type { IRedactor } from "../../domain/ports/redactor.js";
15
+ import type { IEmbeddingRepository, EmbeddingServiceConfig } from "../../domain/ports/repositories.js";
16
+ /**
17
+ * Options for embedding operations.
18
+ */
19
+ export interface EmbedOptions {
20
+ /** Callback invoked after each batch completes */
21
+ onProgress?: (progress: EmbedProgress) => void;
22
+ }
23
+ /**
24
+ * Progress report after each embedding batch.
25
+ */
26
+ export interface EmbedProgress {
27
+ /** Number of messages embedded so far */
28
+ current: number;
29
+ /** Total number of messages to embed */
30
+ total: number;
31
+ }
32
+ /**
33
+ * Result of an embedding operation.
34
+ */
35
+ export interface EmbedResult {
36
+ /** Number of messages successfully embedded */
37
+ embedded: number;
38
+ /** Number of messages skipped */
39
+ skipped: number;
40
+ /** Total duration in milliseconds */
41
+ durationMs: number;
42
+ /** Embedding rate in messages per second */
43
+ rate: number;
44
+ }
45
+ /**
46
+ * Model state comparison result.
47
+ *
48
+ * Used to detect model changes and determine whether re-embedding
49
+ * is needed. Carries human-readable model names for user prompts.
50
+ */
51
+ export interface ModelState {
52
+ /** Whether the configured model differs from the stored model */
53
+ modelChanged: boolean;
54
+ /** Whether all embeddings need to be regenerated */
55
+ needsReEmbed: boolean;
56
+ /** Hash of the previously-used model (from embedding_state) */
57
+ storedHash?: string;
58
+ /** Hash of the currently-configured model */
59
+ currentHash: string;
60
+ /**
61
+ * Human-readable name of the previously-used model.
62
+ * Falls back to storedHash if the stored name is unavailable (legacy data).
63
+ */
64
+ storedModelName?: string;
65
+ /** Human-readable name of the currently-configured model (from config) */
66
+ currentModelName: string;
67
+ /** Number of existing embeddings (for re-embedding cost estimation) */
68
+ embeddedCount?: number;
69
+ }
70
+ /**
71
+ * Compute a model hash from embedding configuration.
72
+ *
73
+ * Generates a SHA-256 hash of the "provider:model:dimensions" string,
74
+ * truncated to 16 hex characters. Used for model change detection.
75
+ *
76
+ * @param config Embedding config with provider, model, and dimensions
77
+ * @returns 16-character hex hash string
78
+ */
79
+ export declare function computeModelHash(config: Pick<EmbeddingServiceConfig, "provider" | "model" | "dimensions">): string;
80
+ /**
81
+ * Application service for embedding orchestration.
82
+ *
83
+ * Manages the embedding lifecycle: model state detection, batch embedding,
84
+ * progress reporting, and re-embedding on model change. Dependencies are
85
+ * injected via constructor for testability.
86
+ */
87
+ export declare class EmbeddingService {
88
+ private readonly repository;
89
+ private readonly provider;
90
+ private readonly batchSize;
91
+ private readonly modelHash;
92
+ private readonly modelName;
93
+ private readonly redactor;
94
+ constructor(deps: {
95
+ repository: IEmbeddingRepository;
96
+ provider: IEmbeddingProvider;
97
+ config: EmbeddingServiceConfig;
98
+ redactor?: IRedactor;
99
+ });
100
+ /**
101
+ * Check whether the configured model matches the stored model.
102
+ *
103
+ * Compares the current model hash against what is stored in
104
+ * embedding_state. Returns model state with human-readable names
105
+ * for user-facing prompts.
106
+ *
107
+ * @returns Model state comparison result
108
+ */
109
+ checkModelState(): ModelState;
110
+ /**
111
+ * Embed all unembedded messages in batches.
112
+ *
113
+ * Queries for unembedded messages, sends them to the provider in
114
+ * batch-sized chunks, and stores the results. Calls onProgress
115
+ * after each batch completes.
116
+ *
117
+ * @param options Embedding options (progress callback)
118
+ * @returns Summary of the embedding operation
119
+ */
120
+ embedUnembedded(options?: EmbedOptions): Promise<EmbedResult>;
121
+ /**
122
+ * Clear all existing embeddings and re-embed everything.
123
+ *
124
+ * Used when the model has changed and all embeddings need
125
+ * to be regenerated. Clears first, then embeds.
126
+ *
127
+ * @param options Embedding options (progress callback)
128
+ * @returns Summary of the re-embedding operation
129
+ */
130
+ clearAndReembed(options?: EmbedOptions): Promise<EmbedResult>;
131
+ }