chisle 3.0.0 → 3.2.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 +15 -8
- package/.cursor/rules/chisle.mdc +16 -9
- package/.github/copilot-instructions.md +15 -8
- package/.kiro/steering/chisle.md +15 -8
- package/.windsurf/rules/chisle.md +15 -8
- package/AGENTS.md +12 -3
- package/README.md +111 -60
- package/bin/install.js +144 -49
- package/commands/chisle-audit.toml +1 -1
- package/commands/chisle-help.toml +1 -1
- package/commands/chisle-review.toml +1 -1
- package/hooks/chisle-compress-output.js +26 -12
- package/hooks/chisle-mode-tracker.js +6 -32
- package/hooks/chisle-mode.js +19 -0
- package/package.json +13 -6
- package/pi-extension/index.js +188 -0
- package/rules/chisle-activate.md +1 -1
- package/skills/chisle/SKILL.md +19 -11
- package/skills/chisle-audit/SKILL.md +4 -4
- package/skills/chisle-help/SKILL.md +2 -2
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const { getDefaultMode } = require('../hooks/chisle-config');
|
|
7
|
+
const { duplicateMarker, limitsFor, transform } = require('../hooks/chisle-compress-output');
|
|
8
|
+
const { requestedMode } = require('../hooks/chisle-mode');
|
|
9
|
+
|
|
10
|
+
const RULES_TYPE = 'chisle-rules';
|
|
11
|
+
const MODE_TYPE = 'chisle-mode';
|
|
12
|
+
const SAFE_TOOLS = new Set(['bash', 'powershell', 'grep', 'find', 'ls']);
|
|
13
|
+
const NEVER_COMPRESS = new Set(['read', 'edit', 'write']);
|
|
14
|
+
const DEDUP_MIN = 2048;
|
|
15
|
+
const FALLBACK_RULES = `CHISLE ACTIVE
|
|
16
|
+
|
|
17
|
+
Chisle: maximum-efficiency dev mode. Zero-fluff prose. YAGNI-first code.
|
|
18
|
+
|
|
19
|
+
Drop articles, filler, pleasantries, and hedging. Fragments OK; technical terms exact.
|
|
20
|
+
Code ladder: YAGNI → reuse → stdlib → native → installed dependency → one line → minimum code.
|
|
21
|
+
Never simplify away input validation, data-loss prevention, security, accessibility, or explicit requirements.`;
|
|
22
|
+
|
|
23
|
+
function loadRules() {
|
|
24
|
+
try {
|
|
25
|
+
const skill = fs.readFileSync(path.join(__dirname, '..', 'skills', 'chisle', 'SKILL.md'), 'utf8');
|
|
26
|
+
return 'CHISLE ACTIVE\n\n' + skill.replace(/^---[\s\S]*?---\s*/, '');
|
|
27
|
+
} catch (_) {
|
|
28
|
+
return FALLBACK_RULES;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function hasRulesEntry(entries) {
|
|
33
|
+
return (entries || []).some(entry =>
|
|
34
|
+
(entry.type === 'custom_message' && entry.customType === RULES_TYPE) ||
|
|
35
|
+
(entry.type === 'message' && entry.message && entry.message.role === 'custom' &&
|
|
36
|
+
entry.message.customType === RULES_TYPE));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function promptHasRules(event) {
|
|
40
|
+
const containsRules = text => /^# Chisle\s*$/m.test(text || '') &&
|
|
41
|
+
(text || '').includes('## Prose: zero fluff') &&
|
|
42
|
+
(text || '').includes('## Code: the efficiency ladder');
|
|
43
|
+
if (containsRules(event.systemPrompt)) return true;
|
|
44
|
+
return (event.systemPromptOptions && event.systemPromptOptions.contextFiles || [])
|
|
45
|
+
.some(file => containsRules(file.content));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function piToolAllowed(name) {
|
|
49
|
+
if (!name || NEVER_COMPRESS.has(name.toLowerCase())) return false;
|
|
50
|
+
if (process.env.CHISLE_COMPRESS_TOOLS) {
|
|
51
|
+
return process.env.CHISLE_COMPRESS_TOOLS.split(',').map(s => s.trim()).filter(Boolean).includes(name);
|
|
52
|
+
}
|
|
53
|
+
return SAFE_TOOLS.has(name.toLowerCase()) || name.startsWith('mcp__');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function chisleExtension(pi) {
|
|
57
|
+
const rules = loadRules();
|
|
58
|
+
let active = getDefaultMode() !== 'off';
|
|
59
|
+
let rulesInContext = false;
|
|
60
|
+
let savedChars = 0;
|
|
61
|
+
let lastByToolBlock = new Map();
|
|
62
|
+
let dedupBaseline = new Map();
|
|
63
|
+
const stopRulesSignal = pi.events.on('chisle:rules-injected', () => { rulesInContext = true; });
|
|
64
|
+
const stopModeSignal = pi.events.on('chisle:mode', mode => { active = mode === 'on'; });
|
|
65
|
+
|
|
66
|
+
function updateStatus(ctx) {
|
|
67
|
+
if (!ctx.hasUI) return;
|
|
68
|
+
if (!active) return ctx.ui.setStatus('chisle', undefined);
|
|
69
|
+
const tokens = Math.floor(savedChars / 4);
|
|
70
|
+
const saved = tokens >= 1000 ? ` ⇣${Math.floor(tokens / 1000)}k tok` : tokens ? ` ⇣${tokens} tok` : '';
|
|
71
|
+
ctx.ui.setStatus('chisle', ctx.ui.theme.fg('accent', `[CHISLE]${saved}`));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function restoreBranch(ctx) {
|
|
75
|
+
active = getDefaultMode() !== 'off';
|
|
76
|
+
savedChars = 0;
|
|
77
|
+
lastByToolBlock = new Map();
|
|
78
|
+
dedupBaseline = new Map();
|
|
79
|
+
|
|
80
|
+
const modeEntry = ctx.sessionManager.getBranch()
|
|
81
|
+
.filter(entry => entry.type === 'custom' && entry.customType === MODE_TYPE).pop();
|
|
82
|
+
if (modeEntry && modeEntry.data && ['on', 'off'].includes(modeEntry.data.mode)) {
|
|
83
|
+
active = modeEntry.data.mode === 'on';
|
|
84
|
+
}
|
|
85
|
+
rulesInContext = hasRulesEntry(ctx.sessionManager.buildContextEntries());
|
|
86
|
+
updateStatus(ctx);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function setMode(request, ctx) {
|
|
90
|
+
active = request === 'on';
|
|
91
|
+
pi.events.emit('chisle:mode', active ? 'on' : 'off');
|
|
92
|
+
pi.appendEntry(MODE_TYPE, { mode: active ? 'on' : 'off' });
|
|
93
|
+
updateStatus(ctx);
|
|
94
|
+
if (ctx.hasUI) ctx.ui.notify(active ? 'Chisle enabled.' : 'Chisle disabled.', 'info');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
pi.registerCommand('chisle', {
|
|
98
|
+
description: 'Enable Chisle or set /chisle on|off',
|
|
99
|
+
handler: async (args, ctx) => {
|
|
100
|
+
const request = requestedMode(`/chisle ${args || ''}`) || 'on';
|
|
101
|
+
setMode(request, ctx);
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
pi.on('session_start', async (_event, ctx) => restoreBranch(ctx));
|
|
106
|
+
pi.on('session_tree', async (_event, ctx) => restoreBranch(ctx));
|
|
107
|
+
|
|
108
|
+
pi.on('session_shutdown', async (_event, ctx) => {
|
|
109
|
+
lastByToolBlock.clear();
|
|
110
|
+
dedupBaseline.clear();
|
|
111
|
+
stopRulesSignal();
|
|
112
|
+
stopModeSignal();
|
|
113
|
+
if (ctx.hasUI) ctx.ui.setStatus('chisle', undefined);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
pi.on('session_compact', async (_event, ctx) => {
|
|
117
|
+
lastByToolBlock.clear();
|
|
118
|
+
dedupBaseline.clear();
|
|
119
|
+
rulesInContext = hasRulesEntry(ctx.sessionManager.buildContextEntries());
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
pi.on('input', async (event, ctx) => {
|
|
123
|
+
if (event.source === 'extension') return { action: 'continue' };
|
|
124
|
+
const request = requestedMode(event.text);
|
|
125
|
+
if (!request) return { action: 'continue' };
|
|
126
|
+
setMode(request, ctx);
|
|
127
|
+
return request === 'off' ? { action: 'handled' } : { action: 'continue' };
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
pi.on('before_agent_start', async event => {
|
|
131
|
+
if (!active) {
|
|
132
|
+
if (event.systemPrompt.includes('CHISLE DISABLED.')) return;
|
|
133
|
+
return { systemPrompt: event.systemPrompt + '\n\nCHISLE DISABLED. Ignore only Chisle style and efficiency-ladder instructions.' };
|
|
134
|
+
}
|
|
135
|
+
if (rulesInContext || promptHasRules(event)) return;
|
|
136
|
+
rulesInContext = true;
|
|
137
|
+
pi.events.emit('chisle:rules-injected');
|
|
138
|
+
return { message: { customType: RULES_TYPE, content: rules, display: false } };
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
pi.on('context', async event => {
|
|
142
|
+
if (active) return;
|
|
143
|
+
return { messages: event.messages.filter(message =>
|
|
144
|
+
!(message.role === 'custom' && message.customType === RULES_TYPE)) };
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
pi.on('turn_start', async () => {
|
|
148
|
+
dedupBaseline = new Map(lastByToolBlock);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
pi.on('tool_result', async (event, ctx) => {
|
|
152
|
+
try {
|
|
153
|
+
if (!active || process.env.CHISLE_COMPRESS === '0' || !piToolAllowed(event.toolName) ||
|
|
154
|
+
!Array.isArray(event.content)) return;
|
|
155
|
+
|
|
156
|
+
let changed = false;
|
|
157
|
+
const content = event.content.map((block, index) => {
|
|
158
|
+
if (!block || block.type !== 'text' || typeof block.text !== 'string') return block;
|
|
159
|
+
const text = block.text;
|
|
160
|
+
const key = `${event.toolName}:${index}`;
|
|
161
|
+
let updated = null;
|
|
162
|
+
|
|
163
|
+
if (process.env.CHISLE_COMPRESS_DEDUP !== '0' && text.length >= DEDUP_MIN) {
|
|
164
|
+
const hash = crypto.createHash('sha256').update(text).digest('hex');
|
|
165
|
+
const previous = dedupBaseline.get(key);
|
|
166
|
+
lastByToolBlock.set(key, hash);
|
|
167
|
+
if (previous === hash) updated = duplicateMarker(event.toolName, text, 'earlier');
|
|
168
|
+
}
|
|
169
|
+
if (updated == null) updated = transform(text, limitsFor());
|
|
170
|
+
if (updated == null || updated.length >= text.length) return block;
|
|
171
|
+
changed = true;
|
|
172
|
+
savedChars += text.length - updated.length;
|
|
173
|
+
return { ...block, text: updated };
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (!changed) return;
|
|
177
|
+
updateStatus(ctx);
|
|
178
|
+
return { content };
|
|
179
|
+
} catch (_) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
module.exports = chisleExtension;
|
|
186
|
+
module.exports.loadRules = loadRules;
|
|
187
|
+
module.exports.hasRulesEntry = hasRulesEntry;
|
|
188
|
+
module.exports.piToolAllowed = piToolAllowed;
|
package/rules/chisle-activate.md
CHANGED
package/skills/chisle/SKILL.md
CHANGED
|
@@ -16,7 +16,7 @@ Maximum signal. Minimum noise. Write less. Ship less. Mean more.
|
|
|
16
16
|
## Persistence
|
|
17
17
|
|
|
18
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
|
|
19
|
+
Off only: "stop chisle" / "normal mode" / `/chisle off`. One mode, no levels to pick.
|
|
20
20
|
|
|
21
21
|
## Prose: Maximum Signal Per Token
|
|
22
22
|
|
|
@@ -27,7 +27,7 @@ causality with arrows (X → Y) instead of "because/therefore/which means". Stan
|
|
|
27
27
|
acronyms fine (DB/API/HTTP); never invent new ones. Technical terms, code, API names,
|
|
28
28
|
error strings: exact, verbatim, never abbreviated. Code blocks unchanged.
|
|
29
29
|
|
|
30
|
-
**Terse ≠ incomplete
|
|
30
|
+
**Terse ≠ incomplete. This is the whole game.** Keep every decisive fact: the fix, the
|
|
31
31
|
gotcha, the caveat, the why. Cut the words *around* the facts, never the facts. A 3-word
|
|
32
32
|
answer that omits the fix loses to a 12-word one that keeps it. This is the edge: say
|
|
33
33
|
everything that matters, in the fewest tokens that still say it.
|
|
@@ -39,7 +39,7 @@ tables/emoji the question didn't ask for. Two tight paragraphs beat five headed
|
|
|
39
39
|
(measured: one such answer ran 173% of a no-tool baseline). Name the two or three
|
|
40
40
|
decisive tradeoffs in prose, give the verdict, stop.
|
|
41
41
|
|
|
42
|
-
No self-reference. Never announce the mode. Output only
|
|
42
|
+
No self-reference. Never announce the mode. Output only, no normal answer plus recap.
|
|
43
43
|
|
|
44
44
|
Not: "Sure! I'd be happy to help. The issue you're experiencing is likely caused by..."
|
|
45
45
|
Yes: "Bug in auth middleware. Expiry check uses `<`, needs `<=`. Fix:"
|
|
@@ -51,7 +51,7 @@ Yes: "Deadlock: two threads each hold a lock the other needs → both wait forev
|
|
|
51
51
|
Stop at first rung that holds:
|
|
52
52
|
|
|
53
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
|
|
54
|
+
2. **Already in this codebase?** Reuse it. Look before writing; re-implementing what's nearby is most common slop.
|
|
55
55
|
3. **Stdlib does it?** Use it.
|
|
56
56
|
4. **Native platform feature covers it?** `<input type="date">` over picker lib, CSS over JS, DB constraint over app code.
|
|
57
57
|
5. **Already-installed dependency solves it?** Use it. Never add new dep for what few lines can do.
|
|
@@ -62,6 +62,14 @@ Ladder runs *after* understanding problem, not instead. Read fully, then be lazy
|
|
|
62
62
|
|
|
63
63
|
**Bug fix = root cause, not symptom.** Grep every caller before editing. One guard in shared function beats guard in every caller.
|
|
64
64
|
|
|
65
|
+
## Thinking Is Billed Too
|
|
66
|
+
|
|
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.
|
|
72
|
+
|
|
65
73
|
## Code Rules
|
|
66
74
|
|
|
67
75
|
- No unrequested abstractions: no interface with one implementation, no factory for one product.
|
|
@@ -70,7 +78,7 @@ Ladder runs *after* understanding problem, not instead. Read fully, then be lazy
|
|
|
70
78
|
- Fewest files possible. Shortest working diff wins.
|
|
71
79
|
- Complex request? Ship lazy version, question it. "Did X; Y covers it. Need full X? Say so."
|
|
72
80
|
- Mark deliberate simplifications: `// chisle: global lock, per-account locks if throughput matters`.
|
|
73
|
-
- Non-trivial logic leaves ONE runnable check
|
|
81
|
+
- Non-trivial logic leaves ONE runnable check: smallest thing that fails if logic breaks. No frameworks unless asked.
|
|
74
82
|
|
|
75
83
|
## Context Diet: Read Less Into the Window
|
|
76
84
|
|
|
@@ -81,7 +89,7 @@ Tool output you pull in is billed on every later turn. Fetch the slice, not the
|
|
|
81
89
|
- Never re-read a file already in context unless it changed.
|
|
82
90
|
- Big-output commands (builds, test suites, installs): filter to failures/summary, not full logs.
|
|
83
91
|
|
|
84
|
-
Exception
|
|
92
|
+
Exception, same as the ladder: never skim what you're about to edit or debug. Diet trims *transport*, never *understanding*.
|
|
85
93
|
|
|
86
94
|
## Output Format
|
|
87
95
|
|
|
@@ -92,13 +100,13 @@ No essays. No feature tours. No design notes. Explanation longer than code → d
|
|
|
92
100
|
|
|
93
101
|
## What it sounds like
|
|
94
102
|
|
|
95
|
-
Example
|
|
103
|
+
Example: "Why does this React component re-render?"
|
|
96
104
|
> New object ref each render. Inline object prop = new ref = re-render. `useMemo`.
|
|
97
105
|
|
|
98
|
-
Example
|
|
99
|
-
> `@lru_cache(maxsize=1000)` on the fetch fn. Skipped a custom cache class
|
|
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.
|
|
100
108
|
|
|
101
|
-
Example
|
|
109
|
+
Example: "Summarize the tradeoffs between REST and GraphQL."
|
|
102
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.
|
|
103
111
|
|
|
104
112
|
## Auto-Clarity
|
|
@@ -112,7 +120,7 @@ Drop compression when:
|
|
|
112
120
|
|
|
113
121
|
Resume after clear part done.
|
|
114
122
|
|
|
115
|
-
Example
|
|
123
|
+
Example, destructive op:
|
|
116
124
|
> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
|
|
117
125
|
> ```sql
|
|
118
126
|
> DROP TABLE users;
|
|
@@ -5,7 +5,7 @@ description: >
|
|
|
5
5
|
once: over-engineered code (reinvented stdlib, needless abstractions,
|
|
6
6
|
speculative config) AND bloated prose (verbose comments, padded docstrings,
|
|
7
7
|
redundant doc sections). Neither a pure code-minimizer nor a pure prose
|
|
8
|
-
compressor does both in one pass
|
|
8
|
+
compressor does both in one pass. That's the point. Ranked report, biggest
|
|
9
9
|
saving first; changes nothing. Use when the user says "chisle audit", "/chisle-audit",
|
|
10
10
|
"audit this for bloat", "what can I cut", "review this PR for over-engineering
|
|
11
11
|
and verbosity".
|
|
@@ -14,7 +14,7 @@ description: >
|
|
|
14
14
|
# Chisle Audit
|
|
15
15
|
|
|
16
16
|
Scan the target (a diff, a file, or the repo tree) and report what to cut, on
|
|
17
|
-
both axes. One-shot. Read-only
|
|
17
|
+
both axes. One-shot. Read-only: never edit, never write a flag, never apply fixes.
|
|
18
18
|
|
|
19
19
|
## Scope
|
|
20
20
|
|
|
@@ -32,7 +32,7 @@ both axes. One-shot. Read-only — never edit, never write a flag, never apply f
|
|
|
32
32
|
- Speculative "for later" scaffolding with no current caller
|
|
33
33
|
- Verbose code where a native platform feature (CSS, DB constraint, `<input type>`) does it
|
|
34
34
|
|
|
35
|
-
**Prose (the compression axis)
|
|
35
|
+
**Prose (the compression axis), the half a code-only auditor misses:**
|
|
36
36
|
- Comments that restate the code (`i += 1 // increment i`)
|
|
37
37
|
- Docstrings/READMEs padded with filler, hedging, ceremony, or duplicated content
|
|
38
38
|
- Multi-paragraph explanations where one tight sentence carries the meaning
|
|
@@ -60,6 +60,6 @@ N findings: X code, Y prose. Est. removable: ~A lines code, ~B lines prose.
|
|
|
60
60
|
Biggest win: <the single highest-impact cut>.
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
Be honest about uncertainty
|
|
63
|
+
Be honest about uncertainty: mark a finding `(check)` if cutting it might lose
|
|
64
64
|
behavior you can't verify from the snippet. Lean toward fewer, high-confidence
|
|
65
65
|
findings over a long speculative list.
|
|
@@ -6,7 +6,7 @@ description: >
|
|
|
6
6
|
use chisle", "what chisle commands", or invokes /chisle-help.
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
# Chisle
|
|
9
|
+
# Chisle quick reference
|
|
10
10
|
|
|
11
11
|
**What it is:** maximum-efficiency dev mode. Zero-fluff prose + YAGNI-first code, always on together.
|
|
12
12
|
|
|
@@ -27,7 +27,7 @@ YAGNI → reuse → stdlib → native → installed dep → one line → minimum
|
|
|
27
27
|
|
|
28
28
|
## Statusline
|
|
29
29
|
|
|
30
|
-
`[CHISLE]`
|
|
30
|
+
`[CHISLE]` is shown while active. Configure via
|
|
31
31
|
`hooks/chisle-statusline.sh` (or `.ps1` on Windows).
|
|
32
32
|
|
|
33
33
|
## Never minimal about
|