chisle 3.4.0 → 3.5.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/README.md +35 -0
- package/hooks/chisle-activate.js +12 -5
- package/hooks/chisle-config.js +162 -0
- package/hooks/chisle-mode-tracker.js +20 -6
- package/package.json +1 -1
- package/pi-extension/index.js +41 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chisle",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.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/README.md
CHANGED
|
@@ -448,6 +448,41 @@ export CHISLE_DEFAULT_MODE=off
|
|
|
448
448
|
|
|
449
449
|
Resolution: env var → config file → `on`. Valid: `off`, `on`.
|
|
450
450
|
|
|
451
|
+
**Output styles are detected automatically.** Claude Code's output-style mechanism also governs
|
|
452
|
+
prose structure, so an active style and Chisle's prose section give directly conflicting
|
|
453
|
+
instructions — the style mandates tables and bullets for comparisons, Chisle's prose section forbids
|
|
454
|
+
manufactured tables and bullets the question didn't ask for. Chisle now reads the active style and
|
|
455
|
+
steps its prose rules aside on its own. Nothing to configure: run `/output-style Explanatory` and the
|
|
456
|
+
prose section stops shipping; switch back to `default` and it returns.
|
|
457
|
+
|
|
458
|
+
The code rules, the ladder, the context diet and the compressor are all unaffected — output styles
|
|
459
|
+
govern prose, so only the prose section yields.
|
|
460
|
+
|
|
461
|
+
Detection reads the `outputStyle` key across Claude Code's own settings precedence
|
|
462
|
+
(`.claude/settings.local.json`, where `/output-style` writes, then `.claude/settings.json`, then
|
|
463
|
+
`~/.claude/settings.json`). Claude Code and Pi only; Pi has no output-style mechanism, so nothing
|
|
464
|
+
changes there.
|
|
465
|
+
|
|
466
|
+
**Suppress a rule group manually.** The same `sections` key gives explicit control, and it always
|
|
467
|
+
beats detection — set `prose: true` to keep Chisle's prose rules even with a style active:
|
|
468
|
+
|
|
469
|
+
```json
|
|
470
|
+
{ "sections": { "prose": false } }
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
`sections.prose: false` suppresses the prose rules; `sections.code: false` suppresses the code
|
|
474
|
+
rules. Config file only, no env override. Anything absent, malformed, or non-boolean falls back to
|
|
475
|
+
enabled, which is also what lets automatic detection apply — only an explicitly written boolean
|
|
476
|
+
overrides it. With no style active and nothing configured, the emitted ruleset is byte-identical to
|
|
477
|
+
before either feature existed. The always-on sections
|
|
478
|
+
(Persistence, Thinking Is Billed Too, Auto-Clarity, When NOT to be lazy, Boundaries) ship regardless
|
|
479
|
+
of either setting.
|
|
480
|
+
|
|
481
|
+
Scope: this only affects the runtime-hook agents that read `skills/chisle/SKILL.md` live — Claude
|
|
482
|
+
Code and Pi. The seven static-rule agents built by `scripts/build-rules.js` (Cursor, Windsurf,
|
|
483
|
+
Cline, Kiro, Codex, Gemini, Copilot) get flat files baked at build time with no runtime config to
|
|
484
|
+
read, so they can't honor this key.
|
|
485
|
+
|
|
451
486
|
---
|
|
452
487
|
|
|
453
488
|
## Prior art & what stacks with it
|
package/hooks/chisle-activate.js
CHANGED
|
@@ -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.
|
|
@@ -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' +
|
|
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
|
-
|
|
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
|
package/hooks/chisle-config.js
CHANGED
|
@@ -92,6 +92,166 @@ 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
|
+
'What it sounds like',
|
|
107
|
+
];
|
|
108
|
+
const CODE_HEADINGS = [
|
|
109
|
+
'Code: The Efficiency Ladder',
|
|
110
|
+
'Code Rules',
|
|
111
|
+
'Context Diet: Read Less Into the Window',
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
function readSectionsFromConfigFile(configPath) {
|
|
115
|
+
try {
|
|
116
|
+
const raw = fs.readFileSync(configPath, 'utf8');
|
|
117
|
+
const config = JSON.parse(raw);
|
|
118
|
+
const s = config && typeof config === 'object' ? config.sections : null;
|
|
119
|
+
return {
|
|
120
|
+
prose: !!(s && typeof s === 'object' && typeof s.prose === 'boolean' ? s.prose : true),
|
|
121
|
+
code: !!(s && typeof s === 'object' && typeof s.code === 'boolean' ? s.code : true),
|
|
122
|
+
};
|
|
123
|
+
} catch (e) {
|
|
124
|
+
return { prose: true, code: true };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getSections() {
|
|
129
|
+
return readSectionsFromConfigFile(path.join(getConfigDir(), 'config.json'));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Drops whole `## Heading` sections (heading line through the line before the
|
|
133
|
+
// next `## ` heading) whose heading is in an excluded group. Byte-identical
|
|
134
|
+
// to `content` when nothing is excluded. Never throws — falls back to the
|
|
135
|
+
// unfiltered content. Assumes no `## ` line appears inside a fenced code
|
|
136
|
+
// block (true for SKILL.md and the fallback ruleset at 07c603b).
|
|
137
|
+
function filterSections(content, sections, proseHeadings, codeHeadings) {
|
|
138
|
+
try {
|
|
139
|
+
if (typeof content !== 'string' || !content) return content;
|
|
140
|
+
const prose = sections ? sections.prose !== false : true;
|
|
141
|
+
const code = sections ? sections.code !== false : true;
|
|
142
|
+
if (prose && code) return content;
|
|
143
|
+
|
|
144
|
+
const dropProse = proseHeadings || PROSE_HEADINGS;
|
|
145
|
+
const dropCode = codeHeadings || CODE_HEADINGS;
|
|
146
|
+
|
|
147
|
+
const chunks = content.split(/(?=^## .+$)/m);
|
|
148
|
+
const kept = chunks.filter((chunk) => {
|
|
149
|
+
const m = /^## (.+)$/m.exec(chunk);
|
|
150
|
+
if (!m) return true; // preamble chunk (no heading)
|
|
151
|
+
const heading = m[1].trim();
|
|
152
|
+
if (!prose && dropProse.includes(heading)) return false;
|
|
153
|
+
if (!code && dropCode.includes(heading)) return false;
|
|
154
|
+
return true;
|
|
155
|
+
});
|
|
156
|
+
return kept.join('');
|
|
157
|
+
} catch (e) {
|
|
158
|
+
return content;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ── Output-style detection ──────────────────────────────────────────────────
|
|
163
|
+
// Claude Code's output-style mechanism also dictates prose structure, so an
|
|
164
|
+
// active style and Chisle's prose section issue contradicting instructions with
|
|
165
|
+
// no precedence between them (#15). `outputStyle` is a documented settings key
|
|
166
|
+
// (schemastore: claude-code-settings.json), and `/output-style` persists the
|
|
167
|
+
// selection to .claude/settings.local.json at the local project level, so the
|
|
168
|
+
// active style IS readable — it just isn't in the SessionStart payload.
|
|
169
|
+
//
|
|
170
|
+
// Kept dependency-free on purpose: this file is copied standalone into
|
|
171
|
+
// ~/.config/opencode/plugins/chisle-hooks/, so it must not require anything
|
|
172
|
+
// outside itself.
|
|
173
|
+
|
|
174
|
+
// Claude Code parses settings.json with strict JSON, but users do leave
|
|
175
|
+
// comments in it. Tolerate that rather than silently reading no style.
|
|
176
|
+
function parseJsonLoose(raw) {
|
|
177
|
+
try { return JSON.parse(raw.replace(/^\uFEFF/, '')); } catch (e) {}
|
|
178
|
+
try {
|
|
179
|
+
const stripped = raw
|
|
180
|
+
.replace(/^\uFEFF/, '')
|
|
181
|
+
.replace(/"(?:[^"\\]|\\.)*"|\/\*[\s\S]*?\*\/|\/\/[^\n]*/g,
|
|
182
|
+
(m) => (m[0] === '"' ? m : ' '));
|
|
183
|
+
return JSON.parse(stripped);
|
|
184
|
+
} catch (e) { return null; }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function readJsonFile(p) {
|
|
188
|
+
try {
|
|
189
|
+
const st = fs.lstatSync(p);
|
|
190
|
+
if (st.isSymbolicLink() || !st.isFile()) return null;
|
|
191
|
+
return parseJsonLoose(fs.readFileSync(p, 'utf8'));
|
|
192
|
+
} catch (e) { return null; }
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Claude Code's own precedence, most specific first. A style set for this
|
|
196
|
+
// project must beat a different one set globally.
|
|
197
|
+
function outputStyleSources(opts) {
|
|
198
|
+
const o = opts || {};
|
|
199
|
+
const cwd = o.cwd || process.cwd();
|
|
200
|
+
const claudeDir = o.claudeDir || getClaudeDir();
|
|
201
|
+
return [
|
|
202
|
+
path.join(cwd, '.claude', 'settings.local.json'),
|
|
203
|
+
path.join(cwd, '.claude', 'settings.json'),
|
|
204
|
+
path.join(claudeDir, 'settings.json'),
|
|
205
|
+
];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// The active output style, or null when none is. "default" is the built-in
|
|
209
|
+
// no-op style, so it counts as none.
|
|
210
|
+
function getActiveOutputStyle(opts) {
|
|
211
|
+
try {
|
|
212
|
+
for (const file of outputStyleSources(opts)) {
|
|
213
|
+
const json = readJsonFile(file);
|
|
214
|
+
if (!json || typeof json !== 'object') continue;
|
|
215
|
+
const style = json.outputStyle;
|
|
216
|
+
if (typeof style !== 'string') continue;
|
|
217
|
+
const trimmed = style.trim();
|
|
218
|
+
if (!trimmed) continue;
|
|
219
|
+
return trimmed.toLowerCase() === 'default' ? null : trimmed;
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
} catch (e) { return null; }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// What the user wrote, distinguishing "absent" from "explicitly true". Only an
|
|
226
|
+
// absent value may be overridden by output-style detection.
|
|
227
|
+
function explicitSections() {
|
|
228
|
+
try {
|
|
229
|
+
const json = readJsonFile(path.join(getConfigDir(), 'config.json'));
|
|
230
|
+
const s = json && typeof json === 'object' ? json.sections : null;
|
|
231
|
+
if (!s || typeof s !== 'object') return {};
|
|
232
|
+
const out = {};
|
|
233
|
+
if (typeof s.prose === 'boolean') out.prose = s.prose;
|
|
234
|
+
if (typeof s.code === 'boolean') out.code = s.code;
|
|
235
|
+
return out;
|
|
236
|
+
} catch (e) { return {}; }
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Final prose/code decision for the Claude Code hooks. Explicit config always
|
|
240
|
+
// wins — a user who wrote `sections.prose: true` has said they want the prose
|
|
241
|
+
// rules even alongside a style, and this must not overrule that. Only an unset
|
|
242
|
+
// prose key yields to an active style. `code` is never touched: output styles
|
|
243
|
+
// govern prose structure, not the efficiency ladder.
|
|
244
|
+
//
|
|
245
|
+
// Pi has no output-style mechanism, so pi-extension keeps calling getSections().
|
|
246
|
+
function resolveSections(opts) {
|
|
247
|
+
const explicit = explicitSections();
|
|
248
|
+
const base = getSections();
|
|
249
|
+
if (explicit.prose !== undefined) return { ...base, outputStyle: null };
|
|
250
|
+
const style = getActiveOutputStyle(opts);
|
|
251
|
+
if (!style) return { ...base, outputStyle: null };
|
|
252
|
+
return { prose: false, code: base.code, outputStyle: style };
|
|
253
|
+
}
|
|
254
|
+
|
|
95
255
|
// Symlink-safe, atomic flag write with 0600 perms.
|
|
96
256
|
// Defends against local attacker replacing predictable path with symlink to clobber other files.
|
|
97
257
|
function safeWriteFlag(flagPath, content) {
|
|
@@ -193,4 +353,6 @@ function readFlag(flagPath) {
|
|
|
193
353
|
module.exports = {
|
|
194
354
|
LEGACY_MODES, legacySetting, getDefaultMode, getClaudeDir, getCopilotDir, getOpencodeDir,
|
|
195
355
|
VALID_MODES, safeWriteFlag, readFlag,
|
|
356
|
+
PROSE_HEADINGS, CODE_HEADINGS, getSections, filterSections,
|
|
357
|
+
getActiveOutputStyle, explicitSections, resolveSections,
|
|
196
358
|
};
|
|
@@ -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
package/pi-extension/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|