whywhy-mcp 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/LICENSE +21 -0
- package/README.md +213 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +110 -0
- package/dist/cli.js.map +1 -0
- package/dist/decisions.d.ts +63 -0
- package/dist/decisions.js +174 -0
- package/dist/decisions.js.map +1 -0
- package/dist/git.d.ts +14 -0
- package/dist/git.js +121 -0
- package/dist/git.js.map +1 -0
- package/dist/guidance.d.ts +8 -0
- package/dist/guidance.js +43 -0
- package/dist/guidance.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +179 -0
- package/dist/index.js.map +1 -0
- package/dist/schema.d.ts +460 -0
- package/dist/schema.js +139 -0
- package/dist/schema.js.map +1 -0
- package/dist/store.d.ts +45 -0
- package/dist/store.js +181 -0
- package/dist/store.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Micky Multani
|
|
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,213 @@
|
|
|
1
|
+
# whywhy
|
|
2
|
+
|
|
3
|
+
**Record _why_ decisions are made during AI-assisted coding.**
|
|
4
|
+
|
|
5
|
+
`whywhy` is an agent-agnostic [MCP](https://modelcontextprotocol.io) server. When
|
|
6
|
+
you and a coding agent (Claude Code, Cursor, Copilot-via-MCP, …) settle a
|
|
7
|
+
meaningful decision — a library choice, a schema, an API shape, an infra call —
|
|
8
|
+
the agent records it through whywhy. Each decision is stored as a clean,
|
|
9
|
+
versioned JSON record that lives in your repo, correlated with the conversation
|
|
10
|
+
reasoning, the git diff, and any related code comments — **without cluttering
|
|
11
|
+
your source files**.
|
|
12
|
+
|
|
13
|
+
Months later you (or a new teammate) can ask _"why did we pick Postgres?"_ and
|
|
14
|
+
get the real answer, including the alternatives that were rejected.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Why
|
|
19
|
+
|
|
20
|
+
The reasoning behind decisions usually leaks into three lossy places: inline
|
|
21
|
+
comments (clutter, decay), commit messages (terse, disconnected), and the agent
|
|
22
|
+
chat transcript (ephemeral, unsearchable). whywhy captures decisions as
|
|
23
|
+
first-class, structured, versioned artifacts instead.
|
|
24
|
+
|
|
25
|
+
## Install & set up
|
|
26
|
+
|
|
27
|
+
Requires Node ≥ 18.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# In your project root:
|
|
31
|
+
npx -y whywhy-mcp init
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`init` creates a `.whywhy/` store and appends an agent-guidance block to
|
|
35
|
+
`CLAUDE.md` (telling the agent when to log decisions).
|
|
36
|
+
|
|
37
|
+
Then register the server with your agent.
|
|
38
|
+
|
|
39
|
+
### Claude Code (project scope, so the team shares it)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
claude mcp add --scope project --transport stdio whywhy -- npx -y whywhy-mcp
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`--scope project` writes to `.mcp.json` at the repo root — commit it so
|
|
46
|
+
teammates inherit the server. Verify with `claude mcp list` or `/mcp` in a
|
|
47
|
+
session.
|
|
48
|
+
|
|
49
|
+
### Other MCP agents (Cursor, etc.)
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"whywhy": { "command": "npx", "args": ["-y", "whywhy-mcp"] }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Paste the guidance block (`npx whywhy-mcp guidance`) into that agent's rules file.
|
|
60
|
+
|
|
61
|
+
## How it works
|
|
62
|
+
|
|
63
|
+
An MCP server can't passively eavesdrop on your conversation — the agent decides
|
|
64
|
+
when to call tools. So whywhy exposes **tools the agent is instructed to call**
|
|
65
|
+
when a decision is reached (via the guidance snippet `init` installs), and the
|
|
66
|
+
server gathers the diff/comment evidence automatically at that moment.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Agent reaches a decision ──▶ calls record_decision(...)
|
|
70
|
+
│
|
|
71
|
+
▼
|
|
72
|
+
whywhy MCP server
|
|
73
|
+
• captures branch/HEAD/working diff
|
|
74
|
+
• scans referenced files for comments
|
|
75
|
+
• assigns DR-id, status=active
|
|
76
|
+
• writes .whywhy/decisions/DR-XXXX.json
|
|
77
|
+
• updates index.json
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## MCP tools
|
|
81
|
+
|
|
82
|
+
| Tool | Purpose |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `record_decision` | Log a new decision; auto-captures git diff + code comments. |
|
|
85
|
+
| `supersede_decision` | Replace a decision; links both, flips the old to `superseded`. |
|
|
86
|
+
| `update_decision` | Edit a record in place; bumps its `version`. |
|
|
87
|
+
| `deprecate_decision` | Retire a decision with no replacement (`deprecated`). |
|
|
88
|
+
| `query_decisions` | Keyword + filter search; powers "why did we…?". |
|
|
89
|
+
| `get_decision` | Fetch one full record by id. |
|
|
90
|
+
| `list_decisions` | Browse lightweight index entries. |
|
|
91
|
+
|
|
92
|
+
A `whywhy://index` MCP **resource** also exposes `index.json` directly.
|
|
93
|
+
|
|
94
|
+
## Storage layout
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
<repo-root>/
|
|
98
|
+
└── .whywhy/
|
|
99
|
+
├── config.json # categories, store path, capture mode, privacy toggles
|
|
100
|
+
├── index.json # rolled-up list for fast lookup
|
|
101
|
+
└── decisions/
|
|
102
|
+
├── DR-0001.json
|
|
103
|
+
└── DR-0002.json
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Everything is plain JSON, committed to git, and reviewable in PRs. **Records are
|
|
107
|
+
never deleted** — the history is the value.
|
|
108
|
+
|
|
109
|
+
### A decision record
|
|
110
|
+
|
|
111
|
+
```jsonc
|
|
112
|
+
{
|
|
113
|
+
"id": "DR-0007",
|
|
114
|
+
"schema_version": 1,
|
|
115
|
+
"title": "Use PostgreSQL for primary datastore",
|
|
116
|
+
"category": "infrastructure", // architecture | infrastructure | dependency |
|
|
117
|
+
// api | data-model | ui | product | security |
|
|
118
|
+
// performance | process | other
|
|
119
|
+
"status": "active", // active | superseded | deprecated
|
|
120
|
+
"decision": "Adopt PostgreSQL 16 …",
|
|
121
|
+
"rationale": "Need strong relational integrity …",
|
|
122
|
+
"alternatives_considered": [
|
|
123
|
+
{ "option": "MongoDB", "reason_rejected": "Weaker multi-doc transactions." }
|
|
124
|
+
],
|
|
125
|
+
"consequences": "Adds an ops dependency; requires connection pooling.",
|
|
126
|
+
"tags": ["database", "backend"],
|
|
127
|
+
"evidence": {
|
|
128
|
+
"conversation": { "summary": "…", "excerpt": "…" },
|
|
129
|
+
"diff": { "branch": "main", "base_commit": "…", "head_commit": "…",
|
|
130
|
+
"files_changed": ["prisma/schema.prisma"], "summary": "…" },
|
|
131
|
+
"code_comments": [{ "file": "prisma/schema.prisma", "line": 3, "text": "…" }]
|
|
132
|
+
},
|
|
133
|
+
"version": 1,
|
|
134
|
+
"supersedes": null,
|
|
135
|
+
"superseded_by": null,
|
|
136
|
+
"created_at": "2026-06-08T17:22:31Z",
|
|
137
|
+
"updated_at": "2026-06-08T17:22:31Z",
|
|
138
|
+
"created_by": "claude-code",
|
|
139
|
+
"session_id": "sess_8c2f"
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Versioning & status
|
|
144
|
+
|
|
145
|
+
- **Record edits** (`update_decision`) bump the `version` field — same identity.
|
|
146
|
+
- **Supersession** (`supersede_decision`) creates a _new_ record, flips the old
|
|
147
|
+
one to `superseded`, and cross-links them (`supersedes` / `superseded_by`).
|
|
148
|
+
- **Deprecation** (`deprecate_decision`) retires a decision with no replacement.
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
record_decision ─▶ active ──supersede──▶ superseded
|
|
152
|
+
│
|
|
153
|
+
└────deprecate────▶ deprecated
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Configuration (`.whywhy/config.json`)
|
|
157
|
+
|
|
158
|
+
| Key | Default | Meaning |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `store_path` | `.whywhy` | Where records live. |
|
|
161
|
+
| `categories` | (the 11 above) | Allowed categories. |
|
|
162
|
+
| `capture_mode` | `inline` | `inline` or `session-summary`. |
|
|
163
|
+
| `store_excerpts` | `false` | If true, verbatim conversation excerpts are stored. |
|
|
164
|
+
| `capture_git` | `true` | If false, skips git diff capture. |
|
|
165
|
+
|
|
166
|
+
## CLI
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
whywhy-mcp [serve] # start the stdio MCP server (default)
|
|
170
|
+
whywhy-mcp init # create .whywhy/ + add guidance to CLAUDE.md
|
|
171
|
+
whywhy-mcp guidance # print the agent-guidance snippet
|
|
172
|
+
whywhy-mcp rebuild-index # rebuild index.json from the decision files
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Development
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
npm install
|
|
179
|
+
npm run build # compile to dist/
|
|
180
|
+
npm run server # run the server from source (tsx)
|
|
181
|
+
node scripts/smoke.mjs # end-to-end stdio smoke test
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Releasing (maintainers)
|
|
185
|
+
|
|
186
|
+
Publishing is automated via GitHub Actions ([.github/workflows/publish.yml](.github/workflows/publish.yml)).
|
|
187
|
+
|
|
188
|
+
**One-time setup:** create an npm **Automation** token (npmjs.com → Access
|
|
189
|
+
Tokens), then add it as a repo secret named `NPM_TOKEN`
|
|
190
|
+
(GitHub repo → Settings → Secrets and variables → Actions).
|
|
191
|
+
|
|
192
|
+
**To cut a release:**
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npm version patch # or minor / major — bumps package.json + creates a git tag
|
|
196
|
+
git push --follow-tags
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Then publish a GitHub Release for that tag (`gh release create vX.Y.Z --generate-notes`).
|
|
200
|
+
The workflow builds, verifies the tag matches `package.json`, and runs
|
|
201
|
+
`npm publish --provenance`.
|
|
202
|
+
|
|
203
|
+
Prefer to publish manually instead? `npm login && npm publish` works too
|
|
204
|
+
(`prepublishOnly` builds first).
|
|
205
|
+
|
|
206
|
+
## Status
|
|
207
|
+
|
|
208
|
+
v0.1 (MVP). Deferred: VS Code extension, HTTP transport, semantic search,
|
|
209
|
+
multi-repo aggregation, Claude Code auto-flush hooks. See `whywhy-PRD.md`.
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { DecisionStore } from "./store.js";
|
|
5
|
+
import { GUIDANCE_SNIPPET, GUIDANCE_BEGIN, GUIDANCE_END } from "./guidance.js";
|
|
6
|
+
/**
|
|
7
|
+
* whywhy CLI.
|
|
8
|
+
*
|
|
9
|
+
* With no args (or `serve`), it launches the stdio MCP server -- this is what
|
|
10
|
+
* `npx -y whywhy-mcp` does when an agent spawns it. `init` sets up the store
|
|
11
|
+
* and writes the agent-guidance snippet.
|
|
12
|
+
*/
|
|
13
|
+
async function runServer() {
|
|
14
|
+
await import("./index.js");
|
|
15
|
+
}
|
|
16
|
+
async function init(repoRoot) {
|
|
17
|
+
const store = new DecisionStore(repoRoot);
|
|
18
|
+
const config = await store.init();
|
|
19
|
+
console.log(`Initialized whywhy store at ${path.relative(repoRoot, store.storeDir) || "."}/`);
|
|
20
|
+
console.log(` - config.json`);
|
|
21
|
+
console.log(` - index.json`);
|
|
22
|
+
console.log(` - decisions/`);
|
|
23
|
+
await appendGuidance(repoRoot, "CLAUDE.md");
|
|
24
|
+
console.log("");
|
|
25
|
+
console.log("Next steps:");
|
|
26
|
+
console.log(" 1. Register the MCP server with your agent. For Claude Code:");
|
|
27
|
+
console.log(" claude mcp add --scope project --transport stdio whywhy -- npx -y whywhy-mcp");
|
|
28
|
+
console.log(" 2. Commit the .whywhy/ folder and .mcp.json so your team inherits it.");
|
|
29
|
+
console.log("");
|
|
30
|
+
void config;
|
|
31
|
+
}
|
|
32
|
+
/** Append the guidance snippet to a rules file, replacing any prior block. */
|
|
33
|
+
async function appendGuidance(repoRoot, fileName) {
|
|
34
|
+
const target = path.resolve(repoRoot, fileName);
|
|
35
|
+
let existing = "";
|
|
36
|
+
try {
|
|
37
|
+
existing = await fs.readFile(target, "utf8");
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
existing = "";
|
|
41
|
+
}
|
|
42
|
+
let next;
|
|
43
|
+
if (existing.includes(GUIDANCE_BEGIN) && existing.includes(GUIDANCE_END)) {
|
|
44
|
+
// Replace the existing whywhy block in place.
|
|
45
|
+
const before = existing.slice(0, existing.indexOf(GUIDANCE_BEGIN));
|
|
46
|
+
const after = existing.slice(existing.indexOf(GUIDANCE_END) + GUIDANCE_END.length);
|
|
47
|
+
next = `${before}${GUIDANCE_SNIPPET.trim()}${after}`;
|
|
48
|
+
console.log(`Updated whywhy guidance block in ${fileName}.`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
const sep = existing && !existing.endsWith("\n") ? "\n\n" : existing ? "\n" : "";
|
|
52
|
+
next = `${existing}${sep}${GUIDANCE_SNIPPET}`;
|
|
53
|
+
console.log(`Appended whywhy guidance to ${fileName}.`);
|
|
54
|
+
}
|
|
55
|
+
await fs.writeFile(target, next, "utf8");
|
|
56
|
+
}
|
|
57
|
+
async function printGuidance() {
|
|
58
|
+
console.log(GUIDANCE_SNIPPET);
|
|
59
|
+
}
|
|
60
|
+
async function main() {
|
|
61
|
+
const [, , cmd, ...rest] = process.argv;
|
|
62
|
+
const repoRoot = process.env.WHYWHY_ROOT || process.cwd();
|
|
63
|
+
switch (cmd) {
|
|
64
|
+
case undefined:
|
|
65
|
+
case "serve":
|
|
66
|
+
await runServer();
|
|
67
|
+
break;
|
|
68
|
+
case "init":
|
|
69
|
+
await init(repoRoot);
|
|
70
|
+
break;
|
|
71
|
+
case "guidance":
|
|
72
|
+
await printGuidance();
|
|
73
|
+
break;
|
|
74
|
+
case "rebuild-index": {
|
|
75
|
+
const store = new DecisionStore(repoRoot);
|
|
76
|
+
const index = await store.rebuildIndex();
|
|
77
|
+
console.log(`Rebuilt index.json with ${index.decisions.length} decision(s).`);
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case "help":
|
|
81
|
+
case "--help":
|
|
82
|
+
case "-h":
|
|
83
|
+
printHelp();
|
|
84
|
+
break;
|
|
85
|
+
default:
|
|
86
|
+
console.error(`Unknown command: ${cmd}\n`);
|
|
87
|
+
printHelp();
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
void rest;
|
|
91
|
+
}
|
|
92
|
+
function printHelp() {
|
|
93
|
+
console.log(`whywhy-mcp -- record WHY decisions are made during AI-assisted coding
|
|
94
|
+
|
|
95
|
+
Usage:
|
|
96
|
+
whywhy-mcp [serve] Start the stdio MCP server (default)
|
|
97
|
+
whywhy-mcp init Create .whywhy/ store and add agent guidance to CLAUDE.md
|
|
98
|
+
whywhy-mcp guidance Print the agent-guidance snippet (for Cursor rules, etc.)
|
|
99
|
+
whywhy-mcp rebuild-index Rebuild index.json from the decision files
|
|
100
|
+
whywhy-mcp help Show this help
|
|
101
|
+
|
|
102
|
+
Environment:
|
|
103
|
+
WHYWHY_ROOT Override the repo root (defaults to current directory)
|
|
104
|
+
`);
|
|
105
|
+
}
|
|
106
|
+
main().catch((err) => {
|
|
107
|
+
console.error(err);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Config, DecisionRecord, IndexEntry, Status } from "./schema.js";
|
|
2
|
+
import { DecisionStore } from "./store.js";
|
|
3
|
+
/** Normalized input for creating a new decision (from record/supersede tools). */
|
|
4
|
+
export interface NewDecisionInput {
|
|
5
|
+
title: string;
|
|
6
|
+
category: DecisionRecord["category"];
|
|
7
|
+
decision: string;
|
|
8
|
+
rationale: string;
|
|
9
|
+
alternatives_considered?: {
|
|
10
|
+
option: string;
|
|
11
|
+
reason_rejected: string;
|
|
12
|
+
}[];
|
|
13
|
+
consequences?: string;
|
|
14
|
+
tags?: string[];
|
|
15
|
+
conversation_summary?: string;
|
|
16
|
+
conversation_excerpt?: string;
|
|
17
|
+
files?: string[];
|
|
18
|
+
session_id?: string;
|
|
19
|
+
created_by?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Create and persist a brand-new decision record (status=active). */
|
|
22
|
+
export declare function createDecision(store: DecisionStore, config: Config, input: NewDecisionInput, opts?: {
|
|
23
|
+
supersedes?: string;
|
|
24
|
+
}): Promise<DecisionRecord>;
|
|
25
|
+
/** Create a replacement decision and flip the old one to `superseded`. */
|
|
26
|
+
export declare function supersedeDecision(store: DecisionStore, config: Config, oldId: string, input: NewDecisionInput): Promise<{
|
|
27
|
+
newRecord: DecisionRecord;
|
|
28
|
+
oldRecord: DecisionRecord;
|
|
29
|
+
}>;
|
|
30
|
+
/** Editable fields for update_decision. */
|
|
31
|
+
export interface DecisionEdits {
|
|
32
|
+
title?: string;
|
|
33
|
+
category?: DecisionRecord["category"];
|
|
34
|
+
decision?: string;
|
|
35
|
+
rationale?: string;
|
|
36
|
+
alternatives_considered?: {
|
|
37
|
+
option: string;
|
|
38
|
+
reason_rejected: string;
|
|
39
|
+
}[];
|
|
40
|
+
consequences?: string;
|
|
41
|
+
tags?: string[];
|
|
42
|
+
conversation_summary?: string;
|
|
43
|
+
}
|
|
44
|
+
/** Apply small corrections to an existing record; bumps version + updated_at. */
|
|
45
|
+
export declare function updateDecision(store: DecisionStore, id: string, edits: DecisionEdits): Promise<DecisionRecord>;
|
|
46
|
+
/** Retire a decision with no replacement. */
|
|
47
|
+
export declare function deprecateDecision(store: DecisionStore, id: string, reason?: string): Promise<DecisionRecord>;
|
|
48
|
+
export interface QueryFilter {
|
|
49
|
+
query?: string;
|
|
50
|
+
category?: string;
|
|
51
|
+
status?: Status;
|
|
52
|
+
tags?: string[];
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Filtered + keyword search over the store. The calling agent does the
|
|
56
|
+
* natural-language reasoning; this just narrows the candidate set and ranks
|
|
57
|
+
* by simple keyword overlap so the most relevant records surface first.
|
|
58
|
+
*/
|
|
59
|
+
export declare function queryDecisions(store: DecisionStore, filter: QueryFilter): Promise<DecisionRecord[]>;
|
|
60
|
+
export declare function listDecisions(store: DecisionStore, filter?: {
|
|
61
|
+
status?: Status;
|
|
62
|
+
category?: string;
|
|
63
|
+
}): Promise<IndexEntry[]>;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { SCHEMA_VERSION, } from "./schema.js";
|
|
2
|
+
import { captureGitEvidence, scanCodeComments } from "./git.js";
|
|
3
|
+
/** Assemble evidence (git diff + code comments) honoring config toggles. */
|
|
4
|
+
async function buildEvidence(store, config, input) {
|
|
5
|
+
const diff = config.capture_git ? await captureGitEvidence(store.root) : null;
|
|
6
|
+
const code_comments = input.files && input.files.length > 0
|
|
7
|
+
? await scanCodeComments(store.root, input.files)
|
|
8
|
+
: [];
|
|
9
|
+
const excerpt = config.store_excerpts && input.conversation_excerpt
|
|
10
|
+
? input.conversation_excerpt
|
|
11
|
+
: undefined;
|
|
12
|
+
return {
|
|
13
|
+
conversation: {
|
|
14
|
+
summary: input.conversation_summary ?? "",
|
|
15
|
+
...(excerpt ? { excerpt } : {}),
|
|
16
|
+
},
|
|
17
|
+
diff,
|
|
18
|
+
code_comments,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** Create and persist a brand-new decision record (status=active). */
|
|
22
|
+
export async function createDecision(store, config, input, opts = {}) {
|
|
23
|
+
const { id } = await store.allocateId();
|
|
24
|
+
const now = new Date().toISOString();
|
|
25
|
+
const evidence = await buildEvidence(store, config, input);
|
|
26
|
+
const record = {
|
|
27
|
+
id,
|
|
28
|
+
schema_version: SCHEMA_VERSION,
|
|
29
|
+
title: input.title,
|
|
30
|
+
category: input.category,
|
|
31
|
+
status: "active",
|
|
32
|
+
decision: input.decision,
|
|
33
|
+
rationale: input.rationale,
|
|
34
|
+
alternatives_considered: input.alternatives_considered ?? [],
|
|
35
|
+
consequences: input.consequences ?? "",
|
|
36
|
+
tags: input.tags ?? [],
|
|
37
|
+
evidence,
|
|
38
|
+
version: 1,
|
|
39
|
+
supersedes: opts.supersedes ?? null,
|
|
40
|
+
superseded_by: null,
|
|
41
|
+
created_at: now,
|
|
42
|
+
updated_at: now,
|
|
43
|
+
created_by: input.created_by ?? "agent",
|
|
44
|
+
session_id: input.session_id ?? null,
|
|
45
|
+
};
|
|
46
|
+
await store.writeRecord(record);
|
|
47
|
+
return record;
|
|
48
|
+
}
|
|
49
|
+
/** Create a replacement decision and flip the old one to `superseded`. */
|
|
50
|
+
export async function supersedeDecision(store, config, oldId, input) {
|
|
51
|
+
const oldRecord = await store.readRecord(oldId);
|
|
52
|
+
const newRecord = await createDecision(store, config, input, { supersedes: oldId });
|
|
53
|
+
const updatedOld = {
|
|
54
|
+
...oldRecord,
|
|
55
|
+
status: "superseded",
|
|
56
|
+
superseded_by: newRecord.id,
|
|
57
|
+
version: oldRecord.version + 1,
|
|
58
|
+
updated_at: new Date().toISOString(),
|
|
59
|
+
};
|
|
60
|
+
await store.writeRecord(updatedOld);
|
|
61
|
+
return { newRecord, oldRecord: updatedOld };
|
|
62
|
+
}
|
|
63
|
+
/** Apply small corrections to an existing record; bumps version + updated_at. */
|
|
64
|
+
export async function updateDecision(store, id, edits) {
|
|
65
|
+
const existing = await store.readRecord(id);
|
|
66
|
+
const updated = {
|
|
67
|
+
...existing,
|
|
68
|
+
...("title" in edits && edits.title !== undefined ? { title: edits.title } : {}),
|
|
69
|
+
...("category" in edits && edits.category !== undefined
|
|
70
|
+
? { category: edits.category }
|
|
71
|
+
: {}),
|
|
72
|
+
...("decision" in edits && edits.decision !== undefined
|
|
73
|
+
? { decision: edits.decision }
|
|
74
|
+
: {}),
|
|
75
|
+
...("rationale" in edits && edits.rationale !== undefined
|
|
76
|
+
? { rationale: edits.rationale }
|
|
77
|
+
: {}),
|
|
78
|
+
...("alternatives_considered" in edits && edits.alternatives_considered !== undefined
|
|
79
|
+
? { alternatives_considered: edits.alternatives_considered }
|
|
80
|
+
: {}),
|
|
81
|
+
...("consequences" in edits && edits.consequences !== undefined
|
|
82
|
+
? { consequences: edits.consequences }
|
|
83
|
+
: {}),
|
|
84
|
+
...("tags" in edits && edits.tags !== undefined ? { tags: edits.tags } : {}),
|
|
85
|
+
version: existing.version + 1,
|
|
86
|
+
updated_at: new Date().toISOString(),
|
|
87
|
+
};
|
|
88
|
+
if (edits.conversation_summary !== undefined) {
|
|
89
|
+
updated.evidence = {
|
|
90
|
+
...existing.evidence,
|
|
91
|
+
conversation: {
|
|
92
|
+
...existing.evidence.conversation,
|
|
93
|
+
summary: edits.conversation_summary,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
await store.writeRecord(updated);
|
|
98
|
+
return updated;
|
|
99
|
+
}
|
|
100
|
+
/** Retire a decision with no replacement. */
|
|
101
|
+
export async function deprecateDecision(store, id, reason) {
|
|
102
|
+
const existing = await store.readRecord(id);
|
|
103
|
+
const updated = {
|
|
104
|
+
...existing,
|
|
105
|
+
status: "deprecated",
|
|
106
|
+
consequences: reason
|
|
107
|
+
? `${existing.consequences}\n\n[Deprecated] ${reason}`.trim()
|
|
108
|
+
: existing.consequences,
|
|
109
|
+
version: existing.version + 1,
|
|
110
|
+
updated_at: new Date().toISOString(),
|
|
111
|
+
};
|
|
112
|
+
await store.writeRecord(updated);
|
|
113
|
+
return updated;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Filtered + keyword search over the store. The calling agent does the
|
|
117
|
+
* natural-language reasoning; this just narrows the candidate set and ranks
|
|
118
|
+
* by simple keyword overlap so the most relevant records surface first.
|
|
119
|
+
*/
|
|
120
|
+
export async function queryDecisions(store, filter) {
|
|
121
|
+
let records = await store.readAllRecords();
|
|
122
|
+
if (filter.category) {
|
|
123
|
+
records = records.filter((r) => r.category === filter.category);
|
|
124
|
+
}
|
|
125
|
+
if (filter.status) {
|
|
126
|
+
records = records.filter((r) => r.status === filter.status);
|
|
127
|
+
}
|
|
128
|
+
if (filter.tags && filter.tags.length > 0) {
|
|
129
|
+
const wanted = filter.tags.map((t) => t.toLowerCase());
|
|
130
|
+
records = records.filter((r) => r.tags.some((t) => wanted.includes(t.toLowerCase())));
|
|
131
|
+
}
|
|
132
|
+
if (filter.query && filter.query.trim()) {
|
|
133
|
+
const terms = filter.query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
134
|
+
const scored = records
|
|
135
|
+
.map((r) => ({ r, score: keywordScore(r, terms) }))
|
|
136
|
+
.filter((s) => s.score > 0)
|
|
137
|
+
.sort((a, b) => b.score - a.score);
|
|
138
|
+
return scored.map((s) => s.r);
|
|
139
|
+
}
|
|
140
|
+
// No query: most-recently-updated first.
|
|
141
|
+
return records.sort((a, b) => b.updated_at.localeCompare(a.updated_at));
|
|
142
|
+
}
|
|
143
|
+
function keywordScore(record, terms) {
|
|
144
|
+
const haystack = [
|
|
145
|
+
record.title,
|
|
146
|
+
record.decision,
|
|
147
|
+
record.rationale,
|
|
148
|
+
record.consequences,
|
|
149
|
+
record.tags.join(" "),
|
|
150
|
+
record.category,
|
|
151
|
+
record.evidence.conversation.summary,
|
|
152
|
+
record.alternatives_considered.map((a) => `${a.option} ${a.reason_rejected}`).join(" "),
|
|
153
|
+
]
|
|
154
|
+
.join(" ")
|
|
155
|
+
.toLowerCase();
|
|
156
|
+
let score = 0;
|
|
157
|
+
for (const term of terms) {
|
|
158
|
+
if (haystack.includes(term))
|
|
159
|
+
score += 1;
|
|
160
|
+
// Title matches are worth more.
|
|
161
|
+
if (record.title.toLowerCase().includes(term))
|
|
162
|
+
score += 2;
|
|
163
|
+
}
|
|
164
|
+
return score;
|
|
165
|
+
}
|
|
166
|
+
export async function listDecisions(store, filter = {}) {
|
|
167
|
+
let entries = await store.listEntries();
|
|
168
|
+
if (filter.status)
|
|
169
|
+
entries = entries.filter((e) => e.status === filter.status);
|
|
170
|
+
if (filter.category)
|
|
171
|
+
entries = entries.filter((e) => e.category === filter.category);
|
|
172
|
+
return entries.sort((a, b) => a.id.localeCompare(b.id));
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=decisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../src/decisions.ts"],"names":[],"mappings":"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"}
|
package/dist/git.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { CodeComment, DiffEvidence } from "./schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Captures git evidence (branch, commits, working diff) for a decision.
|
|
4
|
+
*
|
|
5
|
+
* Everything here is best-effort: a repo with no git, no commits, or no
|
|
6
|
+
* changes still produces a valid (mostly-null) DiffEvidence rather than
|
|
7
|
+
* throwing, so recording a decision never fails because of git state.
|
|
8
|
+
*/
|
|
9
|
+
export declare function captureGitEvidence(repoRoot: string): Promise<DiffEvidence | null>;
|
|
10
|
+
/**
|
|
11
|
+
* Scans the given files for code comments, returning a small sample so a
|
|
12
|
+
* decision record can point at the in-code rationale without bloating.
|
|
13
|
+
*/
|
|
14
|
+
export declare function scanCodeComments(repoRoot: string, files: string[], maxPerFile?: number): Promise<CodeComment[]>;
|