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.
- package/.claude-plugin/plugin.json +1 -1
- package/.clinerules/chisle.md +1 -1
- package/.cursor/rules/chisle.mdc +1 -1
- package/.github/copilot-instructions.md +1 -1
- package/.kiro/steering/chisle.md +1 -1
- package/.windsurf/rules/chisle.md +1 -1
- package/AGENTS.md +2 -2
- package/README.md +159 -33
- package/commands/chisle.toml +2 -3
- package/hooks/chisle-activate.js +57 -28
- package/hooks/chisle-compress-output.js +44 -14
- package/hooks/chisle-config.js +29 -4
- package/hooks/chisle-mode-tracker.js +9 -25
- package/hooks/chisle-statusline.ps1 +4 -9
- package/hooks/chisle-statusline.sh +3 -8
- package/package.json +4 -2
- package/rules/chisle-activate.md +1 -1
- package/skills/chisle/SKILL.md +10 -18
- package/skills/chisle-help/SKILL.md +3 -12
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chisle",
|
|
3
|
-
"version": "
|
|
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": [
|
package/.clinerules/chisle.md
CHANGED
|
@@ -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
|
-
|
|
36
|
+
Always on once installed. Deactivate: "stop chisle" / "normal mode".
|
package/.cursor/rules/chisle.mdc
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
36
|
+
Always on once installed. Deactivate: "stop chisle" / "normal mode".
|
package/.kiro/steering/chisle.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
44
|
+
## Switching off
|
|
45
45
|
|
|
46
|
-
|
|
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.
|
|
2
|
+
<img src="assets/logo.png" width="120" alt="Chisle">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
<h1 align="center"
|
|
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>
|
|
25
|
+
<strong>44% of a bare model on coding work · 41% on the long ones · 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
|
|
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
|
-
|
|
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**
|
|
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
|
-
|
|
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?"**
|
|
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
|
-
**"
|
|
203
|
-
>
|
|
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
|
|
215
|
-
| `/chisle` | Re-activate
|
|
216
|
-
| `/chisle
|
|
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".
|
|
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
|
-
|
|
232
|
-
|
|
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,
|
|
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=
|
|
381
|
+
export CHISLE_DEFAULT_MODE=off
|
|
258
382
|
|
|
259
383
|
# config file (persists across shells)
|
|
260
|
-
~/.config/chisle/config.json → { "defaultMode": "
|
|
384
|
+
~/.config/chisle/config.json → { "defaultMode": "off" }
|
|
261
385
|
```
|
|
262
386
|
|
|
263
|
-
Resolution: env var → config file → `
|
|
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
|
|
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 #
|
|
439
|
+
npm test # 67 tests: flag safety, tracker, settings merge, installer, compressor
|
|
314
440
|
```
|
|
315
441
|
|
|
316
442
|
## License
|
package/commands/chisle.toml
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# chisle slash command
|
|
2
2
|
# Maximum-efficiency dev mode
|
|
3
|
-
# Usage: /chisle
|
|
3
|
+
# Usage: /chisle · /chisle off
|
|
4
4
|
|
|
5
5
|
[command]
|
|
6
6
|
name = "chisle"
|
|
7
|
-
description = "Activate chisle
|
|
8
|
-
argument-hint = "[lite|full|ultra]"
|
|
7
|
+
description = "Activate chisle: zero-fluff prose + YAGNI ladder code decisions. /chisle off to stop."
|
package/hooks/chisle-activate.js
CHANGED
|
@@ -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
|
|
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.
|
|
76
|
-
|
|
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
|
-
|
|
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
|
|
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"
|
|
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
|
|
135
|
-
'(
|
|
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
|
-
|
|
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 >
|
|
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
|
|
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
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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(
|
|
70
|
-
const base = THRESHOLDS
|
|
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(
|
|
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
|
|
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:
|
|
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 };
|
package/hooks/chisle-config.js
CHANGED
|
@@ -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. '
|
|
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
|
-
|
|
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 '
|
|
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 = {
|
|
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,
|
|
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
|
|
62
|
+
'CHISLE ACTIVE. ' +
|
|
79
63
|
'Prose: drop articles/filler/pleasantries/hedging. Fragments OK. ' +
|
|
80
|
-
|
|
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
|
-
'
|
|
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
|
-
|
|
51
|
-
|
|
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|
|
|
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
|
-
|
|
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": "
|
|
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/",
|
package/rules/chisle-activate.md
CHANGED
|
@@ -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
|
-
**
|
|
13
|
+
**Off:** `/chisle off` · "stop chisle" · "normal mode"
|
package/skills/chisle/SKILL.md
CHANGED
|
@@ -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.
|
|
7
|
-
|
|
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"
|
|
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
|
-
##
|
|
93
|
+
## What it sounds like
|
|
95
94
|
|
|
96
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
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
|
|
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]`
|
|
30
|
+
`[CHISLE]` — shown while active. Configure via
|
|
40
31
|
`hooks/chisle-statusline.sh` (or `.ps1` on Windows).
|
|
41
32
|
|
|
42
33
|
## Never minimal about
|