chisle 3.4.0 → 3.6.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": "3.4.0",
3
+ "version": "3.6.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": [
@@ -8,7 +8,8 @@ meaning survives. Causality as arrows (X → Y). Technical terms, code, API name
8
8
  errors: exact, verbatim. Terse ≠ incomplete: keep every decisive fact (the fix,
9
9
  the gotcha, the why); cut the words around them, never the facts. Structure is
10
10
  tokens, so answer at the question's altitude; no manufactured headings, bullet
11
- lists, or sections the question didn't ask for. "Summarize/compare X vs Y" is
11
+ lists, numbered steps, or sections the question didn't ask for. Two tight
12
+ paragraphs beat five headed sections. "Summarize/compare X vs Y" is
12
13
  the trap: skip headed pro/con bullet walls, name the decisive tradeoffs in
13
14
  prose, give the verdict, stop.
14
15
 
@@ -13,7 +13,8 @@ meaning survives. Causality as arrows (X → Y). Technical terms, code, API name
13
13
  errors: exact, verbatim. Terse ≠ incomplete: keep every decisive fact (the fix,
14
14
  the gotcha, the why); cut the words around them, never the facts. Structure is
15
15
  tokens, so answer at the question's altitude; no manufactured headings, bullet
16
- lists, or sections the question didn't ask for. "Summarize/compare X vs Y" is
16
+ lists, numbered steps, or sections the question didn't ask for. Two tight
17
+ paragraphs beat five headed sections. "Summarize/compare X vs Y" is
17
18
  the trap: skip headed pro/con bullet walls, name the decisive tradeoffs in
18
19
  prose, give the verdict, stop.
19
20
 
@@ -8,7 +8,8 @@ meaning survives. Causality as arrows (X → Y). Technical terms, code, API name
8
8
  errors: exact, verbatim. Terse ≠ incomplete: keep every decisive fact (the fix,
9
9
  the gotcha, the why); cut the words around them, never the facts. Structure is
10
10
  tokens, so answer at the question's altitude; no manufactured headings, bullet
11
- lists, or sections the question didn't ask for. "Summarize/compare X vs Y" is
11
+ lists, numbered steps, or sections the question didn't ask for. Two tight
12
+ paragraphs beat five headed sections. "Summarize/compare X vs Y" is
12
13
  the trap: skip headed pro/con bullet walls, name the decisive tradeoffs in
13
14
  prose, give the verdict, stop.
14
15
 
@@ -12,7 +12,8 @@ meaning survives. Causality as arrows (X → Y). Technical terms, code, API name
12
12
  errors: exact, verbatim. Terse ≠ incomplete: keep every decisive fact (the fix,
13
13
  the gotcha, the why); cut the words around them, never the facts. Structure is
14
14
  tokens, so answer at the question's altitude; no manufactured headings, bullet
15
- lists, or sections the question didn't ask for. "Summarize/compare X vs Y" is
15
+ lists, numbered steps, or sections the question didn't ask for. Two tight
16
+ paragraphs beat five headed sections. "Summarize/compare X vs Y" is
16
17
  the trap: skip headed pro/con bullet walls, name the decisive tradeoffs in
17
18
  prose, give the verdict, stop.
18
19
 
@@ -12,7 +12,8 @@ meaning survives. Causality as arrows (X → Y). Technical terms, code, API name
12
12
  errors: exact, verbatim. Terse ≠ incomplete: keep every decisive fact (the fix,
13
13
  the gotcha, the why); cut the words around them, never the facts. Structure is
14
14
  tokens, so answer at the question's altitude; no manufactured headings, bullet
15
- lists, or sections the question didn't ask for. "Summarize/compare X vs Y" is
15
+ lists, numbered steps, or sections the question didn't ask for. Two tight
16
+ paragraphs beat five headed sections. "Summarize/compare X vs Y" is
16
17
  the trap: skip headed pro/con bullet walls, name the decisive tradeoffs in
17
18
  prose, give the verdict, stop.
18
19
 
package/AGENTS.md CHANGED
@@ -13,6 +13,11 @@ Drop articles, filler (just/really/basically/actually), pleasantries
13
13
  (sure/certainly/happy to), hedging. Fragments OK. Technical terms exact.
14
14
  Code blocks unchanged. Pattern: `[thing] [action] [reason].`
15
15
 
16
+ Terse ≠ incomplete: keep every decisive fact (the fix, the gotcha, the why);
17
+ cut the words around them. Structure is tokens: no headings, bullets,
18
+ numbered steps or tables the question didn't ask for. Two tight paragraphs
19
+ beat five headed sections.
20
+
16
21
  ## Code: the efficiency ladder
17
22
 
18
23
  Before writing anything, stop at the first rung that holds:
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <em>The only tool in this class that publishes the runs where it lost.</em>
12
+ <em>The only tool in this class that publishes the runs where it lost. <a href="https://jaypokale.me/writing/chisle-benchmarks-it-loses">Here's why.</a></em>
13
13
  </p>
14
14
 
15
15
  <p align="center">
@@ -22,7 +22,7 @@
22
22
  </p>
23
23
 
24
24
  <p align="center">
25
- <strong>44% of a bare model's output tokens on coding prompts &middot; 11 agents &middot; zero dependencies &middot; one command</strong>
25
+ <strong>83% of a bare model's output, where caveman and ponytail land above 100% &middot; 11 agents &middot; zero dependencies &middot; one command</strong>
26
26
  </p>
27
27
 
28
28
  <p align="center">
@@ -153,15 +153,25 @@ That refreshes every agent that already has Chisle and installs it into none tha
153
153
  - **Plain `npx chisle` does not upgrade.** Every install path skips what is already present, so an upgrade run reports success and changes nothing. `--update` pairs the refresh with that check.
154
154
  - **`@latest` matters.** `npx chisle` can serve a cached copy of the package from a previous run, so the pin is what guarantees you get the new one.
155
155
 
156
- Per-agent equivalents, if you prefer the native tool:
156
+ Update one agent at a time with `--only`:
157
157
 
158
158
  ```bash
159
- claude plugin update chisle@chisle # Claude Code plugin install
160
- pi install npm:chisle # Pi package
161
- gemini extensions install https://github.com/JayPokale/Chisle
159
+ npx chisle@latest --update --only claude # or: claude plugin update chisle@chisle
160
+ npx chisle@latest --update --only pi # or: pi install npm:chisle
161
+ npx chisle@latest --update --only gemini # or: gemini extensions install https://github.com/JayPokale/Chisle
162
+ npx chisle@latest --update --only codex
163
+ npx chisle@latest --update --only opencode # ruleset + skills + the compression plugin
164
+ npx chisle@latest --update --only hermes
162
165
  ```
163
166
 
164
- Project-scoped agents (Cursor, Windsurf, Cline, Kiro, Copilot) keep their rule file inside the repo, so run the update once per project that has one.
167
+ Cursor, Windsurf, Cline, Kiro and Copilot keep their rule file **inside the repo**, because that is how those agents load rules:
168
+
169
+ ```bash
170
+ cd ~/code/my-project
171
+ npx chisle@latest --update --only cursor
172
+ ```
173
+
174
+ `--update` looks for that file in the current directory, so running it from `~` reports "Nothing to update" even when the project is set up correctly. Run it once per repo that has one. Full table, including what each agent's update actually refreshes: [INSTALL.md](./INSTALL.md#per-agent).
165
175
 
166
176
  Not sure what you are running? `npx chisle --list` prints the agents it detects, and `claude plugin list` shows the installed plugin and its version.
167
177
 
@@ -224,7 +234,21 @@ node benchmarks/replay-compress.js pi # Pi; marginal over Pi's native truncat
224
234
 
225
235
  Nothing here is estimated. Every figure below is recomputed from committed raw data; the 2026-07-07 [verification writeup](benchmarks/results/2026-07-07-verify-rerun.md) re-derived the old claims from scratch, re-ran the whole suite against the competitors' **installed plugins**, and retired the one claim that didn't survive.
226
236
 
227
- ### Output axis: vs caveman & ponytail, 20 live tasks
237
+ ### Output axis, re-measured 2026-10-01 (current)
238
+
239
+ Re-run with the shipped ruleset, Claude Code 2.1.285 and the rivals' current plugins (caveman `ef6050c5e184`, ponytail 4.8.3): 13 live prompts × 2 seeds = 26 cells per arm on Haiku 4.5, billed output tokens vs the no-tool baseline ([writeup + raw cells](benchmarks/results/2026-10-01-live-rerun.md)):
240
+
241
+ | | total bill | 95% CI | visible answer | worst cell | backfires |
242
+ |---|--:|--:|--:|--:|--:|
243
+ | caveman | 102% | 79–128% | 105% | 305% | 15 / 26 |
244
+ | ponytail | 105% | 86–129% | 97% | 493% | 16 / 26 |
245
+ | **Chisle** | **83%** | **69–95%** | **76%** | **170%** | **11 / 26** |
246
+
247
+ Chisle is the only arm measurably below a bare model, by about a third of what the June table below claimed. It pays on long answers (77%) and coding prompts (76%); on short answers it breaks even (106%), on explanation prompts ponytail is leaner (86% vs 91%), and reasoning tokens are not cut (107%). Single cells swing hard between seeds (57% → 135% on one prompt), and the bare model "backfires" against itself on 13 of 26 cells, so read totals, not backfire counts ([control](benchmarks/results/2026-10-01-backfires.md)).
248
+
249
+ **Why the June headline was retired.** Its 52% leaned on one cell: the June bare `cache` answer billed 4,910 tokens, while every later run of the same prompt billed 375–813. The ruleset also carried examples matching `cache` (from June 18) and `auth-bug` (from June 27), so four cells may have been primed; without them the June total was 70%. The tables below stay as what they were.
250
+
251
+ ### Output axis, June–July 2026 (superseded): vs caveman & ponytail, 20 live tasks
228
252
 
229
253
  **What this suite measures, and what it does not.** Every figure below is *billed output tokens on single-turn prompts with no tools available*. That isolates the ruleset's effect on how the model writes, which is what it was built to measure. It is not whole-session cost: a real agentic session is dominated by tool output and cached input, so a tool can win here and still fail to pay for itself end to end. The input axis below is measured separately, and against its own baseline.
230
254
 
@@ -316,10 +340,10 @@ SUITE=large bash benchmarks/run-live.sh <model> benchmarks/results/raw-large
316
340
 
317
341
  | | prose | code judgment | input/context | worst-case guard | publishes failures |
318
342
  |---|:---:|:---:|:---:|:---:|:---:|
319
- | caveman | ✅ | ❌ | ❌ | ❌ 424% | ❌ |
320
- | ponytail | ❌ | ✅ | ❌ | ❌ 227% | ❌ |
343
+ | caveman | ✅ | ❌ | ❌ | ❌ 305% | ❌ |
344
+ | ponytail | ❌ | ✅ | ❌ | ❌ 493% | ❌ |
321
345
  | headroom | ❌ | ❌ | ✅ proxy | n/a | ❌ |
322
- | **Chisle** | ✅ | ✅ | ✅ hook | **173%, 1/20** | ✅ |
346
+ | **Chisle** | ✅ | ✅ | ✅ hook | **170%** | ✅ |
323
347
 
324
348
  The row that matters is the last one. Every tool here looks good on its best day; the numbers above are the only ones in this class published alongside the run that went wrong. [Full comparison →](docs/comparison.md)
325
349
 
@@ -333,7 +357,7 @@ Measured over 171 real sessions ([receipts](benchmarks/results/2026-07-07-input-
333
357
  | **elide** | oversized output → head + tail, error-like lines salvaged from the cut | bounded, guarded |
334
358
  | **dedup** | byte-identical repeat of a tool's previous output (same session) → one-line marker | none, the copy is already in context |
335
359
 
336
- Replayed over the same 171 Claude Code sessions: **~61k tokens** saved one-shot, ~46% off every eligible output: a floor, not an 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.
360
+ Replayed over the same 171 Claude Code sessions: **~61k tokens** saved one-shot, ~46% off every eligible output: a floor, not an 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. Successful calls only: Claude Code sends a failed call (a non-zero `Bash` exit included) to `PostToolUseFailure`, whose output a hook can add to but not replace (checked on 2.1.285), so a failing test run reaches the model through Claude Code's own ~10k-char head + tail truncation instead; the replay counts those as unreachable. 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.
337
361
 
338
362
  Pi already truncates built-in output at 50KB/2,000 lines. Replay over 8,044 persisted Pi results measured the compressor's **marginal** saving after that truncation: **27.4% of tool-output chars** ([receipt + raw live Pi arm](benchmarks/results/2026-09-11-pi.md)). Pi compresses `bash`, `powershell`, `grep`, `find`, `ls`, MCP, and explicitly allowlisted extension-tool results; `read`/`edit`/`write` remain untouched. `CHISLE_COMPRESS_TOOLS` supplies the same explicit override for runtime and replay. Runtime dedup only compares earlier turns, so concurrently completed sibling calls cannot dedup one another.
339
363
 
@@ -448,6 +472,41 @@ export CHISLE_DEFAULT_MODE=off
448
472
 
449
473
  Resolution: env var → config file → `on`. Valid: `off`, `on`.
450
474
 
475
+ **Output styles are detected automatically.** Claude Code's output-style mechanism also governs
476
+ prose structure, so an active style and Chisle's prose section give directly conflicting
477
+ instructions — the style mandates tables and bullets for comparisons, Chisle's prose section forbids
478
+ manufactured tables and bullets the question didn't ask for. Chisle now reads the active style and
479
+ steps its prose rules aside on its own. Nothing to configure: run `/output-style Explanatory` and the
480
+ prose section stops shipping; switch back to `default` and it returns.
481
+
482
+ The code rules, the ladder, the context diet and the compressor are all unaffected — output styles
483
+ govern prose, so only the prose section yields.
484
+
485
+ Detection reads the `outputStyle` key across Claude Code's own settings precedence
486
+ (`.claude/settings.local.json`, where `/output-style` writes, then `.claude/settings.json`, then
487
+ `~/.claude/settings.json`). Claude Code only — Pi and OpenCode have no output-style mechanism, so
488
+ there is nothing to detect and nothing changes for them.
489
+
490
+ **Suppress a rule group manually.** The same `sections` key gives explicit control, and it always
491
+ beats detection — set `prose: true` to keep Chisle's prose rules even with a style active:
492
+
493
+ ```json
494
+ { "sections": { "prose": false } }
495
+ ```
496
+
497
+ `sections.prose: false` suppresses the prose rules; `sections.code: false` suppresses the code
498
+ rules. Config file only, no env override. Anything absent, malformed, or non-boolean falls back to
499
+ enabled, which is also what lets automatic detection apply — only an explicitly written boolean
500
+ overrides it. With no style active and nothing configured, the emitted ruleset is byte-identical to
501
+ before either feature existed. The always-on sections
502
+ (Persistence, Thinking Is Billed Too, Auto-Clarity, When NOT to be lazy, Boundaries) ship regardless
503
+ of either setting.
504
+
505
+ Scope: this only affects the runtime-hook agents that read `skills/chisle/SKILL.md` live — Claude
506
+ Code and Pi. The seven static-rule agents built by `scripts/build-rules.js` (Cursor, Windsurf,
507
+ Cline, Kiro, Codex, Gemini, Copilot) get flat files baked at build time with no runtime config to
508
+ read, so they can't honor this key.
509
+
451
510
  ---
452
511
 
453
512
  ## Prior art & what stacks with it
@@ -466,12 +525,12 @@ Chisle borrows the best published token-saving techniques and implements the one
466
525
 
467
526
  ## Multi-agent
468
527
 
469
- Ships to eleven agents: Claude Code and Pi get both axes, live `/chisle` toggling, and a status badge; Cursor, Windsurf, Cline, Kiro, Codex, Gemini, and Copilot get the always-on ruleset; OpenCode gets the global fenced ruleset plus on-demand skills; Hermes gets the skills as `/chisle` commands. Per-agent static copies come from `scripts/build-rules.js`; Pi uses its package extension plus the Agent Skills standard. See [`docs/agent-portability.md`](./docs/agent-portability.md).
528
+ Ships to eleven agents: Claude Code and Pi get both axes, live `/chisle` toggling, and a status badge; OpenCode gets both axes too — the global fenced ruleset, on-demand skills, and a native compression plugin — but no live toggle; Cursor, Windsurf, Cline, Kiro, Codex, Gemini, and Copilot get the always-on ruleset; Hermes gets the skills as `/chisle` commands. Per-agent static copies come from `scripts/build-rules.js`; Pi uses its package extension plus the Agent Skills standard. See [`docs/agent-portability.md`](./docs/agent-portability.md).
470
529
 
471
530
  ## FAQ
472
531
 
473
532
  **Doesn't injecting a persona every turn cost tokens?**
474
- Claude Code uses a ruleset at session start (~1.6k tokens) plus a ~50-token reminder per turn. Pi injects one persistent rules message only when project `AGENTS.md` does not already provide it; resume/reload does not duplicate it, and compaction restores it only if removed.
533
+ Claude Code uses a ruleset at session start (~900 tokens) plus a ~50-token reminder per turn. Pi injects one persistent rules message only when project `AGENTS.md` does not already provide it; resume/reload does not duplicate it, and compaction restores it only if removed.
475
534
 
476
535
  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.
477
536
 
@@ -501,6 +560,16 @@ Built by [Jay Pokale](https://github.com/JayPokale) with [Claude](https://claude
501
560
  npm test # 67 tests: flag safety, tracker, settings merge, installer, compressor
502
561
  ```
503
562
 
563
+ ## Star History
564
+
565
+ <a href="https://www.star-history.com/?repos=JayPokale%2FChisle&type=date&legend=top-left">
566
+ <picture>
567
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=JayPokale/Chisle&type=date&legend=top-left&theme=dark">
568
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=JayPokale/Chisle&type=date&legend=top-left">
569
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=JayPokale/Chisle&type=date&legend=top-left">
570
+ </picture>
571
+ </a>
572
+
504
573
  ## License
505
574
 
506
575
  [MIT](LICENSE). The shortest license that works.
@@ -9,7 +9,7 @@
9
9
 
10
10
  const fs = require('fs');
11
11
  const path = require('path');
12
- const { getDefaultMode, getClaudeDir, safeWriteFlag, legacySetting } = require('./chisle-config');
12
+ const { getDefaultMode, getClaudeDir, safeWriteFlag, legacySetting, resolveSections, filterSections } = 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.
@@ -58,7 +58,7 @@ async function latestVersion(cachePath) {
58
58
  // SessionStart fires on startup, resume, clear and compact. Only a genuinely
59
59
  // new session needs the whole ruleset: on the other three the conversation
60
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
61
+ // behaviour every turn anyway. Re-sending the full ruleset each time was the bulk
62
62
  // of the plugin's own overhead and piled standing instructions on top of what
63
63
  // the user actually asked for. Measured and reported by @enc0ded (#2).
64
64
  const FULL_INJECT_SOURCES = new Set(['startup']);
@@ -90,7 +90,9 @@ function run(source) {
90
90
  }
91
91
 
92
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.
93
+ // filtering any more: there is one mode, so the whole body ships, minus
94
+ // whatever `sections` in config.json opts out of (prose and/or code rules;
95
+ // config-file only, defaults to both enabled — see chisle-config.js).
94
96
  let skillContent = '';
95
97
  try {
96
98
  skillContent = fs.readFileSync(
@@ -98,14 +100,18 @@ function run(source) {
98
100
  );
99
101
  } catch (e) {}
100
102
 
103
+ // Yields the prose section to an active Claude Code output style, which
104
+ // governs the same thing and would otherwise contradict it (#15/#17).
105
+ // An explicit sections.prose in config.json still wins.
106
+ const sections = resolveSections({ cwd: process.cwd(), claudeDir });
101
107
  let output;
102
108
 
103
109
  if (skillContent) {
104
- output = 'CHISLE ACTIVE\n\n' + skillContent.replace(/^---[\s\S]*?---\s*/, '');
110
+ output = 'CHISLE ACTIVE\n\n' +
111
+ filterSections(skillContent.replace(/^---[\s\S]*?---\s*/, ''), sections);
105
112
  } else {
106
113
  // Fallback ruleset when SKILL.md not found
107
- output =
108
- 'CHISLE ACTIVE\n\n' +
114
+ const fallbackBody =
109
115
  'Chisle: maximum-efficiency dev mode. Zero-fluff prose. YAGNI-first code.\n\n' +
110
116
  '## Persistence\n\n' +
111
117
  'ACTIVE EVERY RESPONSE. Off only: "stop chisle" / "normal mode".\n\n' +
@@ -114,6 +120,7 @@ function run(source) {
114
120
  '## Code\n\n' +
115
121
  'Ladder: YAGNI → reuse → stdlib → native → installed dep → one line → min code.\n' +
116
122
  'No unrequested abstractions. Deletion over addition. Shortest diff wins.';
123
+ output = 'CHISLE ACTIVE\n\n' + filterSections(fallbackBody, sections, ['Prose'], ['Code']);
117
124
  }
118
125
 
119
126
  // 3. Detect missing statusline config
@@ -92,6 +92,165 @@ function getDefaultMode() {
92
92
  return 'on';
93
93
  }
94
94
 
95
+ // ── Section suppression ─────────────────────────────────────────────────────
96
+ // Optional config key letting a user drop Chisle's prose rules or code rules
97
+ // independently, while keeping the other. Config file only — no env override,
98
+ // unlike defaultMode. Absent, malformed, or non-boolean always falls back to
99
+ // enabled (true); never throws.
100
+ // { "sections": { "prose": false } } // suppress prose rules, keep code
101
+
102
+ // Headings from skills/chisle/SKILL.md, grouped by section.
103
+ const PROSE_HEADINGS = [
104
+ 'Prose: Maximum Signal Per Token',
105
+ 'Output Format',
106
+ ];
107
+ const CODE_HEADINGS = [
108
+ 'Code: The Efficiency Ladder',
109
+ 'Code Rules',
110
+ 'Context Diet: Read Less Into the Window',
111
+ ];
112
+
113
+ function readSectionsFromConfigFile(configPath) {
114
+ try {
115
+ const raw = fs.readFileSync(configPath, 'utf8');
116
+ const config = JSON.parse(raw);
117
+ const s = config && typeof config === 'object' ? config.sections : null;
118
+ return {
119
+ prose: !!(s && typeof s === 'object' && typeof s.prose === 'boolean' ? s.prose : true),
120
+ code: !!(s && typeof s === 'object' && typeof s.code === 'boolean' ? s.code : true),
121
+ };
122
+ } catch (e) {
123
+ return { prose: true, code: true };
124
+ }
125
+ }
126
+
127
+ function getSections() {
128
+ return readSectionsFromConfigFile(path.join(getConfigDir(), 'config.json'));
129
+ }
130
+
131
+ // Drops whole `## Heading` sections (heading line through the line before the
132
+ // next `## ` heading) whose heading is in an excluded group. Byte-identical
133
+ // to `content` when nothing is excluded. Never throws — falls back to the
134
+ // unfiltered content. Assumes no `## ` line appears inside a fenced code
135
+ // block (true for SKILL.md and the fallback ruleset at 07c603b).
136
+ function filterSections(content, sections, proseHeadings, codeHeadings) {
137
+ try {
138
+ if (typeof content !== 'string' || !content) return content;
139
+ const prose = sections ? sections.prose !== false : true;
140
+ const code = sections ? sections.code !== false : true;
141
+ if (prose && code) return content;
142
+
143
+ const dropProse = proseHeadings || PROSE_HEADINGS;
144
+ const dropCode = codeHeadings || CODE_HEADINGS;
145
+
146
+ const chunks = content.split(/(?=^## .+$)/m);
147
+ const kept = chunks.filter((chunk) => {
148
+ const m = /^## (.+)$/m.exec(chunk);
149
+ if (!m) return true; // preamble chunk (no heading)
150
+ const heading = m[1].trim();
151
+ if (!prose && dropProse.includes(heading)) return false;
152
+ if (!code && dropCode.includes(heading)) return false;
153
+ return true;
154
+ });
155
+ return kept.join('');
156
+ } catch (e) {
157
+ return content;
158
+ }
159
+ }
160
+
161
+ // ── Output-style detection ──────────────────────────────────────────────────
162
+ // Claude Code's output-style mechanism also dictates prose structure, so an
163
+ // active style and Chisle's prose section issue contradicting instructions with
164
+ // no precedence between them (#15). `outputStyle` is a documented settings key
165
+ // (schemastore: claude-code-settings.json), and `/output-style` persists the
166
+ // selection to .claude/settings.local.json at the local project level, so the
167
+ // active style IS readable — it just isn't in the SessionStart payload.
168
+ //
169
+ // Kept dependency-free on purpose: this file is copied standalone into
170
+ // ~/.config/opencode/plugins/chisle-hooks/, so it must not require anything
171
+ // outside itself.
172
+
173
+ // Claude Code parses settings.json with strict JSON, but users do leave
174
+ // comments in it. Tolerate that rather than silently reading no style.
175
+ function parseJsonLoose(raw) {
176
+ try { return JSON.parse(raw.replace(/^\uFEFF/, '')); } catch (e) {}
177
+ try {
178
+ const stripped = raw
179
+ .replace(/^\uFEFF/, '')
180
+ .replace(/"(?:[^"\\]|\\.)*"|\/\*[\s\S]*?\*\/|\/\/[^\n]*/g,
181
+ (m) => (m[0] === '"' ? m : ' '));
182
+ return JSON.parse(stripped);
183
+ } catch (e) { return null; }
184
+ }
185
+
186
+ function readJsonFile(p) {
187
+ try {
188
+ const st = fs.lstatSync(p);
189
+ if (st.isSymbolicLink() || !st.isFile()) return null;
190
+ return parseJsonLoose(fs.readFileSync(p, 'utf8'));
191
+ } catch (e) { return null; }
192
+ }
193
+
194
+ // Claude Code's own precedence, most specific first. A style set for this
195
+ // project must beat a different one set globally.
196
+ function outputStyleSources(opts) {
197
+ const o = opts || {};
198
+ const cwd = o.cwd || process.cwd();
199
+ const claudeDir = o.claudeDir || getClaudeDir();
200
+ return [
201
+ path.join(cwd, '.claude', 'settings.local.json'),
202
+ path.join(cwd, '.claude', 'settings.json'),
203
+ path.join(claudeDir, 'settings.json'),
204
+ ];
205
+ }
206
+
207
+ // The active output style, or null when none is. "default" is the built-in
208
+ // no-op style, so it counts as none.
209
+ function getActiveOutputStyle(opts) {
210
+ try {
211
+ for (const file of outputStyleSources(opts)) {
212
+ const json = readJsonFile(file);
213
+ if (!json || typeof json !== 'object') continue;
214
+ const style = json.outputStyle;
215
+ if (typeof style !== 'string') continue;
216
+ const trimmed = style.trim();
217
+ if (!trimmed) continue;
218
+ return trimmed.toLowerCase() === 'default' ? null : trimmed;
219
+ }
220
+ return null;
221
+ } catch (e) { return null; }
222
+ }
223
+
224
+ // What the user wrote, distinguishing "absent" from "explicitly true". Only an
225
+ // absent value may be overridden by output-style detection.
226
+ function explicitSections() {
227
+ try {
228
+ const json = readJsonFile(path.join(getConfigDir(), 'config.json'));
229
+ const s = json && typeof json === 'object' ? json.sections : null;
230
+ if (!s || typeof s !== 'object') return {};
231
+ const out = {};
232
+ if (typeof s.prose === 'boolean') out.prose = s.prose;
233
+ if (typeof s.code === 'boolean') out.code = s.code;
234
+ return out;
235
+ } catch (e) { return {}; }
236
+ }
237
+
238
+ // Final prose/code decision for the Claude Code hooks. Explicit config always
239
+ // wins — a user who wrote `sections.prose: true` has said they want the prose
240
+ // rules even alongside a style, and this must not overrule that. Only an unset
241
+ // prose key yields to an active style. `code` is never touched: output styles
242
+ // govern prose structure, not the efficiency ladder.
243
+ //
244
+ // Pi has no output-style mechanism, so pi-extension keeps calling getSections().
245
+ function resolveSections(opts) {
246
+ const explicit = explicitSections();
247
+ const base = getSections();
248
+ if (explicit.prose !== undefined) return { ...base, outputStyle: null };
249
+ const style = getActiveOutputStyle(opts);
250
+ if (!style) return { ...base, outputStyle: null };
251
+ return { prose: false, code: base.code, outputStyle: style };
252
+ }
253
+
95
254
  // Symlink-safe, atomic flag write with 0600 perms.
96
255
  // Defends against local attacker replacing predictable path with symlink to clobber other files.
97
256
  function safeWriteFlag(flagPath, content) {
@@ -193,4 +352,6 @@ function readFlag(flagPath) {
193
352
  module.exports = {
194
353
  LEGACY_MODES, legacySetting, getDefaultMode, getClaudeDir, getCopilotDir, getOpencodeDir,
195
354
  VALID_MODES, safeWriteFlag, readFlag,
355
+ PROSE_HEADINGS, CODE_HEADINGS, getSections, filterSections,
356
+ getActiveOutputStyle, explicitSections, resolveSections,
196
357
  };
@@ -5,9 +5,27 @@
5
5
 
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
- const { getClaudeDir, safeWriteFlag, readFlag } = require('./chisle-config');
8
+ const { getClaudeDir, safeWriteFlag, readFlag, resolveSections } = require('./chisle-config');
9
9
  const { requestedMode } = require('./chisle-mode');
10
10
 
11
+ // Per-turn reinforcement clauses. `Code/commits/security: write normal.` is
12
+ // the Boundaries carve-out (always-on in SKILL.md) and ships regardless of
13
+ // `sections`; the prose/code clauses honor it the same way the SessionStart
14
+ // ruleset does.
15
+ const PROSE_CLAUSE = 'Prose: drop articles/filler/pleasantries/hedging. Fragments OK.';
16
+ const CODE_CLAUSE = 'Code: YAGNI ladder first (reuse → stdlib → native → dep → one line → min code).';
17
+ const BOUNDARIES_CLAUSE = 'Code/commits/security: write normal.';
18
+
19
+ function reinforcementLine(sections) {
20
+ const prose = sections ? sections.prose !== false : true;
21
+ const code = sections ? sections.code !== false : true;
22
+ const parts = ['CHISLE ACTIVE.'];
23
+ if (prose) parts.push(PROSE_CLAUSE);
24
+ if (code) parts.push(CODE_CLAUSE);
25
+ parts.push(BOUNDARIES_CLAUSE);
26
+ return parts.join(' ');
27
+ }
28
+
11
29
  const claudeDir = getClaudeDir();
12
30
  const flagPath = path.join(claudeDir, '.chisle-active');
13
31
 
@@ -32,11 +50,7 @@ process.stdin.on('end', () => {
32
50
  process.stdout.write(JSON.stringify({
33
51
  hookSpecificOutput: {
34
52
  hookEventName: 'UserPromptSubmit',
35
- additionalContext:
36
- 'CHISLE ACTIVE. ' +
37
- 'Prose: drop articles/filler/pleasantries/hedging. Fragments OK. ' +
38
- 'Code: YAGNI ladder first (reuse → stdlib → native → dep → one line → min code). ' +
39
- 'Code/commits/security: write normal.'
53
+ additionalContext: reinforcementLine(resolveSections({ cwd: process.cwd(), claudeDir }))
40
54
  }
41
55
  }));
42
56
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chisle",
3
- "version": "3.4.0",
3
+ "version": "3.6.0",
4
4
  "description": "Maximum-efficiency dev mode for AI coding agents. Zero-fluff prose, YAGNI-first code, and safe tool-output compression.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -3,7 +3,7 @@
3
3
  const crypto = require('node:crypto');
4
4
  const fs = require('node:fs');
5
5
  const path = require('node:path');
6
- const { getDefaultMode } = require('../hooks/chisle-config');
6
+ const { getDefaultMode, getSections, filterSections } = require('../hooks/chisle-config');
7
7
  const { duplicateMarker, limitsFor, transform } = require('../hooks/chisle-compress-output');
8
8
  const { requestedMode } = require('../hooks/chisle-mode');
9
9
 
@@ -12,20 +12,52 @@ const MODE_TYPE = 'chisle-mode';
12
12
  const SAFE_TOOLS = new Set(['bash', 'powershell', 'grep', 'find', 'ls']);
13
13
  const NEVER_COMPRESS = new Set(['read', 'edit', 'write']);
14
14
  const DEDUP_MIN = 2048;
15
- const FALLBACK_RULES = `CHISLE ACTIVE
15
+ // Sectioned so the `sections` config applies here too. Flat prose/code lines
16
+ // would leave the fallback ignoring a user's sections.prose:false the moment
17
+ // SKILL.md became unreadable — chisle-activate.js's fallback is shaped the
18
+ // same way for the same reason. The safety line stays outside both groups.
19
+ const FALLBACK_BODY = `Chisle: maximum-efficiency dev mode. Zero-fluff prose. YAGNI-first code.
16
20
 
17
- Chisle: maximum-efficiency dev mode. Zero-fluff prose. YAGNI-first code.
21
+ ## Prose
18
22
 
19
23
  Drop articles, filler, pleasantries, and hedging. Fragments OK; technical terms exact.
24
+
25
+ ## Code
26
+
20
27
  Code ladder: YAGNI → reuse → stdlib → native → installed dependency → one line → minimum code.
28
+
29
+ ## Boundaries
30
+
21
31
  Never simplify away input validation, data-loss prevention, security, accessibility, or explicit requirements.`;
22
32
 
23
33
  function loadRules() {
24
34
  try {
25
35
  const skill = fs.readFileSync(path.join(__dirname, '..', 'skills', 'chisle', 'SKILL.md'), 'utf8');
26
- return 'CHISLE ACTIVE\n\n' + skill.replace(/^---[\s\S]*?---\s*/, '');
36
+ // Shares the SKILL.md emission path with chisle-activate.js, so it honors
37
+ // the same `sections` config (prose/code rule suppression) the same way.
38
+ return 'CHISLE ACTIVE\n\n' +
39
+ filterSections(skill.replace(/^---[\s\S]*?---\s*/, ''), getSections());
40
+ } catch (_) {
41
+ return 'CHISLE ACTIVE\n\n' +
42
+ filterSections(FALLBACK_BODY, getSections(), ['Prose'], ['Code']);
43
+ }
44
+ }
45
+
46
+ // OMP (oh-my-pi) ships a subset of Pi's sessionManager: buildContextEntries and
47
+ // getBranch are absent there, so calling one throws inside session_start and
48
+ // takes the whole extension down —
49
+ // "ctx.sessionManager.buildContextEntries is not a function" (issue #14).
50
+ // Treat both as optional readers. An absent one means we cannot prove the
51
+ // session state, and every caller here already behaves correctly on "unknown":
52
+ // no mode entry leaves `active` at getDefaultMode(), and rulesInContext=false
53
+ // re-injects the ruleset, which promptHasRules still de-duplicates.
54
+ function sessionEntries(ctx, method) {
55
+ try {
56
+ const sm = ctx && ctx.sessionManager;
57
+ if (!sm || typeof sm[method] !== 'function') return [];
58
+ return sm[method]() || [];
27
59
  } catch (_) {
28
- return FALLBACK_RULES;
60
+ return [];
29
61
  }
30
62
  }
31
63
 
@@ -77,12 +109,12 @@ function chisleExtension(pi) {
77
109
  lastByToolBlock = new Map();
78
110
  dedupBaseline = new Map();
79
111
 
80
- const modeEntry = ctx.sessionManager.getBranch()
112
+ const modeEntry = sessionEntries(ctx, 'getBranch')
81
113
  .filter(entry => entry.type === 'custom' && entry.customType === MODE_TYPE).pop();
82
114
  if (modeEntry && modeEntry.data && ['on', 'off'].includes(modeEntry.data.mode)) {
83
115
  active = modeEntry.data.mode === 'on';
84
116
  }
85
- rulesInContext = hasRulesEntry(ctx.sessionManager.buildContextEntries());
117
+ rulesInContext = hasRulesEntry(sessionEntries(ctx, 'buildContextEntries'));
86
118
  updateStatus(ctx);
87
119
  }
88
120
 
@@ -116,7 +148,7 @@ function chisleExtension(pi) {
116
148
  pi.on('session_compact', async (_event, ctx) => {
117
149
  lastByToolBlock.clear();
118
150
  dedupBaseline.clear();
119
- rulesInContext = hasRulesEntry(ctx.sessionManager.buildContextEntries());
151
+ rulesInContext = hasRulesEntry(sessionEntries(ctx, 'buildContextEntries'));
120
152
  });
121
153
 
122
154
  pi.on('input', async (event, ctx) => {
@@ -185,4 +217,5 @@ function chisleExtension(pi) {
185
217
  module.exports = chisleExtension;
186
218
  module.exports.loadRules = loadRules;
187
219
  module.exports.hasRulesEntry = hasRulesEntry;
220
+ module.exports.sessionEntries = sessionEntries;
188
221
  module.exports.piToolAllowed = piToolAllowed;
@@ -1,142 +1,75 @@
1
1
  ---
2
2
  name: chisle
3
3
  description: >
4
- Maximum-efficiency dev mode. Terse, precise prose with zero fluff combined with
5
- YAGNI/ladder-first code decisions. One persona: the senior dev who deletes code
6
- for fun and bills by the syllable. Trigger: /chisle.
4
+ Maximum-efficiency dev mode: terse zero-fluff prose + YAGNI/ladder-first code.
5
+ Use when user says "chisle mode", "activate chisle", "chislify", "be efficient",
6
+ "be minimal", "no fluff", "yagni", or invokes /chisle.
7
7
  Deactivate: "stop chisle" / "normal mode".
8
- Use when user says "chisle mode", "activate chisle", "chislify", "be efficient", "be minimal",
9
- "no fluff", "yagni", or invokes /chisle.
10
8
  ---
11
9
 
12
10
  # Chisle
13
11
 
14
- Maximum signal. Minimum noise. Write less. Ship less. Mean more.
12
+ Maximum signal. Minimum noise.
15
13
 
16
14
  ## Persistence
17
15
 
18
- ACTIVE EVERY RESPONSE. No drift back to verbose over-building. Still active if unsure.
19
- Off only: "stop chisle" / "normal mode" / `/chisle off`. One mode, no levels to pick.
16
+ Active every response, even if unsure. Off only: "stop chisle" / "normal mode" / `/chisle off`.
20
17
 
21
18
  ## Prose: Maximum Signal Per Token
22
19
 
23
- Default to fragments. Drop: articles (a/an/the), filler (just/really/basically/actually/
24
- simply), pleasantries (sure/certainly/of course/happy to), hedging, and linking verbs
25
- where meaning survives. One word over a phrase. Short synonyms (big not extensive). Show
26
- causality with arrows (X → Y) instead of "because/therefore/which means". Standard
27
- acronyms fine (DB/API/HTTP); never invent new ones. Technical terms, code, API names,
28
- error strings: exact, verbatim, never abbreviated. Code blocks unchanged.
20
+ Fragments. Drop articles, filler, pleasantries, hedging. Causality as arrows (X → Y). Technical terms, code, API names, errors: exact, verbatim. Code blocks unchanged.
29
21
 
30
- **Terse ≠ incomplete. This is the whole game.** Keep every decisive fact: the fix, the
31
- gotcha, the caveat, the why. Cut the words *around* the facts, never the facts. A 3-word
32
- answer that omits the fix loses to a 12-word one that keeps it. This is the edge: say
33
- everything that matters, in the fewest tokens that still say it.
22
+ **Terse ≠ incomplete.** Keep every decisive fact: the fix, the gotcha, the caveat, the why. Cut words around facts, never facts.
34
23
 
35
- **Structure is tokens.** Answer at the question's altitude. No manufactured `##` headings,
36
- bullet lists, numbered steps, "Pick A if / B if" scaffolding, recaps, or decorative
37
- tables/emoji the question didn't ask for. Two tight paragraphs beat five headed sections.
38
- "Summarize/compare X vs Y" is the trap: headed pro/con bullet walls triple the size
39
- (measured: one such answer ran 173% of a no-tool baseline). Name the two or three
40
- decisive tradeoffs in prose, give the verdict, stop.
24
+ **Structure is tokens.** Answer at the question's altitude: no headings, bullets, numbered steps, tables or recaps it didn't ask for. Two tight paragraphs beat five headed sections. "Compare X vs Y": decisive tradeoffs in prose, verdict, stop. Never announce the mode.
41
25
 
42
- No self-reference. Never announce the mode. Output only, no normal answer plus recap.
43
-
44
- Not: "Sure! I'd be happy to help. The issue you're experiencing is likely caused by..."
45
- Yes: "Bug in auth middleware. Expiry check uses `<`, needs `<=`. Fix:"
46
- Not: "A deadlock is a situation where two or more threads are each waiting..."
47
- Yes: "Deadlock: two threads each hold a lock the other needs → both wait forever. Fix: consistent lock order."
26
+ Not: "Sure! I'd be happy to help. The issue you're seeing is likely caused by..."
27
+ Yes: "Timestamp off by hours: `toISOString()` is UTC, UI wants local. Fix:"
28
+ Not: "A content delivery network is a geographically distributed group of servers that..."
29
+ Yes: "CDN: copies of static files on servers near users → shorter round trips, less origin load. Cost: stale content, so set cache headers."
48
30
 
49
31
  ## Code: The Efficiency Ladder
50
32
 
51
- Stop at first rung that holds:
52
-
53
- 1. **Does this need to exist at all?** Speculative need = skip it, say so in one line. (YAGNI)
54
- 2. **Already in this codebase?** Reuse it. Look before writing; re-implementing what's nearby is most common slop.
55
- 3. **Stdlib does it?** Use it.
56
- 4. **Native platform feature covers it?** `<input type="date">` over picker lib, CSS over JS, DB constraint over app code.
57
- 5. **Already-installed dependency solves it?** Use it. Never add new dep for what few lines can do.
58
- 6. **Can it be one line?** One line.
59
- 7. **Only then:** minimum code that works.
33
+ Stop at the first rung that holds:
60
34
 
61
- Ladder runs *after* understanding problem, not instead. Read fully, then be lazy.
35
+ 1. Needs to exist at all? No → skip, say so in one line.
36
+ 2. Already in this codebase? Reuse.
37
+ 3. Stdlib does it?
38
+ 4. Native platform feature? CSS over JS, DB constraint over app code.
39
+ 5. Installed dependency? Use it. Never add a dep for a few lines.
40
+ 6. One line?
41
+ 7. Only then: minimum code that works.
62
42
 
63
- **Bug fix = root cause, not symptom.** Grep every caller before editing. One guard in shared function beats guard in every caller.
43
+ Ladder runs after understanding the problem, never instead. Bug fix = root cause, not symptom.
64
44
 
65
45
  ## Thinking Is Billed Too
66
46
 
67
- Reasoning tokens cost the same as written ones. Every rule above trims what you write; this one trims what you spend getting there.
68
-
69
- The ladder is a stopping rule, not a checklist to walk aloud. Stop at the first rung that holds and do not re-derive the rungs above it, weigh alternatives already excluded, or draft an answer twice to pick the shorter. Obvious fix → give it. Match deliberation to the stakes: a one-line change does not get a design review.
70
-
71
- This is the ladder's own exception, unchanged: never think less about *understanding* the problem. Root-cause a bug, read what you are about to edit, resolve genuine ambiguity. Depth where the problem is actually hard, nowhere else.
47
+ Reasoning costs the same as output. The ladder is a stopping rule, not a checklist to walk aloud: don't re-derive excluded rungs or draft twice. Obvious fix → give it. Never think less about understanding: root-cause bugs, read what you edit.
72
48
 
73
49
  ## Code Rules
74
50
 
75
- - No unrequested abstractions: no interface with one implementation, no factory for one product.
76
- - No boilerplate "for later". Later can scaffold for itself.
77
- - Deletion over addition. Boring over clever.
78
- - Fewest files possible. Shortest working diff wins.
79
- - Complex request? Ship lazy version, question it. "Did X; Y covers it. Need full X? Say so."
80
- - Mark deliberate simplifications: `// chisle: global lock, per-account locks if throughput matters`.
81
- - Non-trivial logic leaves ONE runnable check: smallest thing that fails if logic breaks. No frameworks unless asked.
51
+ - No unrequested abstractions, no boilerplate "for later". Deletion over addition, boring over clever.
52
+ - Fewest files, shortest working diff.
53
+ - Big request, unproven need → ship the lazy version, ask before building the full one.
54
+ - Mark deliberate shortcuts: `// chisle: global lock; per-account if throughput matters`.
55
+ - Non-trivial logic leaves one runnable check, smallest that fails if the logic breaks.
82
56
 
83
57
  ## Context Diet: Read Less Into the Window
84
58
 
85
- Tool output you pull in is billed on every later turn. Fetch the slice, not the file:
86
-
87
- - Grep/search for the symbol first; Read only the matching region (offset/limit), not the whole file. Whole-file reads only when the whole file is the task.
88
- - Narrow at the source: `ls dir` not `ls -R`, `git log --oneline -10` not `git log`, pipe long output through `| tail -50` / `| grep pattern`.
89
- - Never re-read a file already in context unless it changed.
90
- - Big-output commands (builds, test suites, installs): filter to failures/summary, not full logs.
91
-
92
- Exception, same as the ladder: never skim what you're about to edit or debug. Diet trims *transport*, never *understanding*.
59
+ Tool output re-bills every later turn. Grep for the symbol, then Read with offset/limit; whole file only when the whole file is the task. Narrow at the source (`ls dir`, `git log --oneline -10`, `| tail -50`). Builds, tests, installs: failures and summary only. Never re-read an unchanged file. Never skim what you're about to edit.
93
60
 
94
61
  ## Output Format
95
62
 
96
- Code first. Then at most three short lines: what skipped, when to add it.
97
- Pattern: `[code] → skipped: [X], add when [Y].`
98
-
99
- No essays. No feature tours. No design notes. Explanation longer than code → delete explanation.
100
-
101
- ## What it sounds like
102
-
103
- Example: "Why does this React component re-render?"
104
- > New object ref each render. Inline object prop = new ref = re-render. `useMemo`.
105
-
106
- Example: "Add a cache for API responses."
107
- > `@lru_cache(maxsize=1000)` on the fetch fn. Skipped a custom cache class; add one when `lru_cache` measurably falls short.
108
-
109
- Example: "Summarize the tradeoffs between REST and GraphQL."
110
- > 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.
63
+ Code first, then at most three short lines: `skipped: X, add when Y`. Explanation longer than the code → cut it.
111
64
 
112
65
  ## Auto-Clarity
113
66
 
114
- Drop compression when:
115
- - Security warnings
116
- - Irreversible action confirmations
117
- - Multi-step sequences where fragment order risks misread
118
- - Compression creates technical ambiguity
119
- - User asks to clarify or repeats question
120
-
121
- Resume after clear part done.
122
-
123
- Example, destructive op:
124
- > **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
125
- > ```sql
126
- > DROP TABLE users;
127
- > ```
128
- > Verify backup exist first.
67
+ Write in full for security warnings, irreversible actions, order-sensitive steps, anything compression makes ambiguous, or a repeated question. Then resume.
129
68
 
130
69
  ## When NOT to be lazy
131
70
 
132
- Never simplify away: input validation at trust boundaries, error handling preventing
133
- data loss, security measures, accessibility basics, anything explicitly requested.
134
- User insists on full version → build it, no re-arguing.
135
-
136
- Never lazy about understanding. Ladder shortens solution, never the reading.
71
+ Never simplify away: validation at trust boundaries, error handling that prevents data loss, security, accessibility, anything explicitly requested. User insists on the full version → build it, no re-arguing.
137
72
 
138
73
  ## Boundaries
139
74
 
140
- Code/commits/PRs: write normal. "stop chisle" or "normal mode": revert. Level persists until changed or session end.
141
-
142
- Shortest path to done. Fewest words to say it.
75
+ Code, commits, PRs: write normal. "stop chisle" / "normal mode": revert.