whygit 0.1.1 → 0.1.3

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 (3) hide show
  1. package/README.md +46 -38
  2. package/package.json +2 -2
  3. package/src/cli.js +6 -1
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
- <div align="center">
1
+ <div align="center">
2
2
 
3
3
  # whygit
4
4
 
5
5
  **Git blame tells you *who*. whygit tells you *why*.**
6
6
 
7
7
  Point it at any repository. It turns the reasons already buried in git history
8
- into answers you and your coding agent can cite 鈥?file, line, commit, author, date.
8
+ into answers you and your coding agent can cite — file, line, commit, author, date.
9
9
 
10
10
  [![npm](https://img.shields.io/npm/v/whygit?color=cb3837)](https://www.npmjs.com/package/whygit)
11
11
  [![tests](https://github.com/carefreebit/whygit/actions/workflows/ci.yml/badge.svg)](https://github.com/carefreebit/whygit/actions/workflows/ci.yml)
@@ -13,7 +13,7 @@ into answers you and your coding agent can cite 鈥?file, line, commit, author,
13
13
  [![license](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
14
14
  [![MCP](https://img.shields.io/badge/MCP-ready-6b46c1)](#give-it-to-your-agent)
15
15
 
16
- [涓枃](README.zh-CN.md) 路 [Quickstart](#quickstart) 路 [Commands](#commands) 路 [Agents](#give-it-to-your-agent) 路 [How it works](#how-it-works)
16
+ [中文](README.zh-CN.md) · [Quickstart](#quickstart) · [Commands](#commands) · [Agents](#give-it-to-your-agent) · [How it works](#how-it-works)
17
17
 
18
18
  </div>
19
19
 
@@ -23,9 +23,9 @@ into answers you and your coding agent can cite 鈥?file, line, commit, author,
23
23
  <img src="https://raw.githubusercontent.com/carefreebit/whygit/main/docs/demo.svg" alt="whygit why &quot;cookie jar&quot; in a terminal" width="900">
24
24
  </p>
25
25
 
26
- <p align="center"><sub>Real output: <code>whygit why "cookie jar"</code> on <code>psf/requests</code> 鈥?4,884 commits indexed in 3s, answered in 21ms.</sub></p>
26
+ <p align="center"><sub>Real output: <code>whygit why "cookie jar"</code> on <code>psf/requests</code> — 4,884 commits indexed in 3s, answered in 21ms.</sub></p>
27
27
 
28
- That answer existed all along 鈥?in a commit body from 2015 that nobody has read since.
28
+ That answer existed all along — in a commit body from 2015 that nobody has read since.
29
29
  whygit found it in 21 milliseconds, without an LLM, an API key, or a network call.
30
30
 
31
31
  ## The problem
@@ -38,7 +38,7 @@ redundant. A guard that seems paranoid. A `sleep(2)` that looks like laziness.
38
38
  - LLM summaries of history **invent plausible reasons**. In a codebase, a plausible
39
39
  wrong reason is worse than no reason.
40
40
  - Coding agents are the worst affected: they rewrite the guard, delete the retry, and
41
- the bug comes back six months later 鈥?because nothing told them the line was load-bearing.
41
+ the bug comes back six months later — because nothing told them the line was load-bearing.
42
42
 
43
43
  whygit reads what your repository already wrote down, extracts the sentences that
44
44
  actually explain a decision, and makes them retrievable in the two places that matter:
@@ -68,7 +68,7 @@ it is incremental: new commits are picked up automatically on the next run.
68
68
  | `whygit why <words>` | Why does this behaviour exist? Ranked decisions with citations. |
69
69
  | `whygit why <file>:<line>` | Why is *this line* still here? Blame + the decision behind it + how much of that change is still alive. |
70
70
  | `whygit story <file>` | The biography of one file: birth, commits per year, the chapters that mattered, who kept it alive. |
71
- | `whygit cursed` | Ranked risk map: churn 脳 fix density 脳 author spread 脳 freshness. Read before refactoring. |
71
+ | `whygit cursed` | Ranked risk map: churn × fix density × author spread × freshness. Read before refactoring. |
72
72
  | `whygit whylog --write` | Generate `WHYLOG.md`: load-bearing decisions + cursed files, in Markdown. |
73
73
  | `whygit check --base origin/main` | CI gate: what fraction of newly added lines trace to a commit that said why. |
74
74
  | `whygit mcp` | MCP server over stdio for Claude Code, Cursor, Codex, and any MCP client. |
@@ -83,30 +83,30 @@ Every command takes `--json` for scripting, and `--cwd <path>` to point at anoth
83
83
  $ whygit why src/requests/models.py:120
84
84
 
85
85
  1. basics working
86
- 2012-12-15 路 366e8e84 路 Kenneth Reitz 路 change
86
+ 2012-12-15 · 366e8e84 · Kenneth Reitz · change
87
87
  line 120 is 78 lines of this change, still in HEAD
88
88
 
89
89
  2. Add hasattr checks for remaining protocol isinstance checks (#7505)
90
- 2026-06-09 路 6f66281a 路 change
90
+ 2026-06-09 · 6f66281a · change
91
91
  ```
92
92
 
93
93
  The line itself is unremarkable. What matters is that 78 lines around it are still
94
- the original 2012 change 鈥?so the surrounding shape is load-bearing, not incidental.
94
+ the original 2012 change — so the surrounding shape is load-bearing, not incidental.
95
95
 
96
96
  ### `story <file>`
97
97
 
98
98
  ```console
99
99
  $ whygit story src/requests/models.py
100
100
 
101
- born 2011-05-14 路 package refactor
101
+ born 2011-05-14 · package refactor
102
102
 
103
- 2011 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅路路路路路路 217
104
- 2012 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅 254
105
- 2013 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅路路路路路路路路路路路路路路路路路路路路路路路路路路 86
103
+ 2011 ██████████████████████████████████······ 217
104
+ 2012 ████████████████████████████████████████ 254
105
+ 2013 ██████████████·························· 86
106
106
  ...
107
107
 
108
- 717 commits 路 202 authors 路 154 fixes 路 0 TODO markers
109
- 鈹€鈹€鈹€ the plot 鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€鈹€
108
+ 717 commits · 202 authors · 154 fixes · 0 TODO markers
109
+ ─── the plot ──────────────────────────────────────────────────
110
110
 
111
111
  2015-04-06 Copy a PreparedRequest's CookieJar reliably
112
112
  By adding _copy_cookie_jar we can reliably copy cookie jars so that we
@@ -119,12 +119,12 @@ $ whygit story src/requests/models.py
119
119
  ```console
120
120
  $ whygit cursed
121
121
 
122
- 1 src/requests/models.py 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅 8.1 717 commits 路 202 authors 路 154 fixes
123
- 2 src/requests/sessions.py 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻埪? 7.5 334 commits 路 110 authors 路 79 fixes
124
- 3 tests/test_requests.py 鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅鈻堚枅路路 7.2 366 commits 路 104 authors 路 76 fixes
122
+ 1 src/requests/models.py ██████████████ 8.1 717 commits · 202 authors · 154 fixes
123
+ 2 src/requests/sessions.py █████████████· 7.5 334 commits · 110 authors · 79 fixes
124
+ 3 tests/test_requests.py ████████████·· 7.2 366 commits · 104 authors · 76 fixes
125
125
  ```
126
126
 
127
- Not a code-quality judgement 鈥?a map of where history is dense enough that a
127
+ Not a code-quality judgement — a map of where history is dense enough that a
128
128
  confident rewrite is likely to be wrong.
129
129
 
130
130
  ## Give it to your agent
@@ -159,17 +159,18 @@ Before changing a line you do not understand, call `why_line` (MCP: whygit).
159
159
  If a past decision contradicts your plan, say so before editing.
160
160
  ```
161
161
 
162
- ### WHYLOG.md 鈥?history your agent reads without being asked
162
+ ### WHYLOG.md — history your agent reads without being asked
163
163
 
164
164
  `whygit whylog --write` produces a Markdown file that is small enough to live in a
165
165
  repo and specific enough to change a decision:
166
166
 
167
- - **Load-bearing decisions** 鈥?ranked, each with the verbatim reason, date, commit, author, and the files it touches.
168
- - **The cursed files** 鈥?a table of where history is thickest.
169
- - **House rule** 鈥?the one-line convention (`WHY: ...`) that makes future commits explain themselves.
167
+ - **Load-bearing decisions** — ranked, each with the verbatim reason, date, commit, author, and the files it touches.
168
+ - **The cursed files** — a table of where history is thickest.
169
+ - **House rule** — the one-line convention (`WHY: ...`) that makes future commits explain themselves.
170
170
 
171
171
  It is generated from git, never hand-edited, and it diffs cleanly in review.
172
- Commit it, and every agent that starts in the repo gets the context for free 鈥?no MCP required.
172
+ Commit it, and every agent that starts in the repo gets the context for free —
173
+ no MCP required.
173
174
 
174
175
  ### CI gate
175
176
 
@@ -184,18 +185,25 @@ It is a nudge, not a linter: the fix is one sentence in the commit body.
184
185
 
185
186
  ```text
186
187
  git log (two passes: metadata + numstat)
187
- 鈹? 鈻? signals: strip trailers, drop bot noise, de-duplicate squash-merge subjects,
188
+ │
189
+ ▼
190
+ signals: strip trailers, drop bot noise, de-duplicate squash-merge subjects,
188
191
  keep sentences that contain a reason ("because", "so that", "to avoid",
189
- "鐢变簬", "閬垮厤", "鍥炴粴" ...)
190
- 鈹? 鈻? SQLite .git/whygit/index.db commits 路 files 路 decisions 路 FTS5 路 risk
191
- 鈹? 鈻? ranking: BM25 脳 confidence 脳 churn 脳 recency 脳 fix-density 脳 noise penalties
192
- 鈹? 鈹溾攢鈹€ whygit why / story / cursed (terminal)
193
- 鈹溾攢鈹€ whygit whylog (WHYLOG.md)
194
- 鈹斺攢鈹€ whygit mcp (agents)
192
+ "由于", "避免", "回滚" ...)
193
+ │
194
+ ▼
195
+ SQLite .git/whygit/index.db commits · files · decisions · FTS5 · risk
196
+ │
197
+ ▼
198
+ ranking: BM25 × confidence × churn × recency × fix-density × noise penalties
199
+ │
200
+ ├── whygit why / story / cursed (terminal)
201
+ ├── whygit whylog (WHYLOG.md)
202
+ └── whygit mcp (agents)
195
203
  ```
196
204
 
197
205
  **What a "decision" is:** a commit whose message contains a sentence explaining a
198
- choice. whygit does not interpret that sentence 鈥?it quotes it. If the message says
206
+ choice. whygit does not interpret that sentence — it quotes it. If the message says
199
207
  nothing, the decision has low confidence and ranks below the ones that do.
200
208
 
201
209
  **Why it can be fast:** the whole index is built from two `git log` passes, and
@@ -203,13 +211,13 @@ queries run against FTS5 in the same process. On a laptop, 4,884 commits index i
203
211
  ~3 seconds and a query takes ~20 ms. No network, no model, no cache to warm.
204
212
 
205
213
  **What it will not do:** invent a reason, summarise code, or judge quality. If the
206
- history is silent, whygit says so 鈥?silence is a finding too.
214
+ history is silent, whygit says so — silence is a finding too.
207
215
 
208
216
  ## Compared to
209
217
 
210
218
  | | Answers "why" | Cites the source | Works offline | Agent-native |
211
219
  | --- | :---: | :---: | :---: | :---: |
212
- | `git blame` | no | 鈥?| yes | no |
220
+ | `git blame` | no | — | yes | no |
213
221
  | `git log` | only if you already know where to look | yes | yes | no |
214
222
  | LLM-generated docs | sometimes | rarely | no | partly |
215
223
  | **whygit** | **yes** | **yes (sha + file + line)** | **yes** | **yes (MCP + WHYLOG.md)** |
@@ -238,7 +246,7 @@ index under `.git/`. History is read-only.
238
246
  - Rationale can only be as good as commit messages. whygit makes silence visible; it cannot fill it.
239
247
  - Very large histories (500k+ commits) index in minutes, not seconds.
240
248
  - Renames are followed by path, not by content similarity: a file moved twice may have two histories.
241
- - The ranking is heuristic. It is tuned to prefer cited reasons over churn, and it will still be wrong sometimes 鈥?each result shows its citations so you can check.
249
+ - The ranking is heuristic. It is tuned to prefer cited reasons over churn, and it will still be wrong sometimes — each result shows its citations so you can check.
242
250
 
243
251
  ## Contributing
244
252
 
@@ -249,7 +257,7 @@ npm run lint
249
257
  node bin/whygit.js why "..." --cwd /path/to/any/repo
250
258
  ```
251
259
 
252
- Issues and PRs welcome 鈥?especially real-world ranking failures. Paste the query, the
260
+ Issues and PRs welcome — especially real-world ranking failures. Paste the query, the
253
261
  repo, and what should have ranked first.
254
262
 
255
263
  ## License
@@ -263,6 +271,6 @@ MIT. See [LICENSE](LICENSE).
263
271
  If whygit saved you from deleting a load-bearing line, a star helps the next person
264
272
  find it.
265
273
 
266
- **[猸?Star whygit](https://github.com/carefreebit/whygit)**
274
+ **[⭐ Star whygit](https://github.com/carefreebit/whygit)**
267
275
 
268
276
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whygit",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Git blame tells you who. whygit tells you why. Turn git history into cited, agent-readable context.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,7 @@
18
18
  "LICENSE"
19
19
  ],
20
20
  "scripts": {
21
- "test": "node --test test/signals.test.js test/index.test.js",
21
+ "test": "node --test test/signals.test.js test/index.test.js test/version.test.js",
22
22
  "lint": "node scripts/lint.js"
23
23
  },
24
24
  "keywords": [
package/src/cli.js CHANGED
@@ -1,5 +1,8 @@
1
1
  /** whygit command line. */
2
2
  import process from 'node:process';
3
+ import { readFileSync } from 'node:fs';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
3
6
  import { c, width } from './util.js';
4
7
  import { cmdWhy } from './cmd-why.js';
5
8
  import { cmdStory } from './cmd-story.js';
@@ -12,7 +15,9 @@ import { loadContext, repoLabel } from './context.js';
12
15
  import { buildWhylog, writeWhylog } from './whylog.js';
13
16
  import { header } from './ui.js';
14
17
 
15
- const VERSION = '0.1.0';
18
+ const VERSION = JSON.parse(
19
+ readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'),
20
+ ).version;
16
21
 
17
22
  const HELP = [
18
23
  '',