whynot-cli 0.0.0-stage → 0.1.1

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.
@@ -0,0 +1,69 @@
1
+ # whynot — paste this block into your AGENTS.md / CLAUDE.md
2
+
3
+ The block below is the *behavioral* half of whynot. The MCP server gives the
4
+ agent the ability to read and write decisions; this text is what makes it
5
+ actually do so. Copy it verbatim.
6
+
7
+ ---
8
+
9
+ ## Recorded decisions (`.decisions/`)
10
+
11
+ This repository keeps a record of settled decisions — especially approaches
12
+ that were **tried and rejected** — in `.decisions/`.
13
+
14
+ **At the start of a session**, call `decisions_check` with no arguments. You get
15
+ a compact index (id, type, title, tags) of everything already settled. It is
16
+ short by design — read it once.
17
+
18
+ **Before proposing a technical approach**, call `decisions_check` with keywords
19
+ for what you are about to suggest, and read the full entries that come back. If
20
+ the topic is covered, do not re-propose the rejected option. You may propose it
21
+ again only if you can point to concrete evidence that the stated `revisit_when`
22
+ condition has now been met.
23
+
24
+ Matching is keyword-based, so **query with the nouns the entry would use**, and
25
+ try more than one phrasing before concluding nothing is recorded. An empty
26
+ result means "nothing found", not "nothing decided".
27
+
28
+ **When the user rejects one of your suggestions**, or when you discover that an
29
+ approach does not work, call `decisions_record` with:
30
+ - `title` — what was ruled out
31
+ - `why` — the specific reason it failed (not "it didn't work"; say what broke)
32
+ - `type: rejected`
33
+ - `evidence` — a commit sha, PR number, or URL a human can check later
34
+ - `revisit_when` — strongly preferred: the condition that would change the answer
35
+
36
+ Every entry expires (90 days for a rejection by default) and then stops being
37
+ shown to you. If a decision is genuinely permanent, say so to the user and let
38
+ them decide — do not set a long TTL yourself to work around this.
39
+
40
+ **When you are asked to plan or design**, call `decisions_stale` first and
41
+ surface anything that needs re-confirmation. Do not silently follow a stale
42
+ entry; ask. A stale entry is a question for the user, not an instruction.
43
+
44
+ Never invent an entry, and never record something the user did not actually
45
+ decide. A wrong entry is worse than a missing one, because it will be trusted
46
+ for months.
47
+
48
+ ---
49
+
50
+ ## Registering the MCP server
51
+
52
+ `decisions_check` / `decisions_record` / `decisions_stale` come from the whynot
53
+ MCP server. Add it to your agent's MCP config:
54
+
55
+ ```json
56
+ {
57
+ "mcpServers": {
58
+ "whynot": {
59
+ "command": "npx",
60
+ "args": ["-y", "whynot-cli", "mcp"]
61
+ }
62
+ }
63
+ }
64
+ ```
65
+
66
+ If your agent reads `AGENTS.md` but not MCP, the fallback still works: the
67
+ entries are plain Markdown in `.decisions/`, so the agent can read and grep them
68
+ directly. That is intentional — the format is the product, and it keeps working
69
+ without the tool.
@@ -0,0 +1,60 @@
1
+ # whynot —— 把这段粘进你的 AGENTS.md / CLAUDE.md
2
+
3
+ 下面这段是 whynot 的**行为部分**。MCP server 只是让 agent 有能力读写;真正让它
4
+ 主动去做的,是这段文字。请原样复制。
5
+
6
+ ---
7
+
8
+ ## 已记录的决策(`.decisions/`)
9
+
10
+ 这个仓库在 `.decisions/` 里记录已经定下来的决策——尤其是**试过并否决**的方案。
11
+
12
+ **每次会话开始时**,不带参数调用 `decisions_check`。你会得到一个紧凑的索引
13
+ (id、类型、标题、标签),里面是已经定下的所有事。它故意做得很短——读一遍就行。
14
+
15
+ **在提出技术方案之前**,用描述你即将建议的关键词调用 `decisions_check`,然后把
16
+ 返回的完整条目读完。如果这个话题已经被覆盖,不要再提被否决的选项。除非你能
17
+ 指出明确证据说明 `revisit_when` 条件已经满足,否则不要重新提议。
18
+
19
+ 匹配是基于关键词的,所以**用条目里可能出现的名词去查**,并且在得出「没有记录」
20
+ 之前,换几种说法再试一次。返回空表示「没找到」,不表示「没定过」。
21
+
22
+ **当用户否决了你的某个建议**,或者你发现某个方案行不通时,调用 `decisions_record`:
23
+
24
+ - `title` —— 被否决的是什么
25
+ - `why` —— 具体是什么失败了(不要写「它不工作」;说清楚哪里崩了)
26
+ - `type: rejected`
27
+ - `evidence` —— 一条 commit sha、PR 号,或者一个 URL,让人以后能核对
28
+ - `revisit_when` —— 强烈建议填写:什么条件会改变这个结论
29
+
30
+ 每条记录都会过期(否决类默认 90 天),过期后就不再展示给你。如果某个决策确实
31
+ 永久有效,告诉用户,让他们决定——不要自己设一个超长 TTL 来绕过这个机制。
32
+
33
+ **当你被要求做规划或设计时**,先调用 `decisions_stale`,把需要重新确认的东西
34
+ 摆出来。不要默默遵循一条过期条目;先问。**过期条目是给用户的问题,不是给你的
35
+ 指令。**
36
+
37
+ 永远不要编造条目,也不要把用户没有真正决定过的事记进去。一条错误的记录比没有
38
+ 记录更糟,因为它会被信任好几个月。
39
+
40
+ ---
41
+
42
+ ## 注册 MCP server
43
+
44
+ `decisions_check` / `decisions_record` / `decisions_stale` 来自 whynot 的 MCP
45
+ server。在你的 agent 的 MCP 配置里加上:
46
+
47
+ ```json
48
+ {
49
+ "mcpServers": {
50
+ "whynot": {
51
+ "command": "npx",
52
+ "args": ["-y", "whynot-cli", "mcp"]
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ 如果你的 agent 读 `AGENTS.md` 但不用 MCP,降级方案依然成立:记录就是
59
+ `.decisions/` 里的普通 Markdown,agent 可以直接读、直接 grep。这是刻意的设计
60
+ ——**格式本身就是产品**,所以没有工具它也能工作。
@@ -0,0 +1,75 @@
1
+ # Contributing
2
+
3
+ Thanks for looking. This project is small on purpose, and a few design
4
+ decisions are not open for negotiation. Read this before opening a PR.
5
+
6
+ ## The four constraints
7
+
8
+ These are the reason the tool exists. A PR that breaks one will be declined,
9
+ even if it is well written and useful in isolation.
10
+
11
+ 1. **The data stays plain Markdown in git.** No database, no binary format, no
12
+ schema that requires the tool to read. If this project dies, users must be
13
+ able to `grep` their own files.
14
+
15
+ 2. **No vector store, no embeddings, no model call on the write path.**
16
+ Retrieval quality is not the bottleneck; auditability is. Vector stores and
17
+ LLM-extracted memories are the failure modes this project is a response to.
18
+
19
+ 3. **No daemon, no background process, no file watching.** The tool runs when
20
+ it is called. Background processes are how these tools become unreliable and
21
+ then abandoned.
22
+
23
+ 4. **No hosted service, no telemetry, no account.** Decisions about someone's
24
+ codebase are the most sensitive thing in their repository. They stay on the
25
+ user's machine.
26
+
27
+ If you think one of these should change, open an issue and make the argument
28
+ with evidence. The constraints are load-bearing, not aesthetic.
29
+
30
+ ## What is welcome
31
+
32
+ - Bug fixes, especially cross-platform ones.
33
+ - Better heuristics for `whynot audit`, with tests.
34
+ - Documentation that is shorter or clearer than what it replaces.
35
+ - Additional agent integrations that reuse the existing file format.
36
+ - Reproductions of the validation experiments on other models. If a model
37
+ behaves differently from what `docs/validation.md` reports, that is a useful
38
+ finding — please share it.
39
+
40
+ ## What will be declined
41
+
42
+ - Anything that makes the tool heavier without removing a reported problem.
43
+ - New configuration surface. Every option is a thing someone has to understand.
44
+ - "Make it work for teams" features that require a server.
45
+ - PRs that rewrite the README into marketing copy.
46
+
47
+ ## Before you send a PR
48
+
49
+ ```bash
50
+ node --test "test/*.test.js"
51
+ ```
52
+
53
+ There are no dependencies to install. If a test fails on your machine but not
54
+ in CI, that is a bug worth reporting — the project claims to work on Windows,
55
+ macOS, and Linux.
56
+
57
+ Keep commits focused. One change per PR. Explain *what problem it removes*, not
58
+ what feature it adds.
59
+
60
+ ## Style
61
+
62
+ - Plain language in docs. Short sentences. No superlatives.
63
+ - Comments explain *why*, not *what*. If the code needs a comment to say what it
64
+ does, rename something instead.
65
+ - No emoji in source files.
66
+ - Chinese and English docs are kept in sync. If you change one, change both or
67
+ flag it in the PR.
68
+
69
+ ## Security and privacy
70
+
71
+ Do not include personal paths, usernames, API keys, or the contents of anyone's
72
+ `~/.codex` or `~/.claude` directory in a PR or an issue. `experiments/out/` is
73
+ gitignored for this reason; keep it that way.
74
+
75
+ If you find a leak, open an issue without pasting the leaked value.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stakenshake
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 CHANGED
@@ -1,3 +1,275 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="assets/icon-dark.svg">
4
+ <img src="assets/icon-light.svg" alt="whynot" width="88">
5
+ </picture>
6
+ </p>
2
7
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
8
+ <h1 align="center">whynot</h1>
9
+
10
+ <p align="center">
11
+ <b>Your agent keeps re-suggesting what you already rejected.</b><br>
12
+ Record the rejection once. It expires. Your agent stops bringing it up.
13
+ </p>
14
+
15
+ <p align="center">
16
+ <a href="https://www.npmjs.com/package/whynot-cli"><img src="https://img.shields.io/npm/v/whynot-cli?style=flat-square&color=111111&label=npm" alt="npm"></a>
17
+ <img src="https://img.shields.io/badge/license-MIT-blue?style=flat-square" alt="MIT">
18
+ <img src="https://img.shields.io/badge/node-%E2%89%A520-brightgreen?style=flat-square" alt="node 20+">
19
+ <img src="https://img.shields.io/badge/tests-29%20passing-brightgreen?style=flat-square" alt="29 tests passing">
20
+ <img src="https://img.shields.io/badge/dependencies-0-brightgreen?style=flat-square" alt="zero dependencies">
21
+ </p>
22
+
23
+ <p align="center">
24
+ <img src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey?style=flat-square" alt="platforms">
25
+ <img src="https://img.shields.io/badge/MCP-compatible-8A2BE2?style=flat-square" alt="MCP compatible">
26
+ <img src="https://img.shields.io/badge/Claude%20Code%20%C2%B7%20Codex%20%C2%B7%20Cursor%20%C2%B7%20Gemini%20CLI-supported-informational?style=flat-square" alt="agent support">
27
+ </p>
28
+
29
+ <p align="center">
30
+ <a href="README.md">English</a> · <a href="README.zh-CN.md">简体中文</a>
31
+ </p>
32
+
33
+ ---
34
+
35
+ <p align="center">
36
+ <a href="#the-problem">The problem</a> ·
37
+ <a href="#install">Install</a> ·
38
+ <a href="#quick-start">Quick start</a> ·
39
+ <a href="#how-it-works">How it works</a> ·
40
+ <a href="#what-the-evidence-says">Evidence</a> ·
41
+ <a href="#docs">Docs</a> ·
42
+ <a href="#license">License</a>
43
+ </p>
44
+
45
+ ---
46
+
47
+ ## The problem
48
+
49
+ You tried Redis for session storage. It failed at 8 seconds during a regional
50
+ outage, so you moved to Postgres.
51
+
52
+ Three weeks later, in a new session, your agent suggests Redis.
53
+
54
+ You explain again. A month after that you switch from Claude Code to Codex, and
55
+ explain a third time. Six months later you no longer remember why Redis was
56
+ rejected — and neither does anyone else on the team.
57
+
58
+ **Code remembers what you chose. Nothing remembers what you rejected.**
59
+
60
+ Memory tools record what happened. That is already in your commits, your pull
61
+ requests, and the code itself. `whynot` records the other half: the options you
62
+ ruled out, and the reason, in a file your agent reads before it proposes
63
+ anything.
64
+
65
+ ---
66
+
67
+ ## Install
68
+
69
+ ```bash
70
+ npx whynot-cli init
71
+ ```
72
+
73
+ Nothing to install globally, no account, no telemetry. Node 20+ is the only
74
+ requirement.
75
+
76
+ ---
77
+
78
+ ## Quick start
79
+
80
+ ### 1. See what will never expire
81
+
82
+ `audit` is read-only. It scans your existing `AGENTS.md`, `CLAUDE.md`, and
83
+ `.cursor/rules` for absolute rules that state no condition — the ones that will
84
+ be obeyed forever.
85
+
86
+ ```bash
87
+ npx whynot-cli audit
88
+ ```
89
+
90
+ ```
91
+ Scanned 2 agent config files:
92
+ AGENTS.md (last changed 412d ago)
93
+ CLAUDE.md (last changed 8d ago)
94
+
95
+ 3 rules look like permanent constraints:
96
+
97
+ AGENTS.md:3
98
+ "Never use TypeScript in this repo."
99
+ → absolute rule with no stated condition; unchanged for 412 days
100
+ ```
101
+
102
+ ### 2. Create the store
103
+
104
+ ```bash
105
+ npx whynot-cli init
106
+ ```
107
+
108
+ This creates `.decisions/` and adds `*.md text eol=lf` to `.gitattributes`.
109
+
110
+ ### 3. Record a rejection
111
+
112
+ ```bash
113
+ npx whynot-cli add \
114
+ --title "session 不存 Redis" \
115
+ --why "us-east 故障时读延迟 8s,SLO 要求 p99 < 500ms" \
116
+ --type rejected \
117
+ --revisit "Redis 支持跨区域强一致时" \
118
+ --evidence pr:412 \
119
+ --tags infra,session
120
+ ```
121
+
122
+ ```markdown
123
+ ---
124
+ id: 0001
125
+ title: session 不存 Redis
126
+ type: rejected
127
+ status: live
128
+ created: 2026-09-21
129
+ expires_on: 2026-12-20
130
+ revisit_when: "Redis 支持跨区域强一致时"
131
+ evidence:
132
+ - "pr:412"
133
+ tags: [infra, session]
134
+ ---
135
+
136
+ ## 结论
137
+ 不能把 session 存 Redis。
138
+
139
+ ## 为什么
140
+ 试过 Redis + 单写区域。us-east 故障时读延迟飙到 8s,SLO 要求 p99 < 500ms。
141
+ ```
142
+
143
+ ### 4. Give it to your agent
144
+
145
+ Add the MCP server:
146
+
147
+ ```json
148
+ {
149
+ "mcpServers": {
150
+ "whynot": {
151
+ "command": "npx",
152
+ "args": ["-y", "whynot-cli", "mcp"]
153
+ }
154
+ }
155
+ }
156
+ ```
157
+
158
+ Then paste the block from [`AGENTS.snippet.md`](AGENTS.snippet.md) into your
159
+ `AGENTS.md` or `CLAUDE.md`. The MCP server gives the agent the tools; the
160
+ snippet is what makes it actually use them.
161
+
162
+ > **If MCP is unavailable, the format still works.** `.decisions/` is plain
163
+ > Markdown, so the agent can read and grep it directly. This was observed in the
164
+ > real-agent test, not assumed.
165
+
166
+ ---
167
+
168
+ ## How it works
169
+
170
+ ### Every entry expires
171
+
172
+ | Type | Default lifetime |
173
+ |---|---|
174
+ | `rejected` | 90 days |
175
+ | `decision` | 180 days |
176
+ | `constraint` | 365 days |
177
+
178
+ When an entry lapses it **stops reaching your agent**. Not annotated, not
179
+ warned about — absent. `whynot stale` lists it, and you either renew it or
180
+ retire it.
181
+
182
+ This is the core of the design. Telling an agent "this note is old, be
183
+ skeptical" was tested and does not work. Removing the note from view does.
184
+
185
+ ### Three entry types
186
+
187
+ | Type | Use it for |
188
+ |---|---|
189
+ | **`rejected`** | An approach you tried and abandoned. The most valuable kind — nothing else in the repository remembers it. |
190
+ | `decision` | A choice between viable options, with the reason |
191
+ | `constraint` | A hard requirement the project must satisfy |
192
+
193
+ ### Written for humans, by design
194
+
195
+ - **Markdown in git.** Read it, diff it, review it, revert it. If this tool
196
+ disappears, `grep` still works.
197
+ - **No vector store, no embeddings, no model call on write.** Each of those is
198
+ a failure mode: index staleness, write-path cost, and results nobody can
199
+ audit.
200
+ - **No daemon, no telemetry, no hosted service.** Your reasoning about your own
201
+ codebase stays on your machine.
202
+
203
+ ---
204
+
205
+ ## What the evidence says
206
+
207
+ Six experiments, three model families, mostly n=20 per cell.
208
+
209
+ | Question | Result |
210
+ |---|---|
211
+ | Does the age of a decision matter? | **No.** 30 days and 7 years were obeyed at the same rate (0/12 each). Expired → 12/12. |
212
+ | Does recording a rejection change the answer? | **Yes.** 78–87 percentage points, p < 0.0001. |
213
+ | Does an irrelevant record distort unrelated work? | **No.** 0/30. |
214
+ | Does a real agent use it across sessions? | **Yes.** Codex respected the decision in 9/9 sessions. |
215
+
216
+ Two qualifications worth knowing before you install:
217
+
218
+ - **About one in five responses resisted a stale record** — by attacking its
219
+ stated reason. A reason that names a *condition* ("the team did not know
220
+ TypeScript") invites re-checking; one that states a *property* ("this project
221
+ changes constantly") does not.
222
+ - **With real code in context, some agents infer the answer from the repository**
223
+ and ignore the record. Records matter most for what code cannot express: the
224
+ option you rejected.
225
+
226
+ Full results, methods, and reproduction scripts: [`docs/findings.md`](docs/findings.md).
227
+
228
+ ---
229
+
230
+ ## Docs
231
+
232
+ | | |
233
+ |---|---|
234
+ | [Design](docs/design.md) | Why the tool looks like this |
235
+ | [Findings](docs/findings.md) | Every experiment, what it found, what it did not |
236
+ | [Validation](docs/validation.md) | Index of the detailed write-ups |
237
+ | [Pre-registration](docs/preregistration.md) | Decision rules fixed before the data |
238
+ | [Contributing](CONTRIBUTING.md) | The four design constraints that are not negotiable |
239
+ | [AGENTS snippet](AGENTS.snippet.md) | The block that makes your agent use this |
240
+
241
+ ---
242
+
243
+ ## CLI reference
244
+
245
+ ```
246
+ whynot init Create .decisions/ and the .gitattributes rule
247
+ whynot add --title T --why W Record an entry
248
+ [--type rejected|decision|constraint]
249
+ [--revisit "condition"] When to reconsider
250
+ [--ttl N] Days until it expires
251
+ [--expires never] Explicitly permanent
252
+ [--evidence pr:412] Repeatable
253
+ [--tags infra,session]
254
+ whynot list [--all] List live entries
255
+ whynot check "<query>" Show live entries matching a query
256
+ whynot stale Entries needing a human decision
257
+ whynot renew <id> [--days N] Extend an entry's life
258
+ whynot retire <id> Archive an entry (never deletes)
259
+ whynot audit [--days N] Scan agent config files (read-only)
260
+ whynot doctor Validate the store
261
+ whynot mcp Run the MCP server on stdio
262
+ ```
263
+
264
+ ---
265
+
266
+ ## License
267
+
268
+ MIT. No account, no telemetry, no paid tier, no closed binary.
269
+ There is nothing to upsell you.
270
+
271
+ ---
272
+
273
+ <p align="center">
274
+ <sub><a href="README.zh-CN.md">简体中文</a> · <a href="https://github.com/Stakenshake/whynot">GitHub</a> · <a href="https://www.npmjs.com/package/whynot-cli">npm</a></sub>
275
+ </p>