@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.
- package/.claude/settings.local.json +33 -0
- package/.github/workflows/ci.yml +35 -0
- package/.github/workflows/publish.yml +37 -0
- package/.prettierrc.json +7 -0
- package/CLAUDE.md +74 -0
- package/LICENSE +21 -0
- package/README.md +359 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +125 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/delete.d.ts +3 -0
- package/dist/commands/delete.js +42 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +111 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/edit.d.ts +1 -0
- package/dist/commands/edit.js +78 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/export.d.ts +8 -0
- package/dist/commands/export.js +90 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/import.d.ts +1 -0
- package/dist/commands/import.js +110 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +23 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/install-claude.d.ts +3 -0
- package/dist/commands/install-claude.js +180 -0
- package/dist/commands/install-claude.js.map +1 -0
- package/dist/commands/list.d.ts +4 -0
- package/dist/commands/list.js +35 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.js +10 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/save.d.ts +4 -0
- package/dist/commands/save.js +74 -0
- package/dist/commands/save.js.map +1 -0
- package/dist/commands/search.d.ts +7 -0
- package/dist/commands/search.js +46 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/show.d.ts +3 -0
- package/dist/commands/show.js +30 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/stats.d.ts +1 -0
- package/dist/commands/stats.js +27 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/summarize.d.ts +3 -0
- package/dist/commands/summarize.js +140 -0
- package/dist/commands/summarize.js.map +1 -0
- package/dist/config.d.ts +11 -0
- package/dist/config.js +17 -0
- package/dist/config.js.map +1 -0
- package/dist/db/connection.d.ts +2 -0
- package/dist/db/connection.js +8 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/migrations.d.ts +2 -0
- package/dist/db/migrations.js +19 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/schema.d.ts +5 -0
- package/dist/db/schema.js +64 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/git/diff.d.ts +2 -0
- package/dist/git/diff.js +45 -0
- package/dist/git/diff.js.map +1 -0
- package/dist/git/git.d.ts +3 -0
- package/dist/git/git.js +25 -0
- package/dist/git/git.js.map +1 -0
- package/dist/git/repoId.d.ts +3 -0
- package/dist/git/repoId.js +49 -0
- package/dist/git/repoId.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +296 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +119 -0
- package/dist/mcp/tools.js +43 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/memory/parseSummary.d.ts +14 -0
- package/dist/memory/parseSummary.js +53 -0
- package/dist/memory/parseSummary.js.map +1 -0
- package/dist/memory/qualityCheck.d.ts +13 -0
- package/dist/memory/qualityCheck.js +78 -0
- package/dist/memory/qualityCheck.js.map +1 -0
- package/dist/memory/redactSecrets.d.ts +7 -0
- package/dist/memory/redactSecrets.js +29 -0
- package/dist/memory/redactSecrets.js.map +1 -0
- package/dist/memory/repoContext.d.ts +2 -0
- package/dist/memory/repoContext.js +23 -0
- package/dist/memory/repoContext.js.map +1 -0
- package/dist/memory/saveMemory.d.ts +40 -0
- package/dist/memory/saveMemory.js +223 -0
- package/dist/memory/saveMemory.js.map +1 -0
- package/dist/memory/searchMemory.d.ts +17 -0
- package/dist/memory/searchMemory.js +122 -0
- package/dist/memory/searchMemory.js.map +1 -0
- package/dist/memory/types.d.ts +48 -0
- package/dist/memory/types.js +2 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/output/format.d.ts +3 -0
- package/dist/output/format.js +43 -0
- package/dist/output/format.js.map +1 -0
- package/docs/architecture.md +387 -0
- package/docs/ec6ab3bf-60cf-4629-ad9e-3048e8e3c43a.png +0 -0
- package/docs/logo.png +0 -0
- package/eslint.config.js +16 -0
- package/how-to-run/01-install.md +69 -0
- package/how-to-run/02-wire-up-your-repo.md +80 -0
- package/how-to-run/03-test-it-manually.md +91 -0
- package/how-to-run/04-test-with-claude-code.md +70 -0
- package/how-to-run/CLAUDE.md.template +72 -0
- package/how-to-run/README.md +49 -0
- package/package.json +60 -0
- package/src/cli.ts +142 -0
- package/src/commands/delete.ts +47 -0
- package/src/commands/doctor.ts +128 -0
- package/src/commands/edit.ts +80 -0
- package/src/commands/export.ts +95 -0
- package/src/commands/import.ts +119 -0
- package/src/commands/init.ts +31 -0
- package/src/commands/install-claude.ts +203 -0
- package/src/commands/list.ts +41 -0
- package/src/commands/mcp.ts +12 -0
- package/src/commands/save.ts +85 -0
- package/src/commands/search.ts +56 -0
- package/src/commands/show.ts +37 -0
- package/src/commands/stats.ts +31 -0
- package/src/commands/summarize.ts +183 -0
- package/src/config.ts +26 -0
- package/src/db/connection.ts +8 -0
- package/src/db/migrations.ts +26 -0
- package/src/db/schema.ts +68 -0
- package/src/git/diff.ts +43 -0
- package/src/git/git.ts +25 -0
- package/src/git/repoId.ts +49 -0
- package/src/hooks/post-commit.sample.sh +9 -0
- package/src/mcp/server.ts +326 -0
- package/src/mcp/tools.ts +53 -0
- package/src/memory/parseSummary.ts +72 -0
- package/src/memory/qualityCheck.ts +102 -0
- package/src/memory/redactSecrets.ts +32 -0
- package/src/memory/repoContext.ts +25 -0
- package/src/memory/saveMemory.ts +369 -0
- package/src/memory/searchMemory.ts +153 -0
- package/src/memory/types.ts +57 -0
- package/src/output/format.ts +44 -0
- package/src/skill/SKILL.md +95 -0
- package/tests/cliV02.test.ts +213 -0
- package/tests/doctor.test.ts +253 -0
- package/tests/exportImport.test.ts +248 -0
- package/tests/fileRename.test.ts +156 -0
- package/tests/gitHelpers.test.ts +94 -0
- package/tests/init.test.ts +93 -0
- package/tests/installClaude.test.ts +157 -0
- package/tests/parseSummary.test.ts +111 -0
- package/tests/qualityCheck.test.ts +182 -0
- package/tests/redactSecrets.test.ts +75 -0
- package/tests/saveMemory.test.ts +196 -0
- package/tests/searchFilters.test.ts +139 -0
- package/tests/searchMemory.test.ts +273 -0
- package/tests/stale.test.ts +47 -0
- package/tsconfig.json +18 -0
- 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 }}
|
package/.prettierrc.json
ADDED
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
|
+

|
|
8
|
+

|
|
9
|
+

|
|
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
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
|