@bglocation/tune-context 1.0.1 → 2.0.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.
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "bglocation",
3
+ "description": "Context-engineering tooling for Claude Code from bglocation — token-saving doctrine, configurators and semantic-search wiring.",
3
4
  "owner": {
4
5
  "name": "Szymon Walczak",
5
6
  "url": "https://bglocation.dev"
@@ -9,7 +10,7 @@
9
10
  "name": "tune-context",
10
11
  "source": "./",
11
12
  "description": "Context-efficiency configurator for Claude Code: doctrine skills (token-efficiency, caveman, cdd, phase-workflow) plus a tune-context configurator that scaffolds a lean CLAUDE.md, subagents, settings and hooks.",
12
- "version": "1.0.1",
13
+ "version": "2.0.0",
13
14
  "author": {
14
15
  "name": "Szymon Walczak",
15
16
  "url": "https://bglocation.dev"
@@ -2,7 +2,7 @@
2
2
  "name": "tune-context",
3
3
  "displayName": "tune-context",
4
4
  "description": "Context-efficiency configurator for Claude Code: doctrine skills (token-efficiency, caveman, cdd, phase-workflow) plus a tune-context configurator that scaffolds a lean CLAUDE.md, subagents, settings and hooks.",
5
- "version": "1.0.1",
5
+ "version": "2.0.0",
6
6
  "author": {
7
7
  "name": "Szymon Walczak",
8
8
  "url": "https://bglocation.dev"
package/CHANGELOG.md ADDED
@@ -0,0 +1,252 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@bglocation/tune-context` are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.0.0] — 2026-07-25
9
+
10
+ Three hardening epics' worth of work plus the first new feature since 1.0: a
11
+ reminder that measures what a session actually costs. **The major bump is for
12
+ one breaking change** — `tune-context` with no subcommand no longer runs `init`
13
+ (see below). Everything else is additive; see "Upgrading from 1.1.0" at the end.
14
+
15
+ ### Added
16
+
17
+ - **Context-size reminder** (`hooks/context-reminder.mjs`, `UserPromptSubmit`).
18
+ Claude can't see its own context usage — nothing in a turn reports how many
19
+ tokens are being re-sent, and Claude Code has no timer or threshold event — so
20
+ this hook reads the tail of the session transcript, sums the tokens actually
21
+ re-sent, and once that passes a PO-frozen **120k** injects one short note
22
+ suggesting `/compact`. Fires **once per crossing**, re-arming only after a
23
+ compaction-sized drop: the note costs tokens on the turn it appears in, so
24
+ nagging every prompt would spend exactly what the tool saves (measured on a
25
+ 12.6h session: 4 reminders total). Reminds on *size*, not "phase boundary" —
26
+ size is measurable, a phase boundary is a judgment, so the reminder hands the
27
+ timing call to the model and tells it to stay quiet mid-task. Override with
28
+ `TUNE_CONTEXT_REMIND_TOKENS`; remove the `UserPromptSubmit` entry to disable.
29
+ (TASK-079)
30
+
31
+ ### Fixed
32
+
33
+ - **Plugin-shaped hook commands no longer misread as broken.** `cli/verify.mjs`
34
+ treated any registered command it couldn't resolve to an installed file as
35
+ stray or dead — including a plugin's own `${CLAUDE_PLUGIN_ROOT}`-based
36
+ registration, which it was never meant to check. Now reported as its own
37
+ always-ok informational line instead. (EPIC-008 / TASK-072)
38
+ - **Adoption report no longer prints a false churn verdict over doubled
39
+ events.** Running the plugin and `tune-context init` together on one repo
40
+ registers each hook twice, so every event logs twice — this previously
41
+ surfaced as spurious "elevated re-read churn." The report now detects a
42
+ near-duplicate signature (records identical on session/tool/lever/detail
43
+ within a measured 100ms window, at ≥10% of assessable records) and withholds
44
+ the re-read-churn verdict instead of printing a number the doubling would
45
+ have faked. Thresholds are frozen from two independent real logs, not
46
+ guessed — a naively "safe" 1000ms window was shown to swallow real churn
47
+ instead. (EPIC-008 / TASK-072)
48
+ - **Generated commands now match the detected package manager exactly.**
49
+ `CLAUDE.md` and `permissions.allow` used to assume npm-style `run` commands
50
+ regardless of lockfile. Now derived from one `runCmd(packageManager, script)`
51
+ table: npm/pnpm use `npm run build` / `pnpm run build` (`test` keeps its
52
+ shorthand), yarn uses the bare short form for all three — `yarn build`,
53
+ `yarn test`, `yarn lint` (PO decision). (EPIC-008 / TASK-073)
54
+ - **`disabledMcpjsonServers` is now honoured.** A project `.mcp.json` server a
55
+ user rejected via `disabledMcpjsonServers` (in any settings file) no longer
56
+ gets a `permissions.allow` rule or a `CLAUDE.md` mention — previously
57
+ `tune-context init` wired it in regardless. Three neighbouring keys
58
+ (`enabledMcpjsonServers`, `allowedMcpServers`, `deniedMcpServers`) are
59
+ deliberately not treated as equivalent, verified against docs rather than
60
+ guessed — each would need introspection this CLI doesn't have.
61
+ (EPIC-008 / TASK-075)
62
+ - **PreCompact snapshot files could collide across projects.** `state/` keyed
63
+ snapshots by a human-readable slug of `cwd` alone, which is not injective —
64
+ `/home/a/b` and `/home/a-b` (also `.`/`_` variants) slugged to the same
65
+ filename, so `SessionStart(compact)` could reinject one project's git state
66
+ into an unrelated session. A short hash of the real `cwd` is now appended to
67
+ the filename. **Breaking for `state/` only**: snapshots written under the old
68
+ filename shape are orphaned (harmless — a snapshot is only ever valid for the
69
+ one compaction right after it) rather than reused; the new retention policy
70
+ below cleans them up over time. (EPIC-008 / TASK-076)
71
+ - **`pre-compact-snapshot.mjs` could exit non-zero.** Brought to parity with
72
+ `adoption-log.mjs`: an unwritable state directory (read-only volume,
73
+ misdirected `CLAUDE_CONFIG_DIR`) now fails the snapshot silently instead of
74
+ surfacing a nonzero exit from a hook at the exact moment a session is
75
+ compacting under context pressure. (EPIC-008 / TASK-076)
76
+ - **Manual hook-install instructions now produce a working hook.** README
77
+ pointed at the raw `settings.json.tmpl`, which still has an unsubstituted
78
+ `{{HOOKS_DIR}}` placeholder and no instruction to replace it — followed
79
+ literally, the hook command was `node "{{HOOKS_DIR}}/adoption-log.mjs"` and
80
+ silently never ran. Replaced with a ready-to-paste `settings.json` snippet.
81
+ (EPIC-008 / TASK-078)
82
+ - **Plugin-only installs now get a working `tune-context` command.** Claude
83
+ Code exposes `bin/` files under their literal filename — confirmed against
84
+ current docs, no extension-stripping — so `bin/tune-context.mjs` only ever
85
+ produced a bare `tune-context.mjs`. A plugin-only install (no
86
+ `npm install -g`) got "command not found" for the command README documents.
87
+ Added an extensionless `bin/tune-context` alias so both channels give the
88
+ identical command. (EPIC-008 / TASK-078)
89
+ - **`tune-context --cwd` (no value) no longer crashes with a Node stacktrace.**
90
+ `resolve(undefined)` used to throw `ERR_INVALID_ARG_TYPE` straight to the
91
+ terminal; now reports "Missing value for `--cwd`" plus usage, on stderr,
92
+ exit 1. **Breaking:** an unrecognized *or missing* subcommand is now also
93
+ rejected the same way instead of silently running `init` — `init` is the
94
+ only accepted subcommand. README never documented calling this tool with no
95
+ arguments; every example already used explicit `init`. (EPIC-008 / TASK-078)
96
+
97
+ ### Changed
98
+
99
+ - **One source of truth for `CLAUDE_CONFIG_DIR` resolution.** `cli/detect.mjs`'s
100
+ `claudeDirs()` now delegates the `CLAUDE_CONFIG_DIR || ~/.claude` rule to
101
+ `hooks/config-dir.mjs`'s `claudeBaseDir()` instead of re-implementing it —
102
+ the two could previously drift apart. No behavior change.
103
+ (EPIC-008 / TASK-074)
104
+ - **Removed a dead `permissions` block from the settings template.**
105
+ `settings.json.tmpl` carried a `permissions.allow` block nothing ever read —
106
+ the CLI merges its own, computed from the detected stack, straight into the
107
+ *project's* settings instead. PO-approved removal. (EPIC-008 / TASK-074)
108
+ - **`bin/tune-context.mjs`'s header states its real scope.** No longer reads
109
+ as a full twin of the `/tune-context` skill — names the two steps it
110
+ deliberately can't do (merging in a semantic-search MCP server, wiki-lever
111
+ wiring) instead of leaving the gap unstated. (EPIC-008 / TASK-075)
112
+
113
+ ### Added
114
+
115
+ - **`state/` retention.** Snapshots untouched for 30 days are pruned on the
116
+ next PreCompact write anywhere on the machine — `state/` is one shared
117
+ directory across all projects, so any project's compaction sweeps it, not
118
+ only the project being written; the snapshot just written is never a prune
119
+ candidate. Previously `state/` was the only tune-context artifact with no
120
+ cleanup policy. (EPIC-008 / TASK-076)
121
+ - **`tune-context --version`** (also `-v`), printing the bare number so
122
+ `$(tune-context --version)` is directly comparable. Read from the shipped
123
+ `package.json` — the same file `verify:plugin` cross-checks against
124
+ `plugin.json` and `marketplace.json`, so the CLI can't report a version the
125
+ manifests disagree with.
126
+ - **README: "Keeping context lean while you work".** The tool configures a
127
+ lean setup, but shedding accumulated context stays a deliberate, recurring
128
+ act — `/compact` when a thread closes, `/clear` when switching tasks — and
129
+ the README never said so. Includes what the PreCompact/SessionStart pair
130
+ does and does not preserve across a compaction.
131
+ - **`token-efficiency` §6 recommends `eval/adoption-report.mjs`.** Previously
132
+ the section on measuring whether doctrine is actually used pointed only at
133
+ `rag-usage`, a different package's tool — silent about the one instrument
134
+ this repo ships for the same purpose. (EPIC-008 / TASK-078)
135
+
136
+ ### Testing / packaging
137
+
138
+ - **Unit test harness for the CLI.** `npm test` now also exercises
139
+ `cli/managed-block.mjs`, `cli/generate.mjs`, `cli/verify.mjs` and
140
+ `cli/detect.mjs` directly — previously only reachable end-to-end through
141
+ `smoke:init` — plus a structural guard that fails when a `cli/` export has
142
+ nothing importing it. (EPIC-008 / TASK-077)
143
+ - **`verify:plugin` and `verify:pack` now guard `bin/` and
144
+ `eval/adoption-report.mjs`.** Both were tracked by git accidentally, not by
145
+ contract, despite README telling a plugin-only or npm install to run them —
146
+ a clone or tarball missing either would previously pass both checks anyway.
147
+ (EPIC-008 / TASK-078)
148
+
149
+ ### Upgrading from 1.1.0
150
+
151
+ Re-running `tune-context init` over a 1.1.0 install is safe and was verified
152
+ end-to-end: hook entries are not duplicated, unrelated `settings.json` keys and
153
+ hand-written `CLAUDE.md` content are untouched, and your own
154
+ `permissions.allow` rules are kept.
155
+
156
+ Two leftovers are deliberate, because removing them would break "never
157
+ clobber" — tune-context cannot tell its own past output from a rule you added
158
+ on purpose:
159
+
160
+ - **pnpm/yarn projects**: the npm-style rules 1.1.0 wrote (`Bash(npm test:*)`
161
+ and friends) stay in `.claude/settings.json` alongside the correct new ones
162
+ (`Bash(pnpm test:*)`). Harmless — an unused allow rule grants nothing extra
163
+ for a command you never run — but safe to delete by hand.
164
+ - **PreCompact snapshots** under `<config dir>/tune-context/state/` written
165
+ before this release keep their old filenames and are ignored. The new
166
+ 30-day retention deletes them on its own; no action needed.
167
+
168
+ ## [1.1.0] — 2026-07-24
169
+
170
+ Two hardening epics land together: consistent `CLAUDE_CONFIG_DIR` support with the
171
+ guards to keep it honest (EPIC-006), and an adoption report that only claims what
172
+ the log can actually support (EPIC-007). Default-path behavior is unchanged — no
173
+ action needed on upgrade.
174
+
175
+ ### Added
176
+
177
+ - **`CLAUDE_CONFIG_DIR` honored across the whole hook lifecycle.** Setting Claude
178
+ Code's `CLAUDE_CONFIG_DIR` now moves *everything* user-level under the override —
179
+ not just the config dir, but registered hook command paths and hook **state**
180
+ (PreCompact snapshots, adoption log) too. Previously the override leaked: scripts
181
+ landed under `/x/`, but hook commands and state still pointed at `~/.claude`.
182
+ Documented in the README with a per-path table. (EPIC-006 / TASK-064, TASK-067)
183
+ - **Golden-fixture test harness** for the adoption report's pure functions
184
+ (`parseLog` / `aggregate` / `formatReport`), run by `npm test` and wired into the
185
+ `prepublishOnly` gate. Deliberately wrong aggregation now fails a fixture instead
186
+ of shipping. (EPIC-007 / TASK-068)
187
+ - **edit:read ratio** in the adoption report — a partial, *ungraded* proxy for
188
+ doctrine applied as method (narrow reads, contracts-first edits), the discipline
189
+ that leaves no `Skill` tool call to count. (EPIC-007 / TASK-071)
190
+
191
+ ### Changed
192
+
193
+ - **`verify` cross-checks hook wiring.** `cli/verify.mjs` now confirms each
194
+ registered hook command points at the file actually installed — checking *all*
195
+ matching entries, not just the first — so a dead or mis-pointed duplicate fails
196
+ red instead of passing on a partial match. (EPIC-006 / TASK-065)
197
+ - **Adoption report splits code search from shell plumbing.** Semantic search (RAG)
198
+ and the Grep tool count as code search; `grep`/`find` inside Bash is reported
199
+ separately and kept out of the RAG share. Removes the "RAG 0%" false negative on
200
+ logs that were only ever doing operational greps. (EPIC-007 / TASK-069)
201
+ - **The adoption verdict is now earned.** RAG and doctrine-skill judgments are
202
+ withheld below a PO-frozen floor of 3 distinct sessions ("insufficient signal"
203
+ instead of a confident ✗ over one atypical session). A lever the log genuinely
204
+ can't distinguish — RAG installed-but-unused vs not installed, or method-level
205
+ doctrine that leaves no tool call — is reported neutrally, never as a false ✗.
206
+ (EPIC-007 / TASK-070, TASK-071)
207
+
208
+ ### Fixed
209
+
210
+ - **Manual hook-install docs were incomplete.** README / SKILL.md now instruct
211
+ copying `hooks/config-dir.mjs` alongside `hooks/adoption-log.mjs` — the latter
212
+ imports the former, so a by-hand install following the old docs threw
213
+ `Cannot find module`. (EPIC-006)
214
+ - **No more false churn/verdict lines over empty data.** The adoption report no
215
+ longer prints "elevated re-read churn" for a zero-read log, and no longer dangles
216
+ a "see verdict below" pointer when the verdict section has nothing to say.
217
+ (EPIC-007 / TASK-070, TASK-071)
218
+
219
+ ### Testing / packaging
220
+
221
+ - `smoke:init` now exercises **both** the default `$HOME` and the
222
+ `CLAUDE_CONFIG_DIR` override paths, asserting hook state round-trips in each.
223
+ (EPIC-006 / TASK-066)
224
+ - `npm test` (the golden-fixture suite) runs first in `prepublishOnly`, ahead of
225
+ `verify:pack` / `verify:plugin` / `smoke:init` — cheapest gate, fails fastest.
226
+ (EPIC-007 / TASK-068)
227
+
228
+ ## [1.0.1] — 2026-07-23
229
+
230
+ ### Fixed
231
+
232
+ - Stop shipping the dev-only MCP config (`.mcp.json`, `rag.config.json`) to plugin
233
+ installers. Because the repo root doubles as the plugin root, a tracked
234
+ `.mcp.json` would force every marketplace install to load a `rag` server pointing
235
+ at a binary and index the installer doesn't have — breaking the "RAG is optional,
236
+ detected never assumed" contract. Now enforced by `verify:plugin`.
237
+
238
+ ## [1.0.0] — 2026-07-23
239
+
240
+ ### Added
241
+
242
+ - Initial dual-channel release under `@bglocation`: a Claude Code **plugin**
243
+ (marketplace) and an **npm CLI** (`tune-context init`), from one self-contained
244
+ repo. Ships the doctrine skills (`token-efficiency`, `caveman`, `cdd`,
245
+ `phase-workflow`), the `tune-context` configurator (skill + deterministic CLI
246
+ twin with a never-clobber managed-block contract), right-sized subagents
247
+ (`explore-cheap`, `reviewer`), the PreCompact / SessionStart / PostToolUse hook
248
+ suite, and the adoption report (`eval/adoption-report.mjs`).
249
+
250
+ [1.1.0]: https://gitlab.com/bglocation/tune-context/-/tags/v1.1.0
251
+ [1.0.1]: https://gitlab.com/bglocation/tune-context/-/tags/v1.0.1
252
+ [1.0.0]: https://gitlab.com/bglocation/tune-context/-/tags/v1.0.0
package/README.md CHANGED
@@ -15,7 +15,7 @@ tune-context/
15
15
  ├── .claude-plugin/plugin.json # Claude Code plugin manifest
16
16
  ├── skills/ # token-efficiency, caveman, cdd, phase-workflow, tune-context (+ templates/)
17
17
  ├── agents/ # explore-cheap, reviewer (model right-sizing)
18
- ├── hooks/ # PreCompact state-doc, SessionStart re-inject, PostToolUse adoption log + hooks.json
18
+ ├── hooks/ # PreCompact state-doc, SessionStart re-inject, adoption log, context reminder + hooks.json
19
19
  ├── bin/ + cli/ # `tune-context init` — deterministic detect → generate → verify
20
20
  ├── eval/ # adoption-report.mjs (the rag-usage analog)
21
21
  └── scripts/ # verify-pack, smoke-init, sync-tune-context (dev/maintenance)
@@ -70,8 +70,9 @@ then generates a lean project `CLAUDE.md`, wires the doctrine skills, and merges
70
70
  hooks + permissions — idempotently, never clobbering hand-written content. This is
71
71
  the full-fidelity path (it also handles wiki wiring and semantic-search detection).
72
72
 
73
- **2. CLI `tune-context init`.** The deterministic, no-model twin of the skill — same
74
- managed-block rules, same never-clobber guarantee. Good for CI or scripted setup:
73
+ **2. CLI `tune-context init`.** The deterministic **subset** of the skill — same
74
+ managed-block rules, same never-clobber guarantee, no model in the loop. Good for
75
+ CI or scripted setup:
75
76
 
76
77
  ```bash
77
78
  npx -p @bglocation/tune-context tune-context init # runs in the current repo
@@ -83,6 +84,15 @@ node bin/tune-context.mjs init --cwd /path/to/your/repo
83
84
  It prints what it detected, what it synced/merged, and a verification report; a
84
85
  second run is a no-op.
85
86
 
87
+ Two steps of the skill have **no CLI equivalent**, because each needs judgment a
88
+ deterministic tool cannot supply — run `/tune-context` if you want them:
89
+
90
+ | Step | Skill | CLI |
91
+ |---|---|---|
92
+ | Project `CLAUDE.md`, doctrine skills, hooks, permissions | ✅ | ✅ |
93
+ | Merge `.mcp.json` — add a semantic-search server if none was detected | ✅ | ❌ reads it, never writes it |
94
+ | Wiki lever — RAG segment vs its own MCP vs a plain pointer | ✅ | ❌ not detected, not wired |
95
+
86
96
  **3. Plugin install.** Load the plugin so the skills, agents and hooks are available
87
97
  in every session. The best UX is the **marketplace** — install once, get updates
88
98
  with `/plugin marketplace update`:
@@ -110,25 +120,124 @@ The plugin manifest (`.claude-plugin/plugin.json`) and hook wiring
110
120
  automatically; `bin/tune-context` is added to `PATH` while the plugin is enabled.
111
121
  The marketplace catalog is [`.claude-plugin/marketplace.json`](.claude-plugin/marketplace.json).
112
122
 
123
+ ### `CLAUDE_CONFIG_DIR`
124
+
125
+ By default the CLI reads and writes Claude config under `~/.claude` (and
126
+ `~/.claude.json`). If you set Claude Code's `CLAUDE_CONFIG_DIR`, `tune-context
127
+ init` honors it consistently — everything user-level moves under the override:
128
+
129
+ | What | Default | With `CLAUDE_CONFIG_DIR=/x` |
130
+ |---|---|---|
131
+ | doctrine skills / agents / hook scripts | `~/.claude/…` | `/x/…` |
132
+ | `settings.json` (hooks + gating) | `~/.claude/settings.json` | `/x/settings.json` |
133
+ | MCP-server detection (`.claude.json`) | `~/.claude.json` | `/x/.claude.json` |
134
+ | registered hook command paths | `$HOME/.claude/hooks/…` | `/x/hooks/…` |
135
+ | hook state (PreCompact snapshot, adoption log) | `~/.claude/tune-context/…` | `/x/tune-context/…` |
136
+
137
+ Without the override the generated `settings.json` keeps the portable
138
+ `$HOME/.claude/hooks/…` form (expanded when the hook fires, so it travels between
139
+ machines). Under the override it's baked to the literal `/x/hooks/…` — where the
140
+ scripts actually land. Useful for shared accounts, containers, and test isolation.
141
+
142
+ ## Keeping context lean while you work
143
+
144
+ Setup is the cheap half. The recurring half is **shedding context you no longer
145
+ need** — and that stays a manual, deliberate act, because only you know when a
146
+ piece of work is actually finished.
147
+
148
+ Every turn re-sends the whole conversation as input. A session that has been
149
+ running for hours pays for its early exploration on *every* later request, long
150
+ after that detail stopped being useful. So:
151
+
152
+ | When | Do | Why |
153
+ |---|---|---|
154
+ | A work thread closes (feature done, bug fixed) | `/compact` | Keeps the conclusions, drops the raw tool output that led to them |
155
+ | Switching to unrelated work | `/clear` | Nothing from the old task is worth carrying — `/compact` would still keep a summary of it |
156
+ | Long session, no natural break yet | `/compact` anyway | Waiting for auto-compact means paying full price right up to the limit |
157
+
158
+ tune-context softens the cost of compacting rather than replacing it: its
159
+ `PreCompact` hook snapshots your branch, dirty files and recent commits, and the
160
+ `SessionStart(compact)` hook injects that back afterwards — so the mechanical
161
+ state survives even though the transcript doesn't. Judgment calls and open
162
+ threads do **not** survive; write those down before compacting if they matter.
163
+
164
+ ### The reminder is automatic
165
+
166
+ Claude cannot see its own context usage — nothing in a turn tells the model how
167
+ many tokens it is re-sending, and Claude Code has no timer or context-threshold
168
+ hook event. So `context-reminder.mjs` (registered under `UserPromptSubmit`)
169
+ supplies the missing measurement: it reads the tail of the session transcript,
170
+ sums the tokens actually re-sent, and once that passes **120,000** it injects a
171
+ single short note suggesting `/compact`.
172
+
173
+ - **Once per crossing, not per prompt.** The note itself costs tokens on the turn
174
+ it appears in, so it re-arms only after a compaction-sized drop. Measured on a
175
+ 12.6-hour session: 4 reminders total.
176
+ - **The model picks the moment.** The reminder tells Claude to stay quiet
177
+ mid-task and raise it when the thread closes — the hook can measure size, but
178
+ only the model can see whether you're mid-refactor.
179
+ - **Raise it for a large context window:** `export TUNE_CONTEXT_REMIND_TOKENS=400000`.
180
+ 120k is ~60% of a standard 200k window; the transcript doesn't record the window
181
+ size, so this can't be auto-scaled.
182
+ - **Turn it off:** delete the `UserPromptSubmit` entry from `settings.json`.
183
+
184
+ Depth lives in the `phase-workflow` and `token-efficiency` skills (both installed
185
+ by `tune-context init`), not here.
186
+
113
187
  ## Measuring adoption (does the doctrine actually get used?)
114
188
  `hooks/adoption-log.mjs` logs one JSONL line per event to
115
- `~/.claude/tune-context/adoption.jsonl`, and `eval/adoption-report.mjs` turns that
116
- into a report: RAG vs grep, doctrine skills, subagents, re-read churn — with a
117
- verdict per lever. It's registered under two Claude Code hook events
189
+ `~/.claude/tune-context/adoption.jsonl` by default (under `CLAUDE_CONFIG_DIR`, the
190
+ override's `tune-context/adoption.jsonl` see above), and `eval/adoption-report.mjs`
191
+ turns that into a report code search (semantic RAG vs the Grep tool, with
192
+ shell-plumbing greps kept out of the RAG share), doctrine skills, subagents, and
193
+ re-read churn — closing with an *earned* verdict: withheld below a PO-frozen floor of
194
+ distinct sessions, and never a false ✗ for a lever the log can't see (RAG
195
+ installed-but-unused vs not installed; method-level doctrine that leaves no tool call).
196
+ It's registered under two Claude Code hook events —
118
197
  `PostToolUse` (every tool call) and `SubagentStart` (subagent spawns, which aren't
119
198
  regular tools).
120
199
 
200
+ **Using both the plugin and `tune-context init` (CLI) on the same repo** registers
201
+ the hook twice — once per channel — so every event logs twice. This is harmless
202
+ (same script, same log, just doubled counts) and the report detects it: past a
203
+ measured share of near-duplicate pairs it withholds the re-read-churn verdict
204
+ instead of printing a number the doubling would have faked.
205
+
121
206
  ### Turn it on
122
207
 
123
208
  1. **Install the hook.** Either run **`/tune-context`** in your repo (it copies the
124
209
  script to `~/.claude/hooks/` and merges the hook entries into `settings.json`
125
210
  for you), or do it by hand:
126
- - copy `hooks/adoption-log.mjs` `~/.claude/hooks/adoption-log.mjs` (needs
127
- Node ≥ 18 on `PATH`);
128
- - add the `PostToolUse` **and** `SubagentStart` blocks from
129
- [`skills/tune-context/templates/settings.json.tmpl`](skills/tune-context/templates/settings.json.tmpl)
130
- to your `settings.json` `~/.claude/settings.json` for every project, or a
131
- repo's `.claude/settings.json` for just that one.
211
+ - copy `hooks/adoption-log.mjs` **and** `hooks/config-dir.mjs` (it imports the
212
+ latter for the log path) → `~/.claude/hooks/` (needs Node ≥ 18 on `PATH`);
213
+ - merge this into your `settings.json` `~/.claude/settings.json` for every
214
+ project, or a repo's `.claude/settings.json` for just that one (if the file
215
+ doesn't exist yet, this can be the whole thing):
216
+ ```json
217
+ {
218
+ "hooks": {
219
+ "PostToolUse": [
220
+ {
221
+ "matcher": "*",
222
+ "hooks": [
223
+ { "type": "command", "command": "node \"$HOME/.claude/hooks/adoption-log.mjs\"" }
224
+ ]
225
+ }
226
+ ],
227
+ "SubagentStart": [
228
+ {
229
+ "matcher": "*",
230
+ "hooks": [
231
+ { "type": "command", "command": "node \"$HOME/.claude/hooks/adoption-log.mjs\"" }
232
+ ]
233
+ }
234
+ ]
235
+ }
236
+ }
237
+ ```
238
+ Using `CLAUDE_CONFIG_DIR`? Replace `$HOME/.claude/hooks` with the literal
239
+ override path instead — `$HOME` is not expanded there (see
240
+ [`CLAUDE_CONFIG_DIR`](#claude_config_dir) above).
132
241
 
133
242
  2. **No reload needed.** Claude Code watches the settings files, so the new hooks
134
243
  go live within a few seconds of saving — **you do not need to reload VSCode,
@@ -137,7 +246,8 @@ regular tools).
137
246
  the `/hooks` command.
138
247
 
139
248
  3. **Just work.** Every tool call and subagent run now appends a line to
140
- `~/.claude/tune-context/adoption.jsonl` (created on first write).
249
+ `~/.claude/tune-context/adoption.jsonl` by default (created on first write; under
250
+ `CLAUDE_CONFIG_DIR` it's the override's `tune-context/adoption.jsonl` instead).
141
251
 
142
252
  4. **Read the report** any time, from this checkout (or the installed package):
143
253
  ```bash
@@ -147,13 +257,16 @@ regular tools).
147
257
 
148
258
  **Stop / opt out:** delete the `PostToolUse` + `SubagentStart` entries from
149
259
  `settings.json` (watcher picks that up too). The log stays local under
150
- `~/.claude/tune-context/` and records only tool names, file paths, and
151
- skill/agent names — never full Bash command strings.
260
+ `~/.claude/tune-context/` (or the `CLAUDE_CONFIG_DIR` override) and records only
261
+ tool names, file paths, and skill/agent names — never full Bash command strings.
152
262
 
153
263
  **Honest scope:** this is a *behavioral proxy*, not a bill. Token cost lands only
154
264
  in OTEL (`CLAUDE_CODE_ENABLE_TELEMETRY=1` → `claude_code.cost.usage`, needs a
155
265
  collector), never a hook; and caveman is an output style with no tool call, so it
156
- can't be counted this way.
266
+ can't be counted this way. Doctrine applied as *method* — narrow reads,
267
+ contracts-first edits, delegation — is only partly visible too: no `Skill` call,
268
+ but its trace shows up in the report's edit:read ratio, so the verdict never
269
+ reads zero `Skill` calls as "unused."
157
270
 
158
271
  ### Getting real cost (OTEL) — outside tune-context
159
272
 
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ // Extensionless alias so the plugin channel's bin/ PATH entry gives the same
3
+ // `tune-context` command the npm channel already provides via package.json's
4
+ // `bin` field. Claude Code exposes bin/ files under their literal filename —
5
+ // no extension-stripping — so without this file, a plugin-only install
6
+ // (no `npm install -g`) only offers `tune-context.mjs` (TASK-078). Real logic
7
+ // stays in tune-context.mjs; this just re-executes it against the same
8
+ // process.argv.
9
+ import { main } from './tune-context.mjs';
10
+
11
+ main();