@bglocation/tune-context 1.1.0 → 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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +160 -0
- package/README.md +92 -7
- package/bin/tune-context +11 -0
- package/bin/tune-context.mjs +109 -15
- package/cli/detect.mjs +92 -16
- package/cli/generate.mjs +6 -0
- package/cli/sync-doctrine.mjs +11 -2
- package/cli/verify.mjs +50 -5
- package/eval/adoption-report.mjs +91 -1
- package/hooks/context-reminder.mjs +212 -0
- package/hooks/hooks.json +11 -0
- package/hooks/pre-compact-snapshot.mjs +68 -6
- package/package.json +1 -1
- package/skills/token-efficiency/SKILL.md +3 -1
- package/skills/tune-context/SKILL.md +58 -15
- package/skills/tune-context/templates/settings.json.tmpl +11 -10
|
@@ -43,10 +43,32 @@ not guesses); monorepo vs single project (workspaces, `packages/`, `apps/`).
|
|
|
43
43
|
| project | `./.mcp.json` |
|
|
44
44
|
| user | `~/.claude.json` → top-level `mcpServers` |
|
|
45
45
|
| local | `~/.claude.json` → `["<abs project path>"].mcpServers` |
|
|
46
|
-
|
|
|
46
|
+
| rejected | any `settings.json`: `disabledMcpjsonServers` (project `.mcp.json` servers only) |
|
|
47
47
|
|
|
48
48
|
If `CLAUDE_CONFIG_DIR` is set, `.claude.json` lives there instead of `~`.
|
|
49
49
|
|
|
50
|
+
**A rejected server is not a lever.** A project `.mcp.json` server named in
|
|
51
|
+
`disabledMcpjsonServers` (in *any* settings file) is rejected — `claude mcp get`
|
|
52
|
+
shows it as `✘ Rejected`. Skip it: no `mcp__<server>__*` permission rule, no
|
|
53
|
+
CLAUDE.md line promising a capability this session does not have. Say which
|
|
54
|
+
servers you skipped and why.
|
|
55
|
+
|
|
56
|
+
Read the neighbouring keys carefully rather than by name — the three below look
|
|
57
|
+
like the same mechanism and are not:
|
|
58
|
+
- `enabledMcpjsonServers` — an *approval* list. A `.mcp.json` server that is
|
|
59
|
+
absent from it is **⏸ pending approval**, not disabled; the user is prompted
|
|
60
|
+
and may approve it. Do not treat absence as rejection.
|
|
61
|
+
- `allowedMcpServers` / `deniedMcpServers` — arrays of **objects**
|
|
62
|
+
(`{serverName}` | `{serverUrl}` | `{serverCommand}`). They may sit in any
|
|
63
|
+
settings file and merge from every source, but they are organization policy,
|
|
64
|
+
not a per-project toggle: enforcement rests on a managed tier
|
|
65
|
+
(`managed-settings.json`, server-managed settings, MDM/registry), and matching
|
|
66
|
+
uses URL wildcards, `${VAR}` expansion and denylist-over-allowlist precedence.
|
|
67
|
+
The docs warn a `serverName` entry "is not a security control".
|
|
68
|
+
- `disabledMcpServers` / `enabledMcpServers` (note: no `json`) — a *different*
|
|
69
|
+
pair, living per-project in `~/.claude.json`, covering user-configured,
|
|
70
|
+
plugin and claude.ai servers. Disjoint from the `…Mcpjson…` keys above.
|
|
71
|
+
|
|
50
72
|
**Classify each server** — this is what turns "a server exists" into a rule:
|
|
51
73
|
|
|
52
74
|
| Type | Rule to write into the project CLAUDE.md |
|
|
@@ -65,8 +87,9 @@ If `CLAUDE_CONFIG_DIR` is set, `.claude.json` lives there instead of `~`.
|
|
|
65
87
|
**Existing config.** Note whether `./CLAUDE.md`, `.claude/settings.json`,
|
|
66
88
|
`.mcp.json`, `rag.config.json` already exist — you will merge, never clobber.
|
|
67
89
|
For `settings.json` also check `hooks.PreCompact` / `hooks.SessionStart` /
|
|
68
|
-
`hooks.PostToolUse` / `hooks.SubagentStart` for an
|
|
69
|
-
`pre-compact-snapshot.mjs` / `session-start-reinject.mjs` /
|
|
90
|
+
`hooks.PostToolUse` / `hooks.SubagentStart` / `hooks.UserPromptSubmit` for an
|
|
91
|
+
entry already calling `pre-compact-snapshot.mjs` / `session-start-reinject.mjs` /
|
|
92
|
+
`adoption-log.mjs` / `context-reminder.mjs`
|
|
70
93
|
(idempotency: skip if present — `adoption-log.mjs` is registered under both
|
|
71
94
|
`PostToolUse` and `SubagentStart`) and `permissions.allow` for entries already
|
|
72
95
|
covering the detected build/test/lint commands.
|
|
@@ -86,6 +109,9 @@ covering the detected build/test/lint commands.
|
|
|
86
109
|
`phase-workflow` are present in `~/.claude/skills/` (they ship with this tool).
|
|
87
110
|
4. **`.mcp.json`** — **merge**. Add a semantic-search server only if none was
|
|
88
111
|
detected in any scope. Preserve every existing server and key.
|
|
112
|
+
*(Skill path only — the `tune-context` CLI reads `.mcp.json` but never writes
|
|
113
|
+
it: picking which server to add is a judgment call, and the CLI runs with no
|
|
114
|
+
model in the loop.)*
|
|
89
115
|
5. **Hooks** — ensure `~/.claude/hooks/pre-compact-snapshot.mjs`,
|
|
90
116
|
`~/.claude/hooks/session-start-reinject.mjs` and `~/.claude/hooks/adoption-log.mjs`
|
|
91
117
|
exist (they ship with this tool, installed once user-level — same as the
|
|
@@ -95,10 +121,10 @@ covering the detected build/test/lint commands.
|
|
|
95
121
|
Install the hooks directory as a set, never single files. Merge into `settings.json` from
|
|
96
122
|
`${CLAUDE_SKILL_DIR}/templates/settings.json.tmpl`: append the `PreCompact`
|
|
97
123
|
(matcher `"*"`, docs: omit or `*` = every trigger), `SessionStart`
|
|
98
|
-
(matcher `"compact"`), `PostToolUse` (matcher `"*"`)
|
|
99
|
-
(matcher `"*"`) entries **only if an
|
|
100
|
-
there** — treat each hook array as a
|
|
101
|
-
overwrite.
|
|
124
|
+
(matcher `"compact"`), `PostToolUse` (matcher `"*"`), `SubagentStart`
|
|
125
|
+
(matcher `"*"`) and `UserPromptSubmit` (matcher `"*"`) entries **only if an
|
|
126
|
+
entry calling that script isn't already there** — treat each hook array as a
|
|
127
|
+
set keyed by `command`, not a value to overwrite.
|
|
102
128
|
- The template's `{{HOOKS_DIR}}` slot decides where those commands point.
|
|
103
129
|
**Default: `$HOME/.claude/hooks`** — keep the literal `$HOME` (expanded when
|
|
104
130
|
the hook fires, so `settings.json` stays portable between machines).
|
|
@@ -122,14 +148,30 @@ covering the detected build/test/lint commands.
|
|
|
122
148
|
contracts-first edits) is likewise not `Skill`-call-visible — the verdict
|
|
123
149
|
reports zero `Skill` calls as a neutral note, not "unused", and points at the
|
|
124
150
|
edit:read ratio as a partial proxy.
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
|
|
151
|
+
- `context-reminder.mjs` is registered under `UserPromptSubmit` (matcher `"*"`).
|
|
152
|
+
It reads the **tail** of the session transcript (`transcript_path`, present on
|
|
153
|
+
every hook payload), sums `input_tokens + cache_read + cache_creation` — the
|
|
154
|
+
context actually re-sent — and once that crosses a PO-frozen threshold
|
|
155
|
+
(120k, override `TUNE_CONTEXT_REMIND_TOKENS`) injects one short reminder to
|
|
156
|
+
consider `/compact`. Fires **once per crossing**, re-arming only after a
|
|
157
|
+
compaction-sized drop: the reminder costs tokens on the prompt it appears in,
|
|
158
|
+
so nagging every turn would spend exactly what the tool saves. It reminds on
|
|
159
|
+
SIZE, not on "phase boundary" — size is measurable from the transcript, a
|
|
160
|
+
phase boundary is a judgment a hook (no model access) cannot make, so the
|
|
161
|
+
reminder hands that timing call to the model and `phase-workflow`.
|
|
162
|
+
- All three state paths resolve through `hooks/config-dir.mjs`'s
|
|
163
|
+
`claudeBaseDir()` — same `CLAUDE_CONFIG_DIR || ~/.claude` rule as the
|
|
164
|
+
registration paths above, so an override isolates state too, not just script
|
|
165
|
+
locations.
|
|
128
166
|
6. **`settings.json` permissions** — merge `permissions.allow`: the project's
|
|
129
167
|
safe, frequent commands (build/test/lint, `git status`/`diff`/`log`) and
|
|
130
|
-
`mcp__<server>__<tool>` for detected servers
|
|
168
|
+
`mcp__<server>__<tool>` for detected servers — **excluding servers rejected
|
|
169
|
+
via `disabledMcpjsonServers`** (see §1). Union with the existing array
|
|
131
170
|
(dedupe, never remove entries). Never add blanket `Bash(*)` or bypass modes.
|
|
132
171
|
7. **Wiki** — wire the lever chosen in detection (segment / MCP note / pointer).
|
|
172
|
+
*(Skill path only — the CLI neither detects the wiki location nor wires this
|
|
173
|
+
lever; choosing between a RAG segment, a dedicated MCP and a plain pointer
|
|
174
|
+
needs judgment.)*
|
|
133
175
|
|
|
134
176
|
Keep the generated CLAUDE.md **thin**: headings and pointers only. Depth belongs
|
|
135
177
|
in skills — that is the whole point of this setup.
|
|
@@ -149,10 +191,11 @@ in skills — that is the whole point of this setup.
|
|
|
149
191
|
should exit 0 without throwing; same for `adoption-log.mjs`).
|
|
150
192
|
- **The merge actually landed — don't just trust step 5 ran.** Re-read the
|
|
151
193
|
target `settings.json` (the same file you merged into) and parse it: confirm
|
|
152
|
-
`hooks.PreCompact`, `hooks.SessionStart`, `hooks.PostToolUse
|
|
153
|
-
`hooks.SubagentStart` each contain an entry whose
|
|
154
|
-
expected script (`pre-compact-snapshot.mjs` /
|
|
155
|
-
`adoption-log.mjs` twice, for `PostToolUse` and
|
|
194
|
+
`hooks.PreCompact`, `hooks.SessionStart`, `hooks.PostToolUse`,
|
|
195
|
+
`hooks.SubagentStart` and `hooks.UserPromptSubmit` each contain an entry whose
|
|
196
|
+
`command` references the expected script (`pre-compact-snapshot.mjs` /
|
|
197
|
+
`session-start-reinject.mjs` / `adoption-log.mjs` twice, for `PostToolUse` and
|
|
198
|
+
`SubagentStart` / `context-reminder.mjs`). Copying the
|
|
156
199
|
hook scripts to `~/.claude/hooks/` is not sufficient — a real prior incident
|
|
157
200
|
had the scripts present and executable while `settings.json` had **no**
|
|
158
201
|
`hooks` key at all, so the hooks silently never fired. Scripts-exist and
|
|
@@ -43,16 +43,17 @@
|
|
|
43
43
|
}
|
|
44
44
|
]
|
|
45
45
|
}
|
|
46
|
-
]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
],
|
|
47
|
+
"UserPromptSubmit": [
|
|
48
|
+
{
|
|
49
|
+
"matcher": "*",
|
|
50
|
+
"hooks": [
|
|
51
|
+
{
|
|
52
|
+
"type": "command",
|
|
53
|
+
"command": "node \"{{HOOKS_DIR}}/context-reminder.mjs\""
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
56
57
|
]
|
|
57
58
|
}
|
|
58
59
|
}
|