@malindar/whyline 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/.claude/settings.local.json +33 -0
  2. package/.github/workflows/ci.yml +35 -0
  3. package/.github/workflows/publish.yml +37 -0
  4. package/.prettierrc.json +7 -0
  5. package/CLAUDE.md +74 -0
  6. package/LICENSE +21 -0
  7. package/README.md +359 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +125 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/commands/delete.d.ts +3 -0
  12. package/dist/commands/delete.js +42 -0
  13. package/dist/commands/delete.js.map +1 -0
  14. package/dist/commands/doctor.d.ts +1 -0
  15. package/dist/commands/doctor.js +111 -0
  16. package/dist/commands/doctor.js.map +1 -0
  17. package/dist/commands/edit.d.ts +1 -0
  18. package/dist/commands/edit.js +78 -0
  19. package/dist/commands/edit.js.map +1 -0
  20. package/dist/commands/export.d.ts +8 -0
  21. package/dist/commands/export.js +90 -0
  22. package/dist/commands/export.js.map +1 -0
  23. package/dist/commands/import.d.ts +1 -0
  24. package/dist/commands/import.js +110 -0
  25. package/dist/commands/import.js.map +1 -0
  26. package/dist/commands/init.d.ts +5 -0
  27. package/dist/commands/init.js +23 -0
  28. package/dist/commands/init.js.map +1 -0
  29. package/dist/commands/install-claude.d.ts +3 -0
  30. package/dist/commands/install-claude.js +180 -0
  31. package/dist/commands/install-claude.js.map +1 -0
  32. package/dist/commands/list.d.ts +4 -0
  33. package/dist/commands/list.js +35 -0
  34. package/dist/commands/list.js.map +1 -0
  35. package/dist/commands/mcp.d.ts +1 -0
  36. package/dist/commands/mcp.js +10 -0
  37. package/dist/commands/mcp.js.map +1 -0
  38. package/dist/commands/save.d.ts +4 -0
  39. package/dist/commands/save.js +74 -0
  40. package/dist/commands/save.js.map +1 -0
  41. package/dist/commands/search.d.ts +7 -0
  42. package/dist/commands/search.js +46 -0
  43. package/dist/commands/search.js.map +1 -0
  44. package/dist/commands/show.d.ts +3 -0
  45. package/dist/commands/show.js +30 -0
  46. package/dist/commands/show.js.map +1 -0
  47. package/dist/commands/stats.d.ts +1 -0
  48. package/dist/commands/stats.js +27 -0
  49. package/dist/commands/stats.js.map +1 -0
  50. package/dist/commands/summarize.d.ts +3 -0
  51. package/dist/commands/summarize.js +140 -0
  52. package/dist/commands/summarize.js.map +1 -0
  53. package/dist/config.d.ts +11 -0
  54. package/dist/config.js +17 -0
  55. package/dist/config.js.map +1 -0
  56. package/dist/db/connection.d.ts +2 -0
  57. package/dist/db/connection.js +8 -0
  58. package/dist/db/connection.js.map +1 -0
  59. package/dist/db/migrations.d.ts +2 -0
  60. package/dist/db/migrations.js +19 -0
  61. package/dist/db/migrations.js.map +1 -0
  62. package/dist/db/schema.d.ts +5 -0
  63. package/dist/db/schema.js +64 -0
  64. package/dist/db/schema.js.map +1 -0
  65. package/dist/git/diff.d.ts +2 -0
  66. package/dist/git/diff.js +45 -0
  67. package/dist/git/diff.js.map +1 -0
  68. package/dist/git/git.d.ts +3 -0
  69. package/dist/git/git.js +25 -0
  70. package/dist/git/git.js.map +1 -0
  71. package/dist/git/repoId.d.ts +3 -0
  72. package/dist/git/repoId.js +49 -0
  73. package/dist/git/repoId.js.map +1 -0
  74. package/dist/mcp/server.d.ts +1 -0
  75. package/dist/mcp/server.js +296 -0
  76. package/dist/mcp/server.js.map +1 -0
  77. package/dist/mcp/tools.d.ts +119 -0
  78. package/dist/mcp/tools.js +43 -0
  79. package/dist/mcp/tools.js.map +1 -0
  80. package/dist/memory/parseSummary.d.ts +14 -0
  81. package/dist/memory/parseSummary.js +53 -0
  82. package/dist/memory/parseSummary.js.map +1 -0
  83. package/dist/memory/qualityCheck.d.ts +13 -0
  84. package/dist/memory/qualityCheck.js +78 -0
  85. package/dist/memory/qualityCheck.js.map +1 -0
  86. package/dist/memory/redactSecrets.d.ts +7 -0
  87. package/dist/memory/redactSecrets.js +29 -0
  88. package/dist/memory/redactSecrets.js.map +1 -0
  89. package/dist/memory/repoContext.d.ts +2 -0
  90. package/dist/memory/repoContext.js +23 -0
  91. package/dist/memory/repoContext.js.map +1 -0
  92. package/dist/memory/saveMemory.d.ts +40 -0
  93. package/dist/memory/saveMemory.js +223 -0
  94. package/dist/memory/saveMemory.js.map +1 -0
  95. package/dist/memory/searchMemory.d.ts +17 -0
  96. package/dist/memory/searchMemory.js +122 -0
  97. package/dist/memory/searchMemory.js.map +1 -0
  98. package/dist/memory/types.d.ts +48 -0
  99. package/dist/memory/types.js +2 -0
  100. package/dist/memory/types.js.map +1 -0
  101. package/dist/output/format.d.ts +3 -0
  102. package/dist/output/format.js +43 -0
  103. package/dist/output/format.js.map +1 -0
  104. package/docs/architecture.md +387 -0
  105. package/docs/ec6ab3bf-60cf-4629-ad9e-3048e8e3c43a.png +0 -0
  106. package/docs/logo.png +0 -0
  107. package/eslint.config.js +16 -0
  108. package/how-to-run/01-install.md +69 -0
  109. package/how-to-run/02-wire-up-your-repo.md +80 -0
  110. package/how-to-run/03-test-it-manually.md +91 -0
  111. package/how-to-run/04-test-with-claude-code.md +70 -0
  112. package/how-to-run/CLAUDE.md.template +72 -0
  113. package/how-to-run/README.md +49 -0
  114. package/package.json +60 -0
  115. package/src/cli.ts +142 -0
  116. package/src/commands/delete.ts +47 -0
  117. package/src/commands/doctor.ts +128 -0
  118. package/src/commands/edit.ts +80 -0
  119. package/src/commands/export.ts +95 -0
  120. package/src/commands/import.ts +119 -0
  121. package/src/commands/init.ts +31 -0
  122. package/src/commands/install-claude.ts +203 -0
  123. package/src/commands/list.ts +41 -0
  124. package/src/commands/mcp.ts +12 -0
  125. package/src/commands/save.ts +85 -0
  126. package/src/commands/search.ts +56 -0
  127. package/src/commands/show.ts +37 -0
  128. package/src/commands/stats.ts +31 -0
  129. package/src/commands/summarize.ts +183 -0
  130. package/src/config.ts +26 -0
  131. package/src/db/connection.ts +8 -0
  132. package/src/db/migrations.ts +26 -0
  133. package/src/db/schema.ts +68 -0
  134. package/src/git/diff.ts +43 -0
  135. package/src/git/git.ts +25 -0
  136. package/src/git/repoId.ts +49 -0
  137. package/src/hooks/post-commit.sample.sh +9 -0
  138. package/src/mcp/server.ts +326 -0
  139. package/src/mcp/tools.ts +53 -0
  140. package/src/memory/parseSummary.ts +72 -0
  141. package/src/memory/qualityCheck.ts +102 -0
  142. package/src/memory/redactSecrets.ts +32 -0
  143. package/src/memory/repoContext.ts +25 -0
  144. package/src/memory/saveMemory.ts +369 -0
  145. package/src/memory/searchMemory.ts +153 -0
  146. package/src/memory/types.ts +57 -0
  147. package/src/output/format.ts +44 -0
  148. package/src/skill/SKILL.md +95 -0
  149. package/tests/cliV02.test.ts +213 -0
  150. package/tests/doctor.test.ts +253 -0
  151. package/tests/exportImport.test.ts +248 -0
  152. package/tests/fileRename.test.ts +156 -0
  153. package/tests/gitHelpers.test.ts +94 -0
  154. package/tests/init.test.ts +93 -0
  155. package/tests/installClaude.test.ts +157 -0
  156. package/tests/parseSummary.test.ts +111 -0
  157. package/tests/qualityCheck.test.ts +182 -0
  158. package/tests/redactSecrets.test.ts +75 -0
  159. package/tests/saveMemory.test.ts +196 -0
  160. package/tests/searchFilters.test.ts +139 -0
  161. package/tests/searchMemory.test.ts +273 -0
  162. package/tests/stale.test.ts +47 -0
  163. package/tsconfig.json +18 -0
  164. package/vitest.config.ts +8 -0
@@ -0,0 +1,33 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm test *)",
5
+ "Bash(npm rebuild *)",
6
+ "Bash(node-gyp rebuild *)",
7
+ "Bash(npx node-gyp *)",
8
+ "Bash(npm install *)",
9
+ "Bash(node -e \"require\\('better-sqlite3'\\)\")",
10
+ "Bash(node -e \"require\\('better-sqlite3'\\); console.log\\('ok'\\)\")",
11
+ "Bash(npm prefix *)",
12
+ "Bash(npm run *)",
13
+ "Bash(node *)",
14
+ "Bash(git -C /Users/malinda.ranasinghe/Documents/contentful/personal/commit-gpt init)",
15
+ "Bash(git -C /Users/malinda.ranasinghe/Documents/contentful/personal/commit-gpt add .)",
16
+ "Bash(git -C /Users/malinda.ranasinghe/Documents/contentful/personal/commit-gpt commit -m \"chore: initial commit\")",
17
+ "Bash(GIT_COMMITTER_NAME=\"Test\" GIT_COMMITTER_EMAIL=\"test@test.com\" GIT_AUTHOR_NAME=\"Test\" GIT_AUTHOR_EMAIL=\"test@test.com\" git -C /Users/malinda.ranasinghe/Documents/contentful/personal/commit-gpt -c commit.gpgsign=false commit -m \"chore: initial commit\")",
18
+ "Bash(git -C /Users/malinda.ranasinghe/Documents/contentful/moi/gatekeeper log --oneline -5)",
19
+ "Bash(git -C /Users/malinda.ranasinghe/Documents/contentful/moi/gatekeeper remote get-url origin)",
20
+ "Read(//Users/malinda.ranasinghe/Documents/contentful/moi/gatekeeper/**)",
21
+ "Bash(npm unlink *)",
22
+ "Bash(npm link *)",
23
+ "Bash(whyline --version)",
24
+ "Bash(git rm *)",
25
+ "Bash(npx vitest *)",
26
+ "Read(//Users/malinda.ranasinghe/Documents/contentful/moi/audit-logging/**)",
27
+ "Bash(whyline init *)",
28
+ "Bash(whyline install-claude *)",
29
+ "Bash(whyline doctor *)",
30
+ "Bash(whyline list *)"
31
+ ]
32
+ }
33
+ }
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ build-and-test:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Setup Node
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 22
20
+ cache: npm
21
+
22
+ - name: Install dependencies
23
+ run: npm ci
24
+
25
+ - name: Rebuild native bindings
26
+ run: npm rebuild better-sqlite3
27
+
28
+ - name: Build
29
+ run: npm run build
30
+
31
+ - name: Test
32
+ run: npm test
33
+
34
+ - name: Lint
35
+ run: npm run lint
@@ -0,0 +1,37 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Setup Node
16
+ uses: actions/setup-node@v4
17
+ with:
18
+ node-version: 22
19
+ cache: npm
20
+ registry-url: https://registry.npmjs.org/
21
+
22
+ - name: Install dependencies
23
+ run: npm ci
24
+
25
+ - name: Rebuild native bindings
26
+ run: npm rebuild better-sqlite3
27
+
28
+ - name: Build
29
+ run: npm run build
30
+
31
+ - name: Test
32
+ run: npm test
33
+
34
+ - name: Publish to npm
35
+ run: npm publish
36
+ env:
37
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,7 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": false,
4
+ "tabWidth": 2,
5
+ "trailingComma": "es5",
6
+ "printWidth": 100
7
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,74 @@
1
+ # Claude Instructions for Whyline
2
+
3
+ You are working on `whyline` — a local-first CLI + MCP server that stores AI coding-session reasoning in SQLite and exposes it to Claude Code.
4
+
5
+ The MVP is complete. All 7 milestones are built and 160 tests pass.
6
+
7
+ ## Build principles
8
+
9
+ - Prefer simple local-first implementation.
10
+ - Do not build hosted sync unless explicitly asked.
11
+ - Do not build a UI unless explicitly asked.
12
+ - Do not store raw transcripts by default.
13
+ - Always redact secrets before saving memory.
14
+ - Keep the CLI useful without MCP.
15
+ - Keep the MCP server thin and deterministic.
16
+ - Write tests for database, search, and redaction behavior.
17
+ - Prefer boring, maintainable TypeScript.
18
+ - Use ESM (`"type": "module"`). All imports must use `.js` extensions even for `.ts` source files.
19
+ - `better-sqlite3` is synchronous — do not use async/await in the DB layer.
20
+
21
+ ## What not to build
22
+
23
+ - Cloud auth or team sharing
24
+ - Hosted vector DB or embeddings service
25
+ - Web UI
26
+ - Background daemons
27
+ - `--manual` interactive mode (deferred to v0.2)
28
+ - PR integrations (deferred to v0.6)
29
+
30
+ ## Schema changes
31
+
32
+ Any change to the database schema must be added as a new entry in `src/db/schema.ts` `MIGRATIONS[]` array with an incremented version number. Never modify existing migration SQL.
33
+
34
+ ## Adding a new CLI command
35
+
36
+ 1. Create `src/commands/<name>.ts` with a `run<Name>()` export.
37
+ 2. Register it in `src/cli.ts`.
38
+ 3. Add tests in `tests/<name>.test.ts` using in-memory SQLite.
39
+
40
+ ## Adding a new MCP tool
41
+
42
+ 1. Add a Zod schema to `src/mcp/tools.ts`.
43
+ 2. Add the tool descriptor to the `ListToolsRequestSchema` handler in `src/mcp/server.ts`.
44
+ 3. Add the `CallToolRequestSchema` case in `src/mcp/server.ts`.
45
+ 4. Run `redactSecrets()` on any user-supplied text before saving.
46
+
47
+ ## Keeping instruction files in sync
48
+
49
+ There are three files that describe how Claude should behave when working with memories. They must always be consistent with each other:
50
+
51
+ | File | Purpose |
52
+ |------|---------|
53
+ | `src/skill/SKILL.md` | Used when Whyline is loaded as a Claude Code skill |
54
+ | `how-to-run/CLAUDE.md.template` | Injected into user repos by `whyline install-claude` |
55
+ | `docs/architecture.md` | System overview — commands, components, pipelines, directory structure |
56
+ | Deployed `CLAUDE.md` files (e.g. `/Users/malinda.ranasinghe/Documents/contentful/moi/audit-logging/CLAUDE.md`) | Already-wired repos that won't re-run `install-claude` |
57
+
58
+ **Any time you add or change a feature, ask: does this change how Claude should behave?**
59
+
60
+ If yes, update all three before closing the task:
61
+
62
+ 1. `src/skill/SKILL.md` — update the relevant section
63
+ 2. `how-to-run/CLAUDE.md.template` — apply the same change
64
+ 3. Any deployed `CLAUDE.md` files — patch manually (or remind the user to re-run `whyline install-claude`)
65
+
66
+ Triggers that always require a sync:
67
+ - New MCP tool added (Claude needs to know when and how to call it)
68
+ - New field added to memory responses (e.g. `isStale`, future `confidence`)
69
+ - Search or save workflow changes
70
+ - New quality, staleness, or deduplication rules
71
+ - Changes to what Claude should say when surfacing a memory
72
+ - New CLI command added — update `docs/architecture.md` directory structure and component diagram
73
+
74
+ If you only update one file, future sessions in repos wired with the old template will behave differently from repos wired with the new one — the instructions will drift.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Malinda Ranasinghe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,359 @@
1
+ # Whyline
2
+
3
+ <p align="center">
4
+ <img src="docs/logo.png" alt="Whyline" width="120" />
5
+ </p>
6
+
7
+ ![npm](https://img.shields.io/npm/v/whyline)
8
+ ![license](https://img.shields.io/github/license/malinda1986/whyline)
9
+ ![build](https://img.shields.io/github/actions/workflow/status/malinda1986/whyline/ci.yml)
10
+
11
+ Local-first memory for AI coding sessions.
12
+
13
+ > Git remembers what changed. Whyline remembers why.
14
+
15
+ ---
16
+
17
+ ## Why this exists
18
+
19
+ You've been there. You open a file, see a decision made months ago, and have no idea why. The diff shows _what_ changed. The commit message says "fix retention logic." But why 90 days? Why a background job and not a cron? Why not S3?
20
+
21
+ That context lived in a conversation — and it's gone.
22
+
23
+ AI coding sessions with tools like Claude produce something valuable beyond the code: the reasoning behind it. The intent, the tradeoffs, the alternatives that were rejected, the risks that were acknowledged. None of that ends up in git. It lives in a chat window and disappears when the context resets.
24
+
25
+ Whyline captures it.
26
+
27
+ After each coding session, it stores a concise reasoning record in SQLite on your machine — no cloud, no auth, no new workflow. When you start a new session touching the same files, Claude searches those records automatically and surfaces the relevant context:
28
+
29
+ > _"Last time we touched this file, we rejected S3 archival because of cost. Still the case?"_
30
+
31
+ The loop is tight:
32
+
33
+ - **Start a task** → Claude searches past memories and brings forward relevant decisions
34
+ - **Finish and commit** → Claude synthesizes the session and saves the reasoning automatically
35
+ - **Come back later** → The why is right there, not lost in a chat history
36
+
37
+ It works through Claude Code's MCP protocol. Two files to set it up in any repo: `.mcp.json` and `CLAUDE.md`.
38
+
39
+ ---
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ git clone https://github.com/malinda1986/whyline.git
45
+ cd whyline
46
+ npm install
47
+ npm run build
48
+ npm link
49
+ ```
50
+
51
+ ### Node version requirement
52
+
53
+ Node 18 or later. The native `better-sqlite3` bindings must be compiled for your Node version:
54
+
55
+ ```bash
56
+ npm rebuild better-sqlite3
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Quick start
62
+
63
+ ### 1. Initialize
64
+
65
+ ```bash
66
+ whyline init
67
+ ```
68
+
69
+ Creates `~/.whyline/` with `memory.db` and `config.json`.
70
+
71
+ ### 2. Wire up a repo
72
+
73
+ Run this once in each repo you want Whyline to work in:
74
+
75
+ ```bash
76
+ cd your-project
77
+ whyline install-claude
78
+ ```
79
+
80
+ Creates or updates three files:
81
+ - `.mcp.json` — registers the Whyline MCP server (merges with existing entries)
82
+ - `CLAUDE.md` — adds the memory instructions for Claude (appends if the file already exists)
83
+ - `.claude/settings.local.json` — auto-approves the five MCP tool calls so Claude doesn't prompt on every use
84
+
85
+ ```
86
+ ✓ .mcp.json (created)
87
+ ✓ CLAUDE.md (created)
88
+ ✓ .claude/settings.local.json (created)
89
+
90
+ Done. Open this repo in Claude Code and run `whyline doctor` to verify.
91
+ ```
92
+
93
+ Running it again is safe — it only writes what's missing and never removes existing content.
94
+
95
+ ### 3. Verify setup (optional)
96
+
97
+ ```bash
98
+ whyline doctor
99
+ ```
100
+
101
+ Checks that the DB exists, migrations are current, the binary is on your PATH, you're inside a git repo, `.mcp.json` is configured, `CLAUDE.md` mentions Whyline, and the MCP server starts. Run this whenever something feels off.
102
+
103
+ ```
104
+ ✓ DB exists (~/.whyline/memory.db)
105
+ ✓ Migrations current (v1)
106
+ ✓ `whyline` on PATH (/usr/local/bin/whyline)
107
+ ✓ Inside a git repo (/home/user/my-app)
108
+ ✓ .mcp.json configured (/home/user/my-app/.mcp.json)
109
+ ✓ CLAUDE.md mentions Whyline (/home/user/my-app/CLAUDE.md)
110
+ ✓ MCP server starts (tools/list responded)
111
+
112
+ All checks passed. Whyline is ready.
113
+ ```
114
+
115
+ ### 3. Create a memory summary file
116
+
117
+ Create `memory.md` in your project:
118
+
119
+ ```markdown
120
+ # Memory
121
+
122
+ Intent:
123
+ Add optimistic comment rendering.
124
+
125
+ Summary:
126
+ Implemented optimistic rendering for new comments so they appear immediately.
127
+
128
+ Decision:
129
+ Render comments immediately and reconcile after server ack.
130
+
131
+ Why:
132
+ Waiting for server confirmation made comment creation feel slow.
133
+
134
+ Alternatives rejected:
135
+
136
+ - Server-confirmed rendering only.
137
+ - Polling for new comments after submit.
138
+
139
+ Risks:
140
+
141
+ - Duplicate comments during reconnect.
142
+ - Failed server ack needs rollback UI.
143
+
144
+ Follow-ups:
145
+
146
+ - Add dedupe reconciliation tests.
147
+ - Add failed-send retry state.
148
+
149
+ Tags:
150
+
151
+ - comments
152
+ - sync
153
+ - optimistic-ui
154
+ ```
155
+
156
+ ### 4. Save a memory
157
+
158
+ ```bash
159
+ whyline save --commit HEAD --summary-file memory.md
160
+ ```
161
+
162
+ Output:
163
+
164
+ ```
165
+ Saved memory mem_abc12345
166
+ Repo: my-app
167
+ Commit: abc12345
168
+ Files: 3
169
+ ```
170
+
171
+ ### 5. Search memories
172
+
173
+ ```bash
174
+ whyline search "why optimistic comments?"
175
+ whyline search "refresh token" --file src/auth/session.ts
176
+ whyline search "checkout validation" --limit 5
177
+
178
+ # Filter by tag (repeat for AND semantics)
179
+ whyline search "" --tag auth --tag security
180
+
181
+ # Filter by date
182
+ whyline search "" --since 2025-01-01
183
+ whyline search "" --before 2025-06-30
184
+ whyline search "token" --tag auth --since 2025-01-01 --before 2025-12-31
185
+ ```
186
+
187
+ ### 6. Browse all memories
188
+
189
+ ```bash
190
+ whyline list # all memories, newest first
191
+ whyline list --repo # scoped to the current git repo
192
+ whyline list --limit 5 # cap results
193
+ ```
194
+
195
+ ### 7. Show a memory
196
+
197
+ ```bash
198
+ whyline show mem_abc12345
199
+ whyline show --commit abc12345
200
+ ```
201
+
202
+ ### 8. Edit a memory
203
+
204
+ Open a memory in `$EDITOR` as markdown, save changes back to the DB:
205
+
206
+ ```bash
207
+ whyline edit mem_abc12345
208
+ ```
209
+
210
+ ### 9. Delete a memory
211
+
212
+ ```bash
213
+ whyline delete mem_abc12345 # prompts for confirmation
214
+ whyline delete mem_abc12345 --force # skips confirmation
215
+ ```
216
+
217
+ ### 10. Export & import memories
218
+
219
+ ```bash
220
+ # Export all memories as JSON (default)
221
+ whyline export > backup.json
222
+
223
+ # Export as markdown
224
+ whyline export --format md --output memories.md
225
+
226
+ # Export only this repo's memories, filtered by tag and date
227
+ whyline export --repo --tag auth --since 2025-01-01 --output auth-memories.json
228
+
229
+ # Import from a previous export (skips duplicates, redacts any exposed secrets)
230
+ whyline import backup.json
231
+ ```
232
+
233
+ ### 11. Storage stats
234
+
235
+ ```bash
236
+ whyline stats
237
+ ```
238
+
239
+ Output:
240
+ ```
241
+ Total memories: 42
242
+ Repos tracked: 3
243
+ Oldest memory: 1/15/2025
244
+ Newest memory: 5/13/2026
245
+
246
+ Most referenced files:
247
+ 12x src/auth/session.ts
248
+ 8x src/comments/sync.ts
249
+ 5x src/db/migrations.ts
250
+ ```
251
+
252
+ ### 12. Start MCP server
253
+
254
+ ```bash
255
+ whyline mcp
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Configure Claude Code
261
+
262
+ Add `.mcp.json` to your repo root:
263
+
264
+ ```json
265
+ {
266
+ "mcpServers": {
267
+ "whyline": {
268
+ "command": "whyline",
269
+ "args": ["mcp"]
270
+ }
271
+ }
272
+ }
273
+ ```
274
+
275
+ Then add the skill to Claude Code by referencing `src/skill/SKILL.md` in your project's `CLAUDE.md`.
276
+
277
+ ---
278
+
279
+ ## Memory summary file format
280
+
281
+ The parser is forgiving — missing fields fall back to safe defaults.
282
+
283
+ Supported headings:
284
+
285
+ | Heading | Type | Default |
286
+ | ------------------------ | ----------- | ------------------------- |
287
+ | `Task:` | text | _(omitted)_ |
288
+ | `Intent:` | text | `"Unspecified intent"` |
289
+ | `Summary:` | text | _(full file content)_ |
290
+ | `Decision:` | text | `"Unspecified decision"` |
291
+ | `Why:` | text | `"Unspecified rationale"` |
292
+ | `Alternatives rejected:` | bullet list | `[]` |
293
+ | `Risks:` | bullet list | `[]` |
294
+ | `Follow-ups:` | bullet list | `[]` |
295
+ | `Tags:` | bullet list | `[]` |
296
+
297
+ ---
298
+
299
+ ## MCP tools
300
+
301
+ Claude Code can call these tools via the MCP server:
302
+
303
+ | Tool | Description |
304
+ | ---------------------- | ------------------------------------- |
305
+ | `search_coding_memory` | Search memories by keyword |
306
+ | `save_coding_memory` | Save a new memory |
307
+ | `get_commit_memory` | Get memories for a specific commit |
308
+ | `get_file_memories` | Get memories touching a specific file |
309
+
310
+ ---
311
+
312
+ ## Secret redaction
313
+
314
+ All text is automatically scanned before saving. The following are redacted and replaced with `[REDACTED_SECRET]`:
315
+
316
+ - GitHub tokens (`ghp_`, `gho_`, `ghs_`)
317
+ - npm tokens (`npm_`)
318
+ - AWS access keys (`AKIA...`)
319
+ - Bearer tokens
320
+ - `.env`-style secrets (`API_KEY=`, `SECRET=`, etc.)
321
+ - PEM private key blocks
322
+
323
+ ---
324
+
325
+ ## Post-commit hook (optional)
326
+
327
+ Copy `src/hooks/post-commit.sample.sh` to `.git/hooks/post-commit` and make it executable:
328
+
329
+ ```bash
330
+ cp src/hooks/post-commit.sample.sh .git/hooks/post-commit
331
+ chmod +x .git/hooks/post-commit
332
+ ```
333
+
334
+ This reminds you to save a memory after each commit.
335
+
336
+ ---
337
+
338
+ ## Development
339
+
340
+ ```bash
341
+ npm run dev -- init # run via tsx without building
342
+ npm test # run tests
343
+ npm run build # compile to dist/
344
+ npm run lint # eslint
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Storage
350
+
351
+ All data is stored locally at `~/.whyline/memory.db` (SQLite). The repository is never modified unless you explicitly install the post-commit hook.
352
+
353
+ ---
354
+
355
+ ## Roadmap
356
+
357
+ - v0.6 — `--manual` interactive save mode
358
+ - v0.6 — GitHub PR integration
359
+ - v0.7 — team-shared memory server
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { runInit } from "./commands/init.js";
4
+ function collect(val, acc) {
5
+ acc.push(val);
6
+ return acc;
7
+ }
8
+ const program = new Command();
9
+ program
10
+ .name("whyline")
11
+ .description("Local-first memory for AI coding sessions")
12
+ .version("0.1.0");
13
+ program.command("init").description("Initialize whyline storage").action(() => runInit());
14
+ program
15
+ .command("doctor")
16
+ .description("Check whyline setup and diagnose configuration problems")
17
+ .action(async () => {
18
+ const { runDoctor } = await import("./commands/doctor.js");
19
+ await runDoctor();
20
+ });
21
+ program
22
+ .command("install-claude")
23
+ .description("Create or update .mcp.json, CLAUDE.md, and .claude/settings.local.json for this repo")
24
+ .option("--repo-path <path>", "Target repo path (defaults to current directory)")
25
+ .action(async (options) => {
26
+ const { runInstallClaude } = await import("./commands/install-claude.js");
27
+ await runInstallClaude(options);
28
+ });
29
+ program
30
+ .command("save")
31
+ .description("Save a coding memory")
32
+ .requiredOption("--commit <ref>", "Git commit ref")
33
+ .requiredOption("--summary-file <path>", "Path to markdown summary file")
34
+ .action(async (options) => {
35
+ const { runSave } = await import("./commands/save.js");
36
+ await runSave(options);
37
+ });
38
+ program
39
+ .command("search <query>")
40
+ .description("Search coding memories")
41
+ .option("--file <path>", "Filter by file path")
42
+ .option("--tag <tag>", "Filter by tag (repeat for multiple)", collect, [])
43
+ .option("--since <date>", "Only memories created after this date (e.g. 2025-01-01)")
44
+ .option("--before <date>", "Only memories created before this date (e.g. 2025-12-31)")
45
+ .option("--limit <n>", "Max results", "10")
46
+ .action(async (query, options) => {
47
+ const { runSearch } = await import("./commands/search.js");
48
+ await runSearch(query, options);
49
+ });
50
+ program
51
+ .command("show [id]")
52
+ .description("Show a single memory")
53
+ .option("--commit <sha>", "Find by commit SHA instead")
54
+ .action(async (id, options) => {
55
+ const { runShow } = await import("./commands/show.js");
56
+ await runShow(id, options);
57
+ });
58
+ program
59
+ .command("list")
60
+ .description("List stored memories in reverse chronological order")
61
+ .option("--repo", "Limit to the current git repository", false)
62
+ .option("--limit <n>", "Max results", "20")
63
+ .action(async (options) => {
64
+ const { runList } = await import("./commands/list.js");
65
+ await runList(options);
66
+ });
67
+ program
68
+ .command("delete <id>")
69
+ .description("Delete a memory by ID")
70
+ .option("--force", "Skip confirmation prompt", false)
71
+ .action(async (id, options) => {
72
+ const { runDelete } = await import("./commands/delete.js");
73
+ await runDelete(id, options);
74
+ });
75
+ program
76
+ .command("stats")
77
+ .description("Show memory storage statistics")
78
+ .action(async () => {
79
+ const { runStats } = await import("./commands/stats.js");
80
+ await runStats();
81
+ });
82
+ program
83
+ .command("edit <id>")
84
+ .description("Edit a memory in $EDITOR")
85
+ .action(async (id) => {
86
+ const { runEdit } = await import("./commands/edit.js");
87
+ await runEdit(id);
88
+ });
89
+ program
90
+ .command("export")
91
+ .description("Export memories to JSON or markdown")
92
+ .option("--format <fmt>", "Output format: json or md", "json")
93
+ .option("--output <path>", "Write to file instead of stdout")
94
+ .option("--repo", "Limit to the current git repository", false)
95
+ .option("--tag <tag>", "Filter by tag (repeat for multiple)", collect, [])
96
+ .option("--since <date>", "Only memories created after this date (e.g. 2025-01-01)")
97
+ .option("--before <date>", "Only memories created before this date (e.g. 2025-12-31)")
98
+ .action(async (options) => {
99
+ const { runExport } = await import("./commands/export.js");
100
+ await runExport(options);
101
+ });
102
+ program
103
+ .command("import <file>")
104
+ .description("Import memories from a JSON export file")
105
+ .action(async (file) => {
106
+ const { runImport } = await import("./commands/import.js");
107
+ await runImport(file);
108
+ });
109
+ program
110
+ .command("summarize <id>")
111
+ .description("Use the Claude API to improve a saved memory's quality (requires ANTHROPIC_API_KEY)")
112
+ .option("--force", "Apply improvements without confirmation prompt", false)
113
+ .action(async (id, options) => {
114
+ const { runSummarize } = await import("./commands/summarize.js");
115
+ await runSummarize(id, options);
116
+ });
117
+ program
118
+ .command("mcp")
119
+ .description("Start MCP server over stdio")
120
+ .action(async () => {
121
+ const { runMcp } = await import("./commands/mcp.js");
122
+ await runMcp();
123
+ });
124
+ program.parse();
125
+ //# sourceMappingURL=cli.js.map