chisle 2.0.0 → 3.1.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 +180 -42
- package/bin/install.js +92 -40
- package/commands/chisle.toml +2 -3
- package/hooks/chisle-activate.js +57 -28
- package/hooks/chisle-compress-output.js +69 -25
- package/hooks/chisle-config.js +29 -4
- package/hooks/chisle-mode-tracker.js +8 -50
- package/hooks/chisle-mode.js +19 -0
- package/hooks/chisle-statusline.ps1 +4 -9
- package/hooks/chisle-statusline.sh +3 -8
- package/package.json +16 -7
- package/pi-extension/index.js +188 -0
- package/rules/chisle-activate.md +1 -1
- package/skills/chisle/SKILL.md +10 -18
- package/skills/chisle-help/SKILL.md +3 -12
|
@@ -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
|
@@ -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
|