chisle 2.0.0 → 3.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,6 +1,6 @@
1
1
  {
2
2
  "name": "chisle",
3
- "version": "1.2.2",
3
+ "version": "3.0.0",
4
4
  "description": "Maximum-efficiency dev mode. Zero-fluff prose combined with YAGNI/ladder-first code decisions. One persona: the senior dev who deletes code for fun and bills by the syllable.",
5
5
  "hooks": {
6
6
  "SessionStart": [
@@ -33,4 +33,4 @@ you're about to edit — diet trims transport, not understanding.
33
33
  **Never minimal about:** input validation at trust boundaries, error handling
34
34
  that prevents data loss, security, accessibility, anything explicitly requested.
35
35
 
36
- Levels: lite / full (default) / ultra. Deactivate: "stop chisle" / "normal mode".
36
+ Always on once installed. Deactivate: "stop chisle" / "normal mode".
@@ -38,4 +38,4 @@ you're about to edit — diet trims transport, not understanding.
38
38
  **Never minimal about:** input validation at trust boundaries, error handling
39
39
  that prevents data loss, security, accessibility, anything explicitly requested.
40
40
 
41
- Levels: lite / full (default) / ultra. Deactivate: "stop chisle" / "normal mode".
41
+ Always on once installed. Deactivate: "stop chisle" / "normal mode".
@@ -33,4 +33,4 @@ you're about to edit — diet trims transport, not understanding.
33
33
  **Never minimal about:** input validation at trust boundaries, error handling
34
34
  that prevents data loss, security, accessibility, anything explicitly requested.
35
35
 
36
- Levels: lite / full (default) / ultra. Deactivate: "stop chisle" / "normal mode".
36
+ Always on once installed. Deactivate: "stop chisle" / "normal mode".
@@ -37,4 +37,4 @@ you're about to edit — diet trims transport, not understanding.
37
37
  **Never minimal about:** input validation at trust boundaries, error handling
38
38
  that prevents data loss, security, accessibility, anything explicitly requested.
39
39
 
40
- Levels: lite / full (default) / ultra. Deactivate: "stop chisle" / "normal mode".
40
+ Always on once installed. Deactivate: "stop chisle" / "normal mode".
@@ -37,4 +37,4 @@ you're about to edit — diet trims transport, not understanding.
37
37
  **Never minimal about:** input validation at trust boundaries, error handling
38
38
  that prevents data loss, security, accessibility, anything explicitly requested.
39
39
 
40
- Levels: lite / full (default) / ultra. Deactivate: "stop chisle" / "normal mode".
40
+ Always on once installed. Deactivate: "stop chisle" / "normal mode".
package/AGENTS.md CHANGED
@@ -41,6 +41,6 @@ edit — diet trims transport, not understanding.
41
41
  Input validation at trust boundaries, error handling that prevents data loss,
42
42
  security, accessibility, anything explicitly requested.
43
43
 
44
- ## Levels
44
+ ## Switching off
45
45
 
46
- `lite` · `full` (default) · `ultra`. Deactivate: "stop chisle" / "normal mode".
46
+ Always on once installed. Deactivate: "stop chisle" / "normal mode".
package/README.md CHANGED
@@ -1,13 +1,17 @@
1
1
  <p align="center">
2
- <img src="assets/logo.svg" width="120" alt="Chisle">
2
+ <img src="assets/logo.png" width="120" alt="Chisle">
3
3
  </p>
4
4
 
5
- <h1 align="center">🧨 Chisle</h1>
5
+ <h1 align="center">Chisle</h1>
6
6
 
7
7
  <p align="center">
8
8
  <em>Your AI talks less, builds less, reads less — and says more. Like a senior dev who bills by the syllable.</em>
9
9
  </p>
10
10
 
11
+ <p align="center">
12
+ <em>The only tool in this class that publishes the runs where it lost.</em>
13
+ </p>
14
+
11
15
  <p align="center">
12
16
  <a href="https://www.npmjs.com/package/chisle"><img src="https://img.shields.io/npm/v/chisle?style=flat-square&color=d78a3c" alt="npm version"></a>
13
17
  <img src="https://img.shields.io/badge/works%20with-8%20agents-d78a3c?style=flat-square" alt="Works with 8 agents">
@@ -18,7 +22,7 @@
18
22
  </p>
19
23
 
20
24
  <p align="center">
21
- <strong>Half the output bill on 20 measured tasks &middot; compresses what the model writes AND reads &middot; one command</strong>
25
+ <strong>44% of a bare model on coding work &middot; 41% on the long ones &middot; one command</strong>
22
26
  </p>
23
27
 
24
28
  <p align="center">
@@ -77,6 +81,41 @@ Most efficiency tools compress one thing. Chisle compresses **three**:
77
81
  | **Output: code** | speculative abstractions, unrequested boilerplate | YAGNI efficiency ladder |
78
82
  | **Input: context** | oversized tool output flooding the window | `PostToolUse` hook — scrub, elide, dedup — plus prevention rules |
79
83
 
84
+ Where each one attaches to a session:
85
+
86
+ ```mermaid
87
+ flowchart LR
88
+ subgraph S["Session start"]
89
+ H1["SessionStart hook<br/>~1.6k tokens, new sessions only"]
90
+ end
91
+ subgraph T["Every turn"]
92
+ H2["UserPromptSubmit hook<br/>~50-token reminder"]
93
+ end
94
+ subgraph L["Every tool call"]
95
+ H3["PostToolUse hook<br/>scrub → elide → dedup"]
96
+ end
97
+
98
+ H1 --> M(["Model"])
99
+ H2 --> M
100
+ M -->|writes| O["Output:<br/>terser prose,<br/>YAGNI-first code"]
101
+ M -->|calls a tool| TOOL[["Bash / Grep / WebFetch / mcp__*"]]
102
+ TOOL -->|raw output| H3
103
+ H3 -->|"compressed, rebuilt into<br/>the tool's own shape"| M
104
+
105
+ RE["Read / Edit"] -.->|"never touched —<br/>exact bytes feed later edits"| M
106
+
107
+ style M fill:#1f2937,stroke:#d78a3c,color:#e6edf3
108
+ style O fill:#14532d,stroke:#2da44e,color:#e6edf3
109
+ style H3 fill:#1f2937,stroke:#2da44e,color:#e6edf3
110
+ style RE fill:#3f1d1d,stroke:#cf3b3b,color:#e6edf3
111
+ ```
112
+
113
+ The loop on the right is the input axis: tool output is billed again on *every* later request in the session, so shrinking it once pays repeatedly. `Read` and `Edit` are deliberately outside it.
114
+
115
+ <p align="center">
116
+ <img src="assets/thumbnail.png" width="820" alt="Chisle — less tokens, same results. Four parts: output compressor (compress noisy tool outputs), context diet (read only what's actually relevant), terse persona (short, focused, YAGNI-first), YAGNI ladder (do less, reuse more, build only when needed).">
117
+ </p>
118
+
80
119
  Every "be concise" tool has a worst day — the day it makes the model write *more* than no tool at all. Across 20 measured tasks over two suites, the specialists had that day **6** and **8** times, blowing up to **424%** of the baseline. Chisle had it **once**, capped at 173% — and that one failure was root-caused, fixed in the ruleset, and re-validated live at 93%, with the whole investigation [committed to the repo](benchmarks/results/2026-07-07-verify-rerun.md). Think of it as downside insurance for your token bill: not always the single cheapest answer, always the smallest worst case — from the only tool in this class that publishes its own failures. [Why not caveman or ponytail? →](docs/comparison.md)
81
120
 
82
121
  ---
@@ -101,7 +140,25 @@ irm https://raw.githubusercontent.com/JayPokale/Chisle/main/install.ps1 | iex
101
140
 
102
141
  Preview first with `npx chisle --dry-run`, scope with `--only claude`, see everything with `npx chisle --help`. Remove with `npx chisle --uninstall`.
103
142
 
104
- **Requirements:** Node ≥18 (installer / `npx`) · Claude Code for live `/chisle` switching and input-side compression — the always-on ruleset still ships to every other agent.
143
+ **Requirements:** Node ≥18 (installer / `npx`) · Claude Code for `/chisle` toggling and input-side compression — the always-on ruleset still ships to every other agent.
144
+
145
+ ### Upgrading
146
+
147
+ Same command as installing — it is idempotent and overwrites the previous copy:
148
+
149
+ ```bash
150
+ npx chisle # npm / standalone install
151
+ claude plugin update chisle@chisle # Claude Code plugin install
152
+ ```
153
+
154
+ Chisle checks npm on session start and mentions it once when a **major** version
155
+ is out (cached 3 days, `CHISLE_UPDATE_CHECK=0` to silence). Minor and patch
156
+ releases stay quiet on purpose.
157
+
158
+ Upgrading to 3.0.0 from 2.x needs nothing: a `lite`/`full`/`ultra` value in
159
+ `CHISLE_DEFAULT_MODE` or `config.json` is no longer meaningful, falls through to
160
+ the default, and Chisle stays active. It says so once so the setting is not
161
+ ignored silently — replace it with `on`/`off` or delete it.
105
162
 
106
163
  ### Claude Code plugin (marketplace)
107
164
 
@@ -147,7 +204,22 @@ Chisle wins all four columns: it cut the total 20-task bill **nearly in half** w
147
204
 
148
205
  The bar is the whole 20-task bill; the badge is each tool's worst single day. caveman's worst day cost **4.2×** a bare model; ponytail's — a tool whose entire job is writing less — **2.3×**. Chisle's worst day was 1.7×, it happened once, and the fix is measured and merged.
149
206
 
150
- On coding tasks Chisle is leanest (June: 22% of baseline vs caveman 46%, ponytail 29%; July: 64% vs 84% and 160%). On pure prose caveman is a hair leaner on a good day — credit where due. And in the July run all 24 answers, every arm, **graded correct**: nobody here buys token savings with wrong answers.
207
+ In the July run all 24 answers, every arm, **graded correct**: nobody here buys token savings with wrong answers.
208
+
209
+ #### Code vs. explanation
210
+
211
+ Across all 20 cells, split by what the prompt actually asks for:
212
+
213
+ | | n | caveman | ponytail | **Chisle** |
214
+ |---|--:|--:|--:|--:|
215
+ | **coding** (wants working code) | 12 | 74% | 59% | **44%** |
216
+ | **non-coding** (wants an explanation) | 8 | 103% | 104% | **87%** |
217
+
218
+ Code is where the YAGNI ladder has something to bite on: an abstraction to skip, a stdlib call to reach for, a file not to create. Chisle bills **44%** of a bare model there — a third less than ponytail, which is the closest thing to a dedicated lazy-code tool.
219
+
220
+ On explanation-only prompts the picture is worse for everyone. Both specialists land **above 100%** — a tool whose job is writing less made the model write *more* than using nothing at all. Chisle is the only arm that stays under water (87%), which is a smaller win than the coding number and worth saying plainly.
221
+
222
+ This does revise a claim the earlier Sonnet writeup made. On that suite's three prose prompts caveman was leaner (44% vs 52%), and that still holds *for those cells*. Pooled across all eight non-coding cells it does not: caveman is at 103%. The prose win was suite-specific, not general.
151
223
 
152
224
  #### Task by task
153
225
 
@@ -159,6 +231,47 @@ Averages hide the interesting part, so here is every cell of the June suite —
159
231
 
160
232
  Chisle is leanest on **5 of 6**. caveman takes `cache` (8% vs our 12%) — it wins by answering in prose where we still emit working code, which is the trade you would want on a task that asked for code. Note the two prose rows where ponytail lands **above** 100%: a tool built to write less made the model write *more* than using no tool at all. That is the failure mode the worst-case column above is really about.
161
233
 
234
+ #### The headline average is hiding the good part
235
+
236
+ Split the same 20 cells at their median baseline — short answers below, long answers above — and the tools separate sharply:
237
+
238
+ <p align="center">
239
+ <img src="assets/by-size.svg" width="820" alt="Total billed output split by answer size. On short answers caveman and Chisle are level at about 84% of baseline and ponytail is above 100%. On long answers Chisle drops to about 45% while caveman is 79% and ponytail 59%.">
240
+ </p>
241
+
242
+ On **short** answers Chisle and caveman are level (84% each) — there is not much to cut in a three-line reply, and the ruleset overhead is proportionally at its worst. On **long** answers Chisle drops to **45%** while caveman only reaches 79%. The 52% headline is the blend of the two, so it understates the case where it matters and overstates the case where it doesn't.
243
+
244
+ The effect is not driven by one lucky cell. Dropping the `cache` outlier (the row where the baseline invented 150 lines against a codebase it never saw) *widens* the gap on long answers: caveman degrades to **116%** — worse than using no tool — while Chisle holds at **65%**.
245
+
246
+ Two honest limits. Per-task rank correlation between baseline size and leanness is weak (Spearman ρ = −0.15), so this is a difference between aggregate bills, not a tidy per-task law — with n=10 a side, treat it as a strong signal rather than a settled result. And much of the widening gap comes from the specialists getting *worse* on long answers, not only from Chisle getting better.
247
+
248
+ #### Size or kind? Both, and they're tangled
249
+
250
+ Coding prompts average ~1129 baseline tokens against ~393 for explanation prompts, so "long" and "code" largely describe the same cells. Crossing the two separates them as far as 20 tasks allow:
251
+
252
+ | | n | caveman | ponytail | **Chisle** |
253
+ |---|--:|--:|--:|--:|
254
+ | coding · short | 5 | **62%** | 116% | 70% |
255
+ | coding · long | 7 | 76% | 52% | **41%** |
256
+ | non-coding · short | 5 | 104% | 98% | **96%** |
257
+ | non-coding · long | 3 | 103% | 111% | **77%** |
258
+
259
+ <p align="center">
260
+ <img src="assets/by-kind.svg" width="820" alt="Billed output by task kind crossed with answer size. Code and short: caveman 62%, ponytail 116%, Chisle 70%. Code and long: caveman 76%, ponytail 52%, Chisle 41%. Explanation and short: caveman 104%, ponytail 98%, Chisle 96%. Explanation and long: caveman 103%, ponytail 111%, Chisle 77%.">
261
+ </p>
262
+
263
+ Size matters *within* each kind — coding goes 70% → 41%, non-coding 96% → 77% — so it isn't merely code in disguise. But the cells are thin, and the non-coding "long" bucket spans only 522–542 tokens, which is barely long at all.
264
+
265
+ The one row Chisle loses is **short coding**, where caveman takes it 62% to 70%. That is the honest shape of it: on a small code question there is little to skip, and the ruleset costs more than the ladder saves. The tool earns its keep on the long ones.
266
+
267
+ `SUITE=large` exists to fill the thin cells — see [below](#see-what-it-would-do-before-it-does-it).
268
+
269
+ These prompts were never designed to test this, which is the real caveat. To probe it directly:
270
+
271
+ ```bash
272
+ SUITE=large bash benchmarks/run-live.sh <model> benchmarks/results/raw-large
273
+ ```
274
+
162
275
  #### Where each tool actually helps
163
276
 
164
277
  | | prose | code judgment | input/context | worst-case guard | publishes failures |
@@ -180,13 +293,13 @@ Measured over 171 real sessions ([receipts](benchmarks/results/2026-07-07-input-
180
293
  | **elide** | oversized output → head + tail, error-like lines salvaged from the cut | bounded, guarded |
181
294
  | **dedup** | byte-identical repeat of a tool's previous output (same session) → one-line marker | none — the copy is already in context |
182
295
 
183
- Replayed over the same 171 sessions: **~61k tokens** (full) / **~103k** (ultra) saved one-shot, ~46% off every eligible output — floor, not estimate, since each saved byte also stops being re-sent on every later request. Correctness rules: allowlist only (`Bash`, `Agent`, `WebFetch`, `WebSearch`, `Grep`, `Glob`, `mcp__*`) — never `Read`/`Edit`, whose exact bytes feed later edits. Honest ledger: dedup scored **0 hits** on this corpus (rtk-filtered at source); it's kept for the test-rerun case, kill-switchable, and labeled speculative until it earns a number. Replay it on your own transcripts:
296
+ Replayed over the same 171 sessions: **~61k tokens** saved one-shot, ~46% off every eligible output — floor, not estimate, since each saved byte also stops being re-sent on every later request. Correctness rules: allowlist only (`Bash`, `Agent`, `WebFetch`, `WebSearch`, `Grep`, `Glob`, `mcp__*`) — never `Read`/`Edit`, whose exact bytes feed later edits. Honest ledger: dedup scored **0 hits** on this corpus (rtk-filtered at source); it's kept for the test-rerun case, kill-switchable, and labeled speculative until it earns a number. Replay it on your own transcripts:
184
297
 
185
298
  ```bash
186
299
  node benchmarks/replay-compress.js # what it would have saved you
187
300
  ```
188
301
 
189
- Thresholds track the `/chisle` level (lite 16k / full 8k / ultra 5k chars). `stop chisle`, `CHISLE_COMPRESS=0`, `CHISLE_COMPRESS_SCRUB=0`, `CHISLE_COMPRESS_DEDUP=0` — every tier has an off switch.
302
+ Outputs over 8k chars are elided. `stop chisle`, `CHISLE_COMPRESS=0`, `CHISLE_COMPRESS_SCRUB=0`, `CHISLE_COMPRESS_DEDUP=0` — every tier has an off switch.
190
303
 
191
304
  ### Prevention — the context diet
192
305
 
@@ -196,14 +309,11 @@ The biggest context whale (whole-file `Read`s — 5.6M chars in the measured cor
196
309
 
197
310
  ## What the output sounds like
198
311
 
199
- **"Why React component re-render?"** (full)
312
+ **"Why does this React component re-render?"**
200
313
  > New object ref each render. Inline object prop = new ref = re-render. `useMemo`.
201
314
 
202
- **"Why React component re-render?"** (ultra)
203
- > Inline obj prop → new ref → re-render. `useMemo`. Why inline object at all?
204
-
205
- **"Add a cache for API responses."** (ultra)
206
- > No cache until profiler says so. When it does: `@lru_cache`. Hand-rolled TTL cache = bug farm with hit rate.
315
+ **"Add a cache for API responses."**
316
+ > `@lru_cache(maxsize=1000)` on the fetch fn. Skipped a custom cache class — add one when `lru_cache` measurably falls short.
207
317
 
208
318
  ---
209
319
 
@@ -211,15 +321,13 @@ The biggest context whale (whole-file `Read`s — 5.6M chars in the measured cor
211
321
 
212
322
  | Command | Effect |
213
323
  |---------|--------|
214
- | *(nothing)* | On automatically at `full` every session after install |
215
- | `/chisle` | Re-activate at default level if you'd stopped it |
216
- | `/chisle lite` | Tighter prose, flags the minimal alternative |
217
- | `/chisle full` | Full compression + YAGNI ladder enforced |
218
- | `/chisle ultra` | Extremist — abbreviate prose, delete before add, challenge requirements |
324
+ | *(nothing)* | On automatically every session after install |
325
+ | `/chisle` | Re-activate if you'd stopped it |
326
+ | `/chisle off` | Deactivate |
219
327
  | `stop chisle` | Deactivate (ruleset *and* input-side compression) |
220
328
  | `normal mode` | Deactivate |
221
329
 
222
- Natural language works too: "activate chisle", "chisle mode", "chislify this". Across every level, code symbols, function/API names, and error strings stay verbatim — only the noise around them compresses.
330
+ Natural language works too: "activate chisle", "chisle mode", "chislify this". Code symbols, function/API names, and error strings stay verbatim — only the noise around them compresses.
223
331
 
224
332
  ---
225
333
 
@@ -227,15 +335,31 @@ Natural language works too: "activate chisle", "chisle mode", "chislify this". A
227
335
 
228
336
  Before writing code, the agent stops at the first rung that holds:
229
337
 
338
+ ```mermaid
339
+ flowchart TD
340
+ A[Request for code] --> R[Read the problem fully]
341
+ R --> Q1{Does this need<br/>to exist at all?}
342
+ Q1 -->|no| S1[Skip it. Say so in one line]
343
+ Q1 -->|yes| Q2{Already in<br/>this codebase?}
344
+ Q2 -->|yes| S2[Reuse it. Don't rewrite]
345
+ Q2 -->|no| Q3{Stdlib<br/>does it?}
346
+ Q3 -->|yes| S3[Use the stdlib]
347
+ Q3 -->|no| Q4{Native platform<br/>feature covers it?}
348
+ Q4 -->|yes| S4["CSS over JS, DB constraint<br/>over app code"]
349
+ Q4 -->|no| Q5{Already-installed<br/>dependency?}
350
+ Q5 -->|yes| S5[Use it. Never add a new dep<br/>for what a few lines do]
351
+ Q5 -->|no| Q6{Can it be<br/>one line?}
352
+ Q6 -->|yes| S6[One line]
353
+ Q6 -->|no| S7[The minimum code that works]
354
+
355
+ S1 & S2 & S3 & S4 & S5 & S6 & S7 --> OUT[Ship it + note what was skipped<br/>and when to add it]
356
+
357
+ style Q1 fill:#1f2937,stroke:#d78a3c,color:#e6edf3
358
+ style OUT fill:#14532d,stroke:#2da44e,color:#e6edf3
359
+ style R fill:#1f2937,stroke:#8b949e,color:#e6edf3
230
360
  ```
231
- 1. Does this need to exist? → no: skip it (YAGNI)
232
- 2. Already in this codebase? → reuse it, don't rewrite
233
- 3. Stdlib does it? → use it
234
- 4. Native platform feature? → use it
235
- 5. Installed dependency? → use it
236
- 6. One line? → one line
237
- 7. Only then: the minimum that works
238
- ```
361
+
362
+ The ladder runs **after** reading, never instead of it. Note the exit: every rung lands on the same obligation — say what you skipped, so "later" doesn't quietly become "never".
239
363
 
240
364
  The ladder runs *after* reading the code — lazy about the solution, never about understanding. Lazy is not negligent: trust-boundary validation, data-loss handling, security, and accessibility are never on the chopping block.
241
365
 
@@ -250,17 +374,17 @@ Mark deliberate simplifications so "later" doesn't quietly become "never":
250
374
 
251
375
  ## Config
252
376
 
253
- **On by default.** After install, CHISLE activates automatically at `full` every session — no `/chisle` needed. Change the default level, or set `off` to stay dormant until you type `/chisle`:
377
+ **On by default.** After install, Chisle activates automatically every session — no `/chisle` needed. Set `off` to stay dormant until you type `/chisle`:
254
378
 
255
379
  ```bash
256
380
  # env var (highest priority)
257
- export CHISLE_DEFAULT_MODE=ultra
381
+ export CHISLE_DEFAULT_MODE=off
258
382
 
259
383
  # config file (persists across shells)
260
- ~/.config/chisle/config.json → { "defaultMode": "ultra" }
384
+ ~/.config/chisle/config.json → { "defaultMode": "off" }
261
385
  ```
262
386
 
263
- Resolution: env var → config file → `full`. Valid: `off`, `lite`, `full`, `ultra`.
387
+ Resolution: env var → config file → `on`. Valid: `off`, `on`.
264
388
 
265
389
  ---
266
390
 
@@ -285,7 +409,9 @@ Primarily a Claude Code plugin, but ships to every agent with a rules/context fi
285
409
  ## FAQ
286
410
 
287
411
  **Doesn't injecting a persona every turn cost tokens?**
288
- Yes — a one-time ruleset at session start (~1.8k tokens) plus a ~40-token reminder per turn. Output is where it pays back: coding answers shrink 40–60% (benchmarks), and output bills several × higher than input, so net is positive after the first couple of turns. On a one-line throwaway prompt the overhead can exceed the saving. The input-side hook has no such tradeoff — it only ever removes tokens.
412
+ Yes — a ruleset at session start (~1.6k tokens) plus a ~50-token reminder per turn. Output is where it pays back: coding answers shrink 40–60% (benchmarks), and output bills several × higher than input.
413
+
414
+ Worth reading the dissent before you take that on faith: [@enc0ded](https://github.com/enc0ded) measured 173 of their own sessions ([#2](https://github.com/JayPokale/Chisle/issues/2)) and found the injection overhead roughly cancelling the compressor's savings — because the ruleset was being re-sent on every resume and clear, not just at startup. That re-injection is fixed, which removes most of the overhead they measured, but their wider point stands: prose is only ~25% of what the model emits, so the ceiling on the output axis is lower than the headline suggests, and on a one-line throwaway prompt the overhead still exceeds the saving.
289
415
 
290
416
  **Will it golf my code into clever one-liners?**
291
417
  No. Boring over clever. Deletion beats addition; obfuscation isn't deletion.
@@ -310,7 +436,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md). Edit the skill (`skills/chisle/SKILL.md`
310
436
  Built by [Jay Pokale](https://github.com/JayPokale) with [Claude](https://claude.com/claude-code), [Antigravity](https://antigravity.google), and [Codex](https://openai.com/blog/openai-codex/) as co-engineers — the input-compression hook, the benchmark verification, and several of the bug hunts documented in the changelog were pair-work.
311
437
 
312
438
  ```bash
313
- npm test # 59 tests: flag safety, tracker, settings merge, installer, compressor
439
+ npm test # 67 tests: flag safety, tracker, settings merge, installer, compressor
314
440
  ```
315
441
 
316
442
  ## License
@@ -1,8 +1,7 @@
1
1
  # chisle slash command
2
2
  # Maximum-efficiency dev mode
3
- # Usage: /chisle [lite|full|ultra]
3
+ # Usage: /chisle · /chisle off
4
4
 
5
5
  [command]
6
6
  name = "chisle"
7
- description = "Activate chisle mode: zero-fluff prose + YAGNI ladder code decisions. Levels: lite, full (default), ultra."
8
- argument-hint = "[lite|full|ultra]"
7
+ description = "Activate chisle: zero-fluff prose + YAGNI ladder code decisions. /chisle off to stop."
@@ -3,13 +3,13 @@
3
3
  //
4
4
  // 1. Writes flag at $CLAUDE_CONFIG_DIR/.chisle-active
5
5
  // 2. Resets session turn counter
6
- // 3. Emits chisle ruleset (filtered to active level) as system context
6
+ // 3. Emits the chisle ruleset as system context
7
7
  // 4. Nudges user to configure statusline if missing
8
8
  // 5. Major-version update notice (majors only, cached, fail-silent)
9
9
 
10
10
  const fs = require('fs');
11
11
  const path = require('path');
12
- const { getDefaultMode, getClaudeDir, safeWriteFlag } = require('./chisle-config');
12
+ const { getDefaultMode, getClaudeDir, safeWriteFlag, legacySetting } = require('./chisle-config');
13
13
 
14
14
  // ── Update-notice helpers (pure — tested directly) ─────────────────────────
15
15
  // Minor/patch releases stay quiet: a nudge per major is signal, more is spam.
@@ -55,8 +55,16 @@ async function latestVersion(cachePath) {
55
55
  } catch (e) { return null; }
56
56
  }
57
57
 
58
+ // SessionStart fires on startup, resume, clear and compact. Only a genuinely
59
+ // new session needs the whole ruleset: on the other three the conversation
60
+ // already carries it, and the UserPromptSubmit reminder re-states the active
61
+ // behaviour every turn anyway. Re-sending ~1.6k tokens each time was the bulk
62
+ // of the plugin's own overhead and piled standing instructions on top of what
63
+ // the user actually asked for. Measured and reported by @enc0ded (#2).
64
+ const FULL_INJECT_SOURCES = new Set(['startup']);
65
+
58
66
  // ── Hook body ───────────────────────────────────────────────────────────────
59
- function run() {
67
+ function run(source) {
60
68
  const claudeDir = getClaudeDir();
61
69
  const flagPath = path.join(claudeDir, '.chisle-active');
62
70
  const settingsPath = path.join(claudeDir, 'settings.json');
@@ -72,8 +80,17 @@ function run() {
72
80
  // 1. Write flag
73
81
  safeWriteFlag(flagPath, mode);
74
82
 
75
- // 2. Read SKILL.md — single source of truth for behavior
76
- const modeLabel = mode;
83
+ // 2. Resumed/cleared/compacted session: reactivate, don't re-teach.
84
+ if (!FULL_INJECT_SOURCES.has(source)) {
85
+ process.stdout.write(
86
+ 'CHISLE ACTIVE (resumed). ' +
87
+ 'Ruleset already in context; see the chisle skill if it is not.'
88
+ );
89
+ return;
90
+ }
91
+
92
+ // 3. Read SKILL.md — single source of truth for behaviour. No level
93
+ // filtering any more: there is one mode, so the whole body ships.
77
94
  let skillContent = '';
78
95
  try {
79
96
  skillContent = fs.readFileSync(
@@ -84,31 +101,14 @@ function run() {
84
101
  let output;
85
102
 
86
103
  if (skillContent) {
87
- const body = skillContent.replace(/^---[\s\S]*?---\s*/, '');
88
-
89
- const filtered = body.split('\n').reduce((acc, line) => {
90
- const tableRowMatch = line.match(/^\|\s*\*\*(\S+?)\*\*\s*\|/);
91
- if (tableRowMatch) {
92
- if (tableRowMatch[1] === modeLabel) acc.push(line);
93
- return acc;
94
- }
95
- const exampleMatch = line.match(/^- (\S+?):\s/);
96
- if (exampleMatch) {
97
- if (exampleMatch[1] === modeLabel) acc.push(line);
98
- return acc;
99
- }
100
- acc.push(line);
101
- return acc;
102
- }, []);
103
-
104
- output = 'CHISLE MODE ACTIVE — level: ' + modeLabel + '\n\n' + filtered.join('\n');
104
+ output = 'CHISLE ACTIVE\n\n' + skillContent.replace(/^---[\s\S]*?---\s*/, '');
105
105
  } else {
106
106
  // Fallback ruleset when SKILL.md not found
107
107
  output =
108
- 'CHISLE MODE ACTIVE — level: ' + modeLabel + '\n\n' +
108
+ 'CHISLE ACTIVE\n\n' +
109
109
  'Chisle: maximum-efficiency dev mode. Zero-fluff prose. YAGNI-first code.\n\n' +
110
110
  '## Persistence\n\n' +
111
- 'ACTIVE EVERY RESPONSE. Off only: "stop chisle" / "normal mode". Switch: `/chisle lite|full|ultra`.\n\n' +
111
+ 'ACTIVE EVERY RESPONSE. Off only: "stop chisle" / "normal mode".\n\n' +
112
112
  '## Prose\n\n' +
113
113
  'Drop articles/filler/pleasantries/hedging. Fragments OK. Technical terms exact.\n\n' +
114
114
  '## Code\n\n' +
@@ -131,14 +131,25 @@ function run() {
131
131
  const statusLineSnippet =
132
132
  '"statusLine": { "type": "command", "command": ' + JSON.stringify(command) + ' }';
133
133
  output += '\n\n' +
134
- 'STATUSLINE SETUP NEEDED: The chisle plugin includes a statusline badge showing active mode ' +
135
- '(e.g. [CHISLE], [CHISLE:ULTRA]) with token savings. It is not configured yet. ' +
134
+ 'STATUSLINE SETUP NEEDED: The chisle plugin includes a statusline badge ' +
135
+ '([CHISLE]) with token savings. It is not configured yet. ' +
136
136
  'To enable, add this to ' + settingsPath + ': ' +
137
137
  statusLineSnippet + ' ' +
138
138
  'Proactively offer to set this up for the user on first interaction.';
139
139
  }
140
140
  } catch (e) {}
141
141
 
142
+ // 3b. One-time courtesy for the 3.0.0 upgrade: a lite/full/ultra setting is
143
+ // no longer meaningful. Chisle still runs (the value falls through to the
144
+ // default), but silently ignoring a setting someone chose is worse than
145
+ // saying so once.
146
+ const legacy = legacySetting();
147
+ if (legacy) {
148
+ output += '\n\nCHISLE NOTE: `' + legacy.value + '` came from ' + legacy.source +
149
+ '. Intensity levels were removed in 3.0.0 — there is one mode now, and it is ' +
150
+ 'active. Set it to `on` or `off`, or drop it entirely. Mention this once.';
151
+ }
152
+
142
153
  // 4. Update notice, then emit. CHISLE_UPDATE_CHECK=0 disables.
143
154
  (async () => {
144
155
  try {
@@ -155,6 +166,24 @@ function run() {
155
166
  })();
156
167
  }
157
168
 
158
- if (require.main === module) run();
169
+ // Claude Code pipes the hook payload (including `source`) on stdin. If it is
170
+ // absent or unparseable, fall back to a full inject — over-teaching once is a
171
+ // far cheaper failure than a session that never receives the ruleset at all.
172
+ function main() {
173
+ let input = '';
174
+ process.stdin.on('data', chunk => { input += chunk; });
175
+ process.stdin.on('end', () => {
176
+ let source = 'startup';
177
+ try {
178
+ const parsed = JSON.parse(input.replace(/^/, ''));
179
+ if (parsed && typeof parsed.source === 'string') source = parsed.source;
180
+ } catch (e) {}
181
+ run(source);
182
+ });
183
+ // stdin never opened (manual run): behave like a fresh session.
184
+ if (process.stdin.isTTY) run('startup');
185
+ }
186
+
187
+ if (require.main === module) main();
159
188
 
160
189
  module.exports = { majorOf, majorUpdateNotice };
@@ -19,11 +19,11 @@
19
19
  // scrub (output > 1k) — lossless: strip ANSI escapes, collapse blank-line
20
20
  // runs, collapse ≥4 identical consecutive lines to
21
21
  // one + "[repeated N×]". No information lost.
22
- // elide (output > mode threshold) — head + tail + error salvage, as above.
22
+ // elide (output > threshold) — head + tail + error salvage, as above.
23
23
  // Plus dedup: a tool output byte-identical to that tool's immediately previous
24
24
  // output is replaced by a short marker — the content is already in context.
25
25
  //
26
- // Compression tracks the /chisle mode flag (off → untouched). Tunables via env:
26
+ // Compression follows the /chisle flag (off → untouched). Tunables via env:
27
27
  // CHISLE_COMPRESS=0 — kill switch
28
28
  // CHISLE_COMPRESS_SCRUB=0 — disable the lossless scrub tier
29
29
  // CHISLE_COMPRESS_DEDUP=0 — disable duplicate-output markers
@@ -39,12 +39,10 @@ const path = require('path');
39
39
  const crypto = require('crypto');
40
40
  const { getClaudeDir, readFlag } = require('./chisle-config');
41
41
 
42
- // Mode → thresholds. Tighter mode, tighter budget. Env overrides all.
43
- const THRESHOLDS = {
44
- lite: { maxChars: 16000, headLines: 100, tailLines: 80 },
45
- full: { maxChars: 8000, headLines: 60, tailLines: 40 },
46
- ultra: { maxChars: 5000, headLines: 40, tailLines: 30 },
47
- };
42
+ // One threshold. Env overrides all.
43
+ // These are the values the old 'full' level used — the default,
44
+ // and the only one the published benchmarks ever ran at.
45
+ const THRESHOLDS = { maxChars: 8000, headLines: 60, tailLines: 40 };
48
46
 
49
47
  // Tools whose output is safe to elide. Read/Edit/Write are absent on purpose:
50
48
  // their output feeds exact-match edits. mcp__* are read-only info tools.
@@ -66,8 +64,8 @@ function envInt(name, fallback) {
66
64
  return Number.isFinite(n) && n > 0 ? n : fallback;
67
65
  }
68
66
 
69
- function limitsFor(mode) {
70
- const base = THRESHOLDS[mode] || THRESHOLDS.full;
67
+ function limitsFor() {
68
+ const base = THRESHOLDS;
71
69
  return {
72
70
  maxChars: envInt('CHISLE_COMPRESS_MAX_CHARS', base.maxChars),
73
71
  headLines: envInt('CHISLE_COMPRESS_HEAD_LINES', base.headLines),
@@ -225,6 +223,32 @@ function transform(text, limits) {
225
223
  return text.length - t.length >= MIN_WIN ? t : null;
226
224
  }
227
225
 
226
+ // Put the compressed text back into the ORIGINAL response shape.
227
+ //
228
+ // Claude Code validates updatedToolOutput against the tool's own output schema
229
+ // before applying it. Bash results are objects ({stdout, stderr, ...}), so
230
+ // handing back a bare string is rejected on every call — the hook appears to
231
+ // work, logs savings, and the model still receives the full output. Returning
232
+ // null (skip) is always safer than emitting a shape the harness will refuse.
233
+ // Reported with the transcript evidence by @sovdchains (#3).
234
+ function rebuildResponse(response, updated) {
235
+ if (response == null || typeof response === 'string') return updated;
236
+ if (typeof response === 'object' && !Array.isArray(response)) {
237
+ for (const key of ['stdout', 'output', 'content', 'text', 'result']) {
238
+ if (typeof response[key] === 'string') {
239
+ const out = { ...response, [key]: updated };
240
+ // extractText already folded stderr into the compressed text; leaving
241
+ // the original stderr in place would duplicate it.
242
+ if (key === 'stdout' && typeof response.stderr === 'string') out.stderr = '';
243
+ return out;
244
+ }
245
+ }
246
+ }
247
+ // Unrecognized shape (e.g. MCP content-block arrays). Skip rather than guess:
248
+ // a wrong shape is silently rejected, which is the bug this function fixes.
249
+ return null;
250
+ }
251
+
228
252
  // Full pipeline on one hook payload → updated output string, or null to keep
229
253
  // the original. Pure given (payload, mode, env) except dedup state.
230
254
  function processPayload(payload, mode) {
@@ -237,7 +261,7 @@ function processPayload(payload, mode) {
237
261
  if (!text) return null;
238
262
  const dup = dedupCheck(payload.tool_name, text, payload.session_id);
239
263
  if (dup != null && dup.length < text.length) return dup;
240
- return transform(text, limitsFor(mode));
264
+ return transform(text, limitsFor());
241
265
  }
242
266
 
243
267
  function main() {
@@ -249,12 +273,18 @@ function main() {
249
273
  const mode = readFlag(path.join(getClaudeDir(), '.chisle-active'));
250
274
  const updated = processPayload(payload, mode);
251
275
  if (updated == null) return;
252
- const original = extractText(payload.tool_response != null ? payload.tool_response : payload.tool_output);
276
+ const response = payload.tool_response != null ? payload.tool_response : payload.tool_output;
277
+ const rebuilt = rebuildResponse(response, updated);
278
+ // Shape we can't safely rebuild — emit nothing and count nothing.
279
+ if (rebuilt == null) return;
280
+ const original = extractText(response);
281
+ // Only after we know a replacement is actually going out, so the stats
282
+ // file stops crediting savings the harness never applied.
253
283
  recordSavings(original.length - updated.length);
254
284
  process.stdout.write(JSON.stringify({
255
285
  hookSpecificOutput: {
256
286
  hookEventName: 'PostToolUse',
257
- updatedToolOutput: updated,
287
+ updatedToolOutput: rebuilt,
258
288
  },
259
289
  }));
260
290
  } catch (e) {} // silent — never break the tool pipeline
@@ -263,4 +293,4 @@ function main() {
263
293
 
264
294
  if (require.main === module) main();
265
295
 
266
- module.exports = { extractText, scrub, compress, transform, limitsFor, toolAllowed, processPayload, THRESHOLDS, SAFE_TOOLS };
296
+ module.exports = { extractText, rebuildResponse, scrub, compress, transform, limitsFor, toolAllowed, processPayload, THRESHOLDS, SAFE_TOOLS };
@@ -4,13 +4,35 @@
4
4
  // Resolution order for default mode:
5
5
  // 1. CHISLE_DEFAULT_MODE environment variable
6
6
  // 2. User config: $XDG_CONFIG_HOME/chisle/config.json, ~/.config/chisle/config.json
7
- // 3. 'full'
7
+ // 3. 'on'
8
8
 
9
9
  const fs = require('fs');
10
10
  const path = require('path');
11
11
  const os = require('os');
12
12
 
13
- const VALID_MODES = ['off', 'lite', 'full', 'ultra'];
13
+ // One mode. The intensity levels are gone: three dials on a tool whose whole
14
+ // argument is "fewer knobs" was the joke writing itself, and the middle setting
15
+ // was the only one ever benchmarked. 'on' or 'off'.
16
+ const VALID_MODES = ['off', 'on'];
17
+
18
+ // Values that meant something before 3.0.0. Kept only so the upgrade can say
19
+ // "your setting no longer does anything" instead of ignoring it in silence.
20
+ const LEGACY_MODES = ['lite', 'full', 'ultra'];
21
+
22
+ // Returns the stale value and where it came from, or null. Never throws.
23
+ function legacySetting() {
24
+ const env = process.env.CHISLE_DEFAULT_MODE;
25
+ if (env && LEGACY_MODES.includes(String(env).toLowerCase())) {
26
+ return { value: String(env).toLowerCase(), source: 'CHISLE_DEFAULT_MODE' };
27
+ }
28
+ try {
29
+ const p = path.join(getConfigDir(), 'config.json');
30
+ const c = JSON.parse(fs.readFileSync(p, 'utf8'));
31
+ const v = c && c.defaultMode ? String(c.defaultMode).toLowerCase() : null;
32
+ if (v && LEGACY_MODES.includes(v)) return { value: v, source: p };
33
+ } catch (e) {}
34
+ return null;
35
+ }
14
36
 
15
37
  function getClaudeDir() {
16
38
  return process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
@@ -48,7 +70,7 @@ function getDefaultMode() {
48
70
  }
49
71
  const userMode = readModeFromConfigFile(path.join(getConfigDir(), 'config.json'));
50
72
  if (userMode) return userMode;
51
- return 'full';
73
+ return 'on';
52
74
  }
53
75
 
54
76
  // Symlink-safe, atomic flag write with 0600 perms.
@@ -149,4 +171,7 @@ function readFlag(flagPath) {
149
171
  }
150
172
  }
151
173
 
152
- module.exports = { getDefaultMode, getClaudeDir, VALID_MODES, safeWriteFlag, readFlag };
174
+ module.exports = {
175
+ LEGACY_MODES, legacySetting, getDefaultMode, getClaudeDir,
176
+ VALID_MODES, safeWriteFlag, readFlag,
177
+ };
@@ -5,7 +5,7 @@
5
5
 
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
- const { getDefaultMode, getClaudeDir, VALID_MODES, safeWriteFlag, readFlag } = require('./chisle-config');
8
+ const { getDefaultMode, getClaudeDir, safeWriteFlag, readFlag } = require('./chisle-config');
9
9
 
10
10
  const claudeDir = getClaudeDir();
11
11
  const flagPath = path.join(claudeDir, '.chisle-active');
@@ -28,28 +28,16 @@ process.stdin.on('end', () => {
28
28
  }
29
29
  }
30
30
 
31
- // /chisle slash commands
31
+ // /chisle slash commands. No level argument any more: /chisle on,
32
+ // /chisle off, nothing else.
32
33
  if (/^\/chisle(\b|:chisle\b)/.test(promptLower)) {
33
34
  const parts = promptLower.split(/\s+/);
34
- const cmd = parts[0];
35
35
  const arg = parts[1] || '';
36
-
37
- let mode = null;
38
-
39
- if (cmd === '/chisle' || cmd === '/chisle:chisle') {
40
- if (!arg) {
41
- mode = getDefaultMode();
42
- } else if (arg === 'off' || arg === 'stop' || arg === 'disable') {
43
- mode = 'off';
44
- } else if (VALID_MODES.includes(arg)) {
45
- mode = arg;
46
- }
47
- }
48
-
49
- if (mode && mode !== 'off') {
50
- safeWriteFlag(flagPath, mode);
51
- } else if (mode === 'off') {
36
+ if (arg === 'off' || arg === 'stop' || arg === 'disable') {
52
37
  try { fs.unlinkSync(flagPath); } catch (e) {}
38
+ } else {
39
+ const mode = getDefaultMode();
40
+ if (mode !== 'off') safeWriteFlag(flagPath, mode);
53
41
  }
54
42
  }
55
43
 
@@ -67,17 +55,13 @@ process.stdin.on('end', () => {
67
55
  const activeMode = readFlag(flagPath);
68
56
  if (activeMode && activeMode !== 'off') {
69
57
  // Inject compact reminder — keeps chisle visible across context compression
70
- const ladderHint = activeMode === 'ultra'
71
- ? 'YAGNI extremist: delete before add, challenge req in same breath.'
72
- : 'Code: YAGNI ladder first (stdlib → native → dep → one line → min code).';
73
-
74
58
  process.stdout.write(JSON.stringify({
75
59
  hookSpecificOutput: {
76
60
  hookEventName: 'UserPromptSubmit',
77
61
  additionalContext:
78
- 'CHISLE MODE ACTIVE (' + activeMode + '). ' +
62
+ 'CHISLE ACTIVE. ' +
79
63
  'Prose: drop articles/filler/pleasantries/hedging. Fragments OK. ' +
80
- ladderHint + ' ' +
64
+ 'Code: YAGNI ladder first (reuse → stdlib → native → dep → one line → min code). ' +
81
65
  'Code/commits/security: write normal.'
82
66
  }
83
67
  }));
@@ -21,10 +21,8 @@ $mode = $raw.Substring(0, [Math]::Min(64, $raw.Length)).ToLower()
21
21
  $mode = ($mode -replace '[^a-z0-9-]', '')
22
22
 
23
23
  switch ($mode) {
24
- 'off' { }
25
- 'lite' { }
26
- 'full' { }
27
- 'ultra' { }
24
+ 'off' { }
25
+ 'on' { }
28
26
  default { exit 0 }
29
27
  }
30
28
 
@@ -47,8 +45,5 @@ if ($sitem -and -not ($sitem.Attributes -band [IO.FileAttributes]::ReparsePoint)
47
45
  }
48
46
  }
49
47
 
50
- if ([string]::IsNullOrEmpty($mode) -or $mode -eq 'full') {
51
- Write-Host -NoNewline "$orange[CHISLE]$reset$suffix"
52
- } else {
53
- Write-Host -NoNewline ("{0}[CHISLE:{1}]{2}{3}" -f $orange, $mode.ToUpper(), $reset, $suffix)
54
- }
48
+ # One mode, one badge.
49
+ Write-Host -NoNewline "$orange[CHISLE]$reset$suffix"
@@ -21,7 +21,7 @@ MODE=$(printf '%s' "$MODE" | tr -cd 'a-z0-9-')
21
21
 
22
22
  # Whitelist — render nothing for unknown values rather than echo attacker bytes.
23
23
  case "$MODE" in
24
- off|lite|full|ultra) ;;
24
+ off|on) ;;
25
25
  *) exit 0 ;;
26
26
  esac
27
27
 
@@ -85,10 +85,5 @@ if [ -f "$STATS" ] && [ ! -L "$STATS" ]; then
85
85
  fi
86
86
  fi
87
87
 
88
- # Orange badge + loading bars trailing outside the bracket
89
- if [ -z "$MODE" ] || [ "$MODE" = "full" ]; then
90
- printf '\033[38;5;172m[CHISLE]\033[0m%s' "$LIMITS"
91
- else
92
- SUFFIX=$(printf '%s' "$MODE" | tr '[:lower:]' '[:upper:]')
93
- printf '\033[38;5;172m[CHISLE:%s]\033[0m%s' "$SUFFIX" "$LIMITS"
94
- fi
88
+ # Orange badge + loading bars trailing outside the bracket. One mode, one badge.
89
+ printf '\033[38;5;172m[CHISLE]\033[0m%s' "$LIMITS"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chisle",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Maximum-efficiency dev mode for Claude Code. Zero-fluff prose + YAGNI-first code decisions. One plugin that does both.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -48,7 +48,9 @@
48
48
  "check:rules": "node scripts/build-rules.js --check",
49
49
  "build:chart": "node scripts/build-chart.js",
50
50
  "check:chart": "node scripts/build-chart.js --check",
51
- "bench": "bash benchmarks/run-live.sh"
51
+ "bench": "bash benchmarks/run-live.sh",
52
+ "build:samples": "node scripts/build-samples.js",
53
+ "check:samples": "node scripts/build-samples.js --check"
52
54
  },
53
55
  "files": [
54
56
  "bin/",
@@ -10,4 +10,4 @@ YAGNI → reuse existing → stdlib → native platform → installed dep → on
10
10
  No unrequested abstractions. Deletion over addition. Shortest diff wins.
11
11
 
12
12
  **Deactivate:** "stop chisle" / "normal mode"
13
- **Switch level:** `/chisle lite` · `/chisle full` · `/chisle ultra`
13
+ **Off:** `/chisle off` · "stop chisle" · "normal mode"
@@ -3,11 +3,10 @@ name: chisle
3
3
  description: >
4
4
  Maximum-efficiency dev mode. Terse, precise prose with zero fluff combined with
5
5
  YAGNI/ladder-first code decisions. One persona: the senior dev who deletes code
6
- for fun and bills by the syllable. Supports intensity levels: lite, full (default),
7
- ultra. Trigger: /chisle. Deactivate: "stop chisle" / "normal mode".
6
+ for fun and bills by the syllable. Trigger: /chisle.
7
+ Deactivate: "stop chisle" / "normal mode".
8
8
  Use when user says "chisle mode", "activate chisle", "chislify", "be efficient", "be minimal",
9
9
  "no fluff", "yagni", or invokes /chisle.
10
- argument-hint: "[lite|full|ultra]"
11
10
  ---
12
11
 
13
12
  # Chisle
@@ -17,7 +16,7 @@ Maximum signal. Minimum noise. Write less. Ship less. Mean more.
17
16
  ## Persistence
18
17
 
19
18
  ACTIVE EVERY RESPONSE. No drift back to verbose over-building. Still active if unsure.
20
- Off only: "stop chisle" / "normal mode". Default: **full**. Switch: `/chisle lite|full|ultra`.
19
+ Off only: "stop chisle" / "normal mode" / `/chisle off`. One mode — no levels to pick.
21
20
 
22
21
  ## Prose: Maximum Signal Per Token
23
22
 
@@ -91,23 +90,16 @@ Pattern: `[code] → skipped: [X], add when [Y].`
91
90
 
92
91
  No essays. No feature tours. No design notes. Explanation longer than code → delete explanation.
93
92
 
94
- ## Intensity
93
+ ## What it sounds like
95
94
 
96
- | Level | What change |
97
- |-------|------------|
98
- | **lite** | No filler/hedging, keep articles + full sentences. Flag the more minimal approach in one line. User picks. |
99
- | **full** | Drop articles, fragments OK, short synonyms. Ladder enforced. Stdlib and native first. Shortest diff, shortest explanation. |
100
- | **ultra** | Abbreviate prose words (DB/auth/config/req/res/fn). Strip conjunctions, arrows for causality (X → Y). YAGNI extremist: deletion before addition, challenge the requirement in same breath. Code symbols/function names/API names: never abbreviate. |
101
-
102
- Example — "Why React component re-render?"
103
- - lite: "Component re-renders because you create a new object reference each render. Wrap it in `useMemo`. FYI: `useCallback` on handlers for same reason."
104
- - full: "New object ref each render. Inline object prop = new ref = re-render. `useMemo`."
105
- - ultra: "Inline obj prop → new ref → re-render. `useMemo`. Why inline object at all?"
95
+ Example — "Why does this React component re-render?"
96
+ > New object ref each render. Inline object prop = new ref = re-render. `useMemo`.
106
97
 
107
98
  Example — "Add a cache for API responses."
108
- - lite: "Cache added. FYI: `functools.lru_cache` covers this in one line if you'd rather not own a class."
109
- - full: "`@lru_cache(maxsize=1000)` on fetch fn. Skipped custom cache class, add when lru_cache measurably falls short."
110
- - ultra: "No cache until profiler says so. When it does: `@lru_cache`. Hand-rolled TTL cache = bug farm with hit rate."
99
+ > `@lru_cache(maxsize=1000)` on the fetch fn. Skipped a custom cache class — add one when `lru_cache` measurably falls short.
100
+
101
+ Example — "Summarize the tradeoffs between REST and GraphQL."
102
+ > GraphQL: one round trip, client picks fields → fewer over-fetches, harder caching + N+1 risk. REST: dumb caching that works, more round trips. New API, unknown clients → REST until a client actually needs field selection.
111
103
 
112
104
  ## Auto-Clarity
113
105
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: chisle-help
3
3
  description: >
4
- Quick-reference card for Chisle modes, levels, and commands. One-shot
4
+ Quick-reference card for Chisle commands. One-shot
5
5
  display, not a persistent mode. Use when the user says "chisle help", "how do I
6
6
  use chisle", "what chisle commands", or invokes /chisle-help.
7
7
  ---
@@ -14,29 +14,20 @@ description: >
14
14
 
15
15
  | Command | Effect |
16
16
  |---------|--------|
17
- | `/chisle` | Activate at default level (full) |
18
- | `/chisle lite` | Tighter prose, flags the minimal alternative |
19
- | `/chisle full` | Full compression + YAGNI ladder |
20
- | `/chisle ultra` | Extremist: abbreviate prose, delete before add |
17
+ | `/chisle` | Activate |
21
18
  | `/chisle-audit [path]` | Audit a diff/file/repo for both code bloat AND prose verbosity |
22
19
  | `/chisle-review` | Review the current diff for over-engineering |
23
20
  | `stop chisle` / `normal mode` | Deactivate |
24
21
 
25
22
  Natural language works: "activate chisle", "chisle mode", "chislify this".
26
23
 
27
- ## Levels at a glance
28
-
29
- - **lite** — keeps full sentences; names the lazier code option, you pick.
30
- - **full** — drops articles, fragments OK, ladder enforced. Default.
31
- - **ultra** — abbreviates prose words, challenges the requirement itself.
32
-
33
24
  ## The code ladder
34
25
 
35
26
  YAGNI → reuse → stdlib → native → installed dep → one line → minimum code.
36
27
 
37
28
  ## Statusline
38
29
 
39
- `[CHISLE]` / `[CHISLE:ULTRA]` — shows the active level. Configure via
30
+ `[CHISLE]` — shown while active. Configure via
40
31
  `hooks/chisle-statusline.sh` (or `.ps1` on Windows).
41
32
 
42
33
  ## Never minimal about