@krtclcdy/tersio 2.22.0 → 2.23.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/README.md +38 -30
- package/cli/common.js +43 -39
- package/cli/currency.js +2 -2
- package/cli/dashboard.js +60 -116
- package/cli/doctor-fix.js +35 -29
- package/cli/doctor.js +26 -15
- package/cli/install.js +15 -34
- package/cli/profile.js +2 -1
- package/cli/settings.js +6 -6
- package/cli/update.js +12 -11
- package/cli/usage.js +6 -1
- package/dashboard/dist/index.html +65 -0
- package/extensions/ai-addons-updater/index.js +7 -3
- package/extensions/ai-addons-updater/index.ts +15 -3
- package/extensions/caveman-session/index.js +17 -12
- package/extensions/caveman-session/index.ts +17 -12
- package/extensions/caveman-session/rule.md +90 -0
- package/extensions/combo-toggle/index.js +22 -2
- package/extensions/combo-toggle/index.ts +26 -2
- package/extensions/lib/utils.js +20 -4
- package/extensions/lib/utils.ts +18 -4
- package/extensions/rtk-session/index.js +11 -10
- package/extensions/rtk-session/index.ts +10 -11
- package/extensions/shared/plugin-settings.js +37 -3
- package/extensions/shared/plugin-settings.ts +34 -3
- package/extensions/shared/rtk-gain.js +2 -2
- package/extensions/shared/rtk-gain.ts +3 -3
- package/extensions/shared/session-state.js +5 -3
- package/extensions/shared/session-state.ts +4 -3
- package/extensions/shared/types.ts +5 -0
- package/extensions/shared/usage-ledger.js +114 -4
- package/extensions/shared/usage-ledger.ts +122 -4
- package/extensions/tersio-commands/index.js +10 -13
- package/extensions/tersio-commands/index.ts +10 -13
- package/package.json +21 -12
- package/tersio.js +10 -10
- package/dashboard/charts.js +0 -1208
- package/dashboard/core.js +0 -357
- package/dashboard/settings.js +0 -166
- package/dashboard/share.js +0 -218
- package/dashboard/styles.css +0 -379
- package/dashboard/template.html +0 -515
- package/extensions/shared/mode-reinforcement.ts +0 -29
- package/gain/dist/brand.webp +0 -0
- package/gain/dist/index.html +0 -65
- package/gain/dist/vite.svg +0 -1
- /package/dashboard/{brand.webp → dist/brand.webp} +0 -0
|
@@ -8,13 +8,13 @@ import { createHash } from 'node:crypto';
|
|
|
8
8
|
import fs from 'node:fs/promises';
|
|
9
9
|
import os from 'node:os';
|
|
10
10
|
import path from 'node:path';
|
|
11
|
-
import { CAVEMAN_REMOTE_RULE as CAVEMAN_REMOTE, RTK_RELEASE_API, fetchJson, findFile, httpsGet, httpsDownload, sha256Hex, parseChecksum, normalizeRtkVersion, readTextIfExists, rtkPlatformSpec, } from '../lib/utils.js';
|
|
11
|
+
import { CAVEMAN_REMOTE_RULE as CAVEMAN_REMOTE, RTK_RELEASE_API, fetchJson, findFile, httpsGet, httpsDownload, sha256Hex, parseChecksum, normalizeRtkVersion, readTextIfExists, rtkPlatformSpec, resolveRtkBinary, } from '../lib/utils.js';
|
|
12
12
|
const IS_WINDOWS = process.platform === 'win32';
|
|
13
13
|
const HOME = os.homedir();
|
|
14
14
|
const PONYTAIL_REMOTE = 'https://raw.githubusercontent.com/DietrichGebert/ponytail/main/package.json';
|
|
15
15
|
const PONYTAIL_LOCAL = path.join(HOME, '.omp', 'plugins', 'node_modules', '@dietrichgebert', 'ponytail', 'package.json');
|
|
16
|
-
const RTK_BINARY =
|
|
17
|
-
const CAVEMAN_LOCAL = path.join(HOME, '.omp', '
|
|
16
|
+
const RTK_BINARY = resolveRtkBinary();
|
|
17
|
+
const CAVEMAN_LOCAL = path.join(HOME, '.omp', 'plugins', 'node_modules', '@krtclcdy', 'tersio', 'extensions', 'caveman-session', 'rule.md');
|
|
18
18
|
const RELOAD_MSG = 'Reminder: restart OMP (or reload extensions) for updates to take effect.';
|
|
19
19
|
function notify(ctx, msg, level) {
|
|
20
20
|
ctx?.ui?.notify?.(String(msg), level);
|
|
@@ -55,6 +55,8 @@ function checkRtk() {
|
|
|
55
55
|
const latestTag = release.tag_name || null;
|
|
56
56
|
let localVer = null;
|
|
57
57
|
try {
|
|
58
|
+
if (!RTK_BINARY)
|
|
59
|
+
throw new Error('rtk not found in PATH');
|
|
58
60
|
const out = execFileSync(RTK_BINARY, ['--version'], { encoding: 'utf8', windowsHide: true, shell: false, timeout: 10000 }) || '';
|
|
59
61
|
if (out)
|
|
60
62
|
localVer = out.trim().split(/\r?\n/)[0];
|
|
@@ -146,6 +148,8 @@ async function updateRtk(ctx, dryRun = false) {
|
|
|
146
148
|
}
|
|
147
149
|
const tag = release.tag_name || 'unknown';
|
|
148
150
|
const assets = Array.isArray(release.assets) ? release.assets : [];
|
|
151
|
+
if (!RTK_BINARY)
|
|
152
|
+
return report(ctx, 'RTK: executable not found in PATH', 'warning');
|
|
149
153
|
// Cross-platform asset selection (mirrors installer stepRtk)
|
|
150
154
|
const PLATFORM = process.platform;
|
|
151
155
|
const ARCH = process.arch;
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
normalizeRtkVersion,
|
|
24
24
|
readTextIfExists,
|
|
25
25
|
rtkPlatformSpec,
|
|
26
|
+
resolveRtkBinary,
|
|
26
27
|
} from '../lib/utils.ts';
|
|
27
28
|
|
|
28
29
|
const IS_WINDOWS = process.platform === 'win32';
|
|
@@ -30,9 +31,18 @@ const HOME = os.homedir();
|
|
|
30
31
|
|
|
31
32
|
const PONYTAIL_REMOTE = 'https://raw.githubusercontent.com/DietrichGebert/ponytail/main/package.json';
|
|
32
33
|
const PONYTAIL_LOCAL = path.join(HOME, '.omp', 'plugins', 'node_modules', '@dietrichgebert', 'ponytail', 'package.json');
|
|
33
|
-
const RTK_BINARY =
|
|
34
|
-
const CAVEMAN_LOCAL = path.join(
|
|
35
|
-
|
|
34
|
+
const RTK_BINARY = resolveRtkBinary();
|
|
35
|
+
const CAVEMAN_LOCAL = path.join(
|
|
36
|
+
HOME,
|
|
37
|
+
'.omp',
|
|
38
|
+
'plugins',
|
|
39
|
+
'node_modules',
|
|
40
|
+
'@krtclcdy',
|
|
41
|
+
'tersio',
|
|
42
|
+
'extensions',
|
|
43
|
+
'caveman-session',
|
|
44
|
+
'rule.md',
|
|
45
|
+
);
|
|
36
46
|
const RELOAD_MSG = 'Reminder: restart OMP (or reload extensions) for updates to take effect.';
|
|
37
47
|
|
|
38
48
|
// --- Types ---
|
|
@@ -104,6 +114,7 @@ function checkRtk(): Promise<AddonStatus> {
|
|
|
104
114
|
const latestTag = release.tag_name || null;
|
|
105
115
|
let localVer: string | null = null;
|
|
106
116
|
try {
|
|
117
|
+
if (!RTK_BINARY) throw new Error('rtk not found in PATH');
|
|
107
118
|
const out = execFileSync(RTK_BINARY, ['--version'], { encoding: 'utf8', windowsHide: true, shell: false, timeout: 10000 }) || '';
|
|
108
119
|
if (out) localVer = out.trim().split(/\r?\n/)[0];
|
|
109
120
|
} catch { localVer = null; }
|
|
@@ -188,6 +199,7 @@ async function updateRtk(ctx: AddonUpdaterCtx, dryRun = false): Promise<string>
|
|
|
188
199
|
}
|
|
189
200
|
const tag = release.tag_name || 'unknown';
|
|
190
201
|
const assets = Array.isArray(release.assets) ? release.assets : [];
|
|
202
|
+
if (!RTK_BINARY) return report(ctx, 'RTK: executable not found in PATH', 'warning');
|
|
191
203
|
|
|
192
204
|
// Cross-platform asset selection (mirrors installer stepRtk)
|
|
193
205
|
const PLATFORM = process.platform;
|
|
@@ -5,16 +5,20 @@ import { dirname, join } from 'node:path';
|
|
|
5
5
|
import { readCavemanDefault } from '../shared/plugin-settings.js';
|
|
6
6
|
const CAVERN_DIR = dirname(fileURLToPath(import.meta.url));
|
|
7
7
|
const RULE_PATH = join(CAVERN_DIR, 'rule.md');
|
|
8
|
-
const FALLBACK_FULL_RULE = `
|
|
8
|
+
const FALLBACK_FULL_RULE = `Caveman full active for this session.
|
|
9
|
+
Respond terse like smart caveman. Keep all technical substance. Drop filler, pleasantries, and hedging.
|
|
9
10
|
|
|
10
11
|
Rules:
|
|
11
|
-
- Drop articles
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
12
|
+
- Drop articles where meaning stays clear. Keep fragments and short sentences.
|
|
13
|
+
- Keep technical terms, code, commands, paths, API names, numbers, units, and errors exact.
|
|
14
|
+
- Never invent abbreviations or causal arrows. Do not add words to sound caveman.
|
|
15
|
+
- Use ASD-STE100 Simplified Technical English. Keep one idea per sentence, target 20 words, active voice, and consistent terms.
|
|
16
|
+
- Make no tool-call narration. Use no decorative tables or emoji.
|
|
17
|
+
- Preserve the user's reply language. Compress style, not language.
|
|
18
|
+
- Drop caveman for security warnings, irreversible actions, ambiguous multi-step sequences, or requests to clarify.
|
|
19
|
+
- Write code, comments, commits, docs, issues, pull requests, and third-party messages in normal prose.
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
Default: **full**. Switch: \`/caveman lite|full|ultra|wenyan-lite|wenyan-full|wenyan-ultra|off\`. Stop: "stop caveman" or "normal mode".`;
|
|
18
22
|
// ponytail: synchronous read each full-mode injection; ceiling = small file, cold session start. Upgrade path: cache file contents + mtime, invalidate on change.
|
|
19
23
|
function readFullRule() {
|
|
20
24
|
try {
|
|
@@ -30,9 +34,10 @@ const INSTRUCTIONS = {
|
|
|
30
34
|
Respond concise. Drop pleasantries, filler, and hedging. Keep complete technical substance. Code, commands, paths, errors, commits, and PR text stay normal/exact.`,
|
|
31
35
|
full: () => `Caveman full active for this session.\n${readFullRule()}`,
|
|
32
36
|
ultra: `Caveman ultra active for this session.
|
|
33
|
-
Maximum terse prose.
|
|
34
|
-
wenyan: `Caveman wenyan active for this session.
|
|
35
|
-
|
|
37
|
+
Maximum terse prose. Strip conjunctions only when meaning stays clear. State each fact once. Use no invented abbreviations or causal arrows. Keep code, commands, commits, PR text, paths, API names, numbers, units, and errors exact. Drop caveman for security warnings, irreversible actions, ambiguous multi-step sequences, or user confusion.`,
|
|
38
|
+
'wenyan-lite': `Caveman wenyan-lite active for this session. Use semi-classical terse prose. Keep grammar structure, user language, technical terms, code, commands, paths, API names, numbers, units, and errors exact. Drop caveman when clarity or safety needs normal prose.`,
|
|
39
|
+
'wenyan-full': `Caveman wenyan-full active for this session. Use maximum classical terseness where meaning stays clear. Use classical sentence patterns, verbs before objects, and optional omitted subjects. Keep user language, technical terms, code, commands, paths, API names, numbers, units, and errors exact. Drop caveman when clarity or safety needs normal prose.`,
|
|
40
|
+
'wenyan-ultra': `Caveman wenyan-ultra active for this session. Use extreme classical abbreviation while keeping a classical Chinese feel and meaning clear. Keep user language, technical terms, code, commands, paths, API names, numbers, units, and errors exact. Drop caveman when clarity or safety needs normal prose.`,
|
|
36
41
|
};
|
|
37
42
|
function resolveMode(entries, fallback = DEFAULT_MODE) {
|
|
38
43
|
return lastCustomValue(entries, 'caveman-mode', (data) => normalizeMode('caveman', data?.mode)) ?? fallback;
|
|
@@ -97,7 +102,7 @@ export default function cavemanSessionExtension(pi) {
|
|
|
97
102
|
return;
|
|
98
103
|
}
|
|
99
104
|
if (!setMode(arg, ctx)) {
|
|
100
|
-
ctx?.ui?.notify?.('Usage: /caveman [lite|full|ultra|wenyan|off|status]', 'warning');
|
|
105
|
+
ctx?.ui?.notify?.('Usage: /caveman [lite|full|ultra|wenyan-lite|wenyan-full|wenyan-ultra|off|status]', 'warning');
|
|
101
106
|
}
|
|
102
107
|
},
|
|
103
108
|
});
|
|
@@ -117,7 +122,7 @@ export default function cavemanSessionExtension(pi) {
|
|
|
117
122
|
// Persisted session state wins; a fresh session falls back to the
|
|
118
123
|
// installer/user-configured default (off unless configured).
|
|
119
124
|
const persisted = resolveMode(entries, '');
|
|
120
|
-
currentMode = persisted || readCavemanDefault();
|
|
125
|
+
currentMode = persisted || normalizeMode('caveman', readCavemanDefault()) || DEFAULT_MODE;
|
|
121
126
|
syncStatus(ctx);
|
|
122
127
|
}
|
|
123
128
|
pi.on('session_start', async (_event, ctx) => {
|
|
@@ -8,16 +8,20 @@ import type { ExtensionApi, ExtensionCtx, InputEvent, SessionEntry, SystemPrompt
|
|
|
8
8
|
const CAVERN_DIR = dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
const RULE_PATH = join(CAVERN_DIR, 'rule.md');
|
|
10
10
|
|
|
11
|
-
const FALLBACK_FULL_RULE = `
|
|
11
|
+
const FALLBACK_FULL_RULE = `Caveman full active for this session.
|
|
12
|
+
Respond terse like smart caveman. Keep all technical substance. Drop filler, pleasantries, and hedging.
|
|
12
13
|
|
|
13
14
|
Rules:
|
|
14
|
-
- Drop articles
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
15
|
+
- Drop articles where meaning stays clear. Keep fragments and short sentences.
|
|
16
|
+
- Keep technical terms, code, commands, paths, API names, numbers, units, and errors exact.
|
|
17
|
+
- Never invent abbreviations or causal arrows. Do not add words to sound caveman.
|
|
18
|
+
- Use ASD-STE100 Simplified Technical English. Keep one idea per sentence, target 20 words, active voice, and consistent terms.
|
|
19
|
+
- Make no tool-call narration. Use no decorative tables or emoji.
|
|
20
|
+
- Preserve the user's reply language. Compress style, not language.
|
|
21
|
+
- Drop caveman for security warnings, irreversible actions, ambiguous multi-step sequences, or requests to clarify.
|
|
22
|
+
- Write code, comments, commits, docs, issues, pull requests, and third-party messages in normal prose.
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
Default: **full**. Switch: \`/caveman lite|full|ultra|wenyan-lite|wenyan-full|wenyan-ultra|off\`. Stop: "stop caveman" or "normal mode".`;
|
|
21
25
|
|
|
22
26
|
// ponytail: synchronous read each full-mode injection; ceiling = small file, cold session start. Upgrade path: cache file contents + mtime, invalidate on change.
|
|
23
27
|
function readFullRule(): string {
|
|
@@ -30,9 +34,10 @@ const INSTRUCTIONS: Record<string, string | (() => string)> = {
|
|
|
30
34
|
Respond concise. Drop pleasantries, filler, and hedging. Keep complete technical substance. Code, commands, paths, errors, commits, and PR text stay normal/exact.`,
|
|
31
35
|
full: () => `Caveman full active for this session.\n${readFullRule()}`,
|
|
32
36
|
ultra: `Caveman ultra active for this session.
|
|
33
|
-
Maximum terse prose.
|
|
34
|
-
wenyan: `Caveman wenyan active for this session.
|
|
35
|
-
|
|
37
|
+
Maximum terse prose. Strip conjunctions only when meaning stays clear. State each fact once. Use no invented abbreviations or causal arrows. Keep code, commands, commits, PR text, paths, API names, numbers, units, and errors exact. Drop caveman for security warnings, irreversible actions, ambiguous multi-step sequences, or user confusion.`,
|
|
38
|
+
'wenyan-lite': `Caveman wenyan-lite active for this session. Use semi-classical terse prose. Keep grammar structure, user language, technical terms, code, commands, paths, API names, numbers, units, and errors exact. Drop caveman when clarity or safety needs normal prose.`,
|
|
39
|
+
'wenyan-full': `Caveman wenyan-full active for this session. Use maximum classical terseness where meaning stays clear. Use classical sentence patterns, verbs before objects, and optional omitted subjects. Keep user language, technical terms, code, commands, paths, API names, numbers, units, and errors exact. Drop caveman when clarity or safety needs normal prose.`,
|
|
40
|
+
'wenyan-ultra': `Caveman wenyan-ultra active for this session. Use extreme classical abbreviation while keeping a classical Chinese feel and meaning clear. Keep user language, technical terms, code, commands, paths, API names, numbers, units, and errors exact. Drop caveman when clarity or safety needs normal prose.`,
|
|
36
41
|
};
|
|
37
42
|
|
|
38
43
|
function resolveMode(entries: SessionEntry[] | null | undefined, fallback: string = DEFAULT_MODE): string {
|
|
@@ -102,7 +107,7 @@ export default function cavemanSessionExtension(pi: ExtensionApi): void {
|
|
|
102
107
|
return;
|
|
103
108
|
}
|
|
104
109
|
if (!setMode(arg, ctx)) {
|
|
105
|
-
ctx?.ui?.notify?.('Usage: /caveman [lite|full|ultra|wenyan|off|status]', 'warning');
|
|
110
|
+
ctx?.ui?.notify?.('Usage: /caveman [lite|full|ultra|wenyan-lite|wenyan-full|wenyan-ultra|off|status]', 'warning');
|
|
106
111
|
}
|
|
107
112
|
},
|
|
108
113
|
});
|
|
@@ -122,7 +127,7 @@ export default function cavemanSessionExtension(pi: ExtensionApi): void {
|
|
|
122
127
|
// Persisted session state wins; a fresh session falls back to the
|
|
123
128
|
// installer/user-configured default (off unless configured).
|
|
124
129
|
const persisted = resolveMode(entries, '');
|
|
125
|
-
currentMode = persisted || readCavemanDefault();
|
|
130
|
+
currentMode = persisted || normalizeMode('caveman', readCavemanDefault()) || DEFAULT_MODE;
|
|
126
131
|
syncStatus(ctx);
|
|
127
132
|
}
|
|
128
133
|
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: caveman
|
|
3
|
+
description: >
|
|
4
|
+
Ultra-compressed communication mode that cuts output tokens while keeping
|
|
5
|
+
technical accuracy. Levels: lite, full, ultra and the wenyan variants. Use for
|
|
6
|
+
/caveman, "caveman mode", "talk like caveman", "be brief" or "less tokens".
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Respond terse like smart caveman. All technical substance stay. Only fluff die.
|
|
10
|
+
|
|
11
|
+
## Persistence
|
|
12
|
+
|
|
13
|
+
Default style for this whole session, every response, until user say "stop caveman" or "normal mode". Keep terse on long sessions no filler drift.
|
|
14
|
+
|
|
15
|
+
Default: **full**. Switch: `/caveman lite|full|ultra|wenyan-lite|wenyan-full|wenyan-ultra|off`.
|
|
16
|
+
|
|
17
|
+
## Rules
|
|
18
|
+
|
|
19
|
+
Drop: articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). No tool-call narration, no decorative tables/emoji, no dumping long raw error logs unless asked quote shortest decisive line. Standard well-known tech acronyms OK (DB/API/HTTP); never invent new abbreviations (cfg/impl/req/res/fn) tokenizer split them same as full word: zero token saved, reader still decode. Full word cheaper AND clearer. No causal arrows (→) either own token, save nothing. Technical terms exact. Code blocks unchanged. Errors quoted exact.
|
|
20
|
+
|
|
21
|
+
Never drop not/never/no/only/except flip meaning worse than any token saved. Numbers, units exact.
|
|
22
|
+
|
|
23
|
+
Never ADD word to sound caveman. Compression only style never grow output. No inserted pronoun or copula to fake broken grammar: "when it not" cost one token more than "when not" and say same thing. Keep correct verb form when correct form cost same "sees" one token, "see" one token, so mangle buy nothing and read worse. Same rule as abbreviations and arrows: if caveman phrasing not shorter than plain phrasing, use plain.
|
|
24
|
+
|
|
25
|
+
Clarity register: mix ASD-STE100 Simplified Technical English into caveman, always. One idea per sentence. Sentence short, target 20 words max. Active voice. Present tense where true. One word one meaning: same term for same thing every time, no synonym rotation. Instruction = imperative: "Run X", not "X should be run". Noun cluster 3 words max. Pronoun only with one clear referent, else repeat noun. Caveman cut filler; STE keep what make meaning unambiguous. Conflict between them → clarity win.
|
|
26
|
+
|
|
27
|
+
Tool calls: fire direct. No preamble, plan, or progress note before or between calls. After result: next call direct or final answer never announce next call. Text before call only to clarify, warn security/irreversible, or resolve ambiguity.
|
|
28
|
+
|
|
29
|
+
Follow explicit reply-language instructions from the user or project. Otherwise preserve the user's dominant language. Never switch because of example text or multilingual context elsewhere. Compress the style, not the language. Every emitted line in that language openings, pre-tool status lines, all not just final reply. ALWAYS keep technical terms, code, API names, CLI commands, commit-type keywords (feat/fix/...), and exact error strings verbatim unless user explicitly ask for translation.
|
|
30
|
+
|
|
31
|
+
'Drop articles' = article languages only. Where small markers carry case/role (particles, postpositions), keep them grammar, not filler; compress politeness/filler instead.
|
|
32
|
+
|
|
33
|
+
Answer directly in this style. Skip "caveman mode on", "me caveman think", "Caveman:" prefix or recap redundant with the reply itself. No normal answer plus caveman duplicate. User ask what mode is → say so plainly.
|
|
34
|
+
|
|
35
|
+
Pattern: `[thing] [action] [reason]. [next step].`
|
|
36
|
+
|
|
37
|
+
Not: "Sure! I'd be happy to help you with that. The issue you're experiencing is likely caused by..."
|
|
38
|
+
Yes: "Bug in auth middleware. Token expiry check use `<` not `<=`. Fix:"
|
|
39
|
+
|
|
40
|
+
## Intensity
|
|
41
|
+
|
|
42
|
+
| Level | What change |
|
|
43
|
+
|-------|------------|
|
|
44
|
+
| **lite** | No filler/hedging. Keep articles + full sentences. Professional but tight |
|
|
45
|
+
| **full** | Drop articles, fragments OK, short synonyms. Classic caveman. No tool-call narration, no decorative tables/emoji, no long raw error-log dumps unless asked. Standard acronyms OK; no invented abbreviations |
|
|
46
|
+
| **ultra** | Strip conjunctions when cause-then-effect stay unambiguous. One word when one word enough. State each fact once. NO prose abbreviations (cfg/impl/req/res/fn/auth), NO arrows (X → Y) measured zero token saving under tokenizer, cost decode clarity. Code symbols, function names, API names, error strings: never touch |
|
|
47
|
+
| **wenyan-lite** | Semi-classical. Drop filler/hedging but keep grammar structure, classical register |
|
|
48
|
+
| **wenyan-full** | Maximum classical terseness. Fully 文言文. 80-90% character reduction chars, not tokens. Classical sentence patterns, verbs precede objects, subjects often omitted, classical particles (之/乃/為/其) |
|
|
49
|
+
| **wenyan-ultra** | Extreme abbreviation while keeping classical Chinese feel. Maximum compression, ultra terse |
|
|
50
|
+
|
|
51
|
+
Example "Why React component re-render?"
|
|
52
|
+
- lite: "Your component re-renders because you create a new object reference each render. Wrap it in `useMemo`."
|
|
53
|
+
- full: "New object ref each render. Inline object prop = new ref = re-render. Wrap in `useMemo`."
|
|
54
|
+
- ultra: "Inline obj prop, new ref, re-render. `useMemo`."
|
|
55
|
+
- wenyan-lite: "組件頻重繪,以每繪新生對象參照故。以 useMemo 包之。"
|
|
56
|
+
- wenyan-full: "每繪新生對象參照,故重繪;以 useMemo 包之則免。"
|
|
57
|
+
- wenyan-ultra: "新參照則重繪。useMemo 包之。"
|
|
58
|
+
|
|
59
|
+
Example "Explain database connection pooling."
|
|
60
|
+
- lite: "Connection pooling reuses open connections instead of creating new ones per request. Avoids repeated handshake overhead."
|
|
61
|
+
- full: "Pool reuse open DB connections. No new connection per request. Skip handshake overhead."
|
|
62
|
+
- ultra: "Pool reuse open DB connections. No per-request handshake."
|
|
63
|
+
- wenyan-full: "池蓄已開之連,不逐請而新開,省握手之費。"
|
|
64
|
+
- wenyan-ultra: "池蓄連,免逐請新開,省握手。"
|
|
65
|
+
|
|
66
|
+
Classical chars = wenyan modes only. Never swap a word to a classical char to shrink at non-wenyan levels.
|
|
67
|
+
|
|
68
|
+
## Auto-Clarity
|
|
69
|
+
|
|
70
|
+
Drop caveman when:
|
|
71
|
+
- Security warnings
|
|
72
|
+
- Irreversible action confirmations
|
|
73
|
+
- Multi-step sequences where fragment order or omitted conjunctions risk misread
|
|
74
|
+
- Compression itself creates technical ambiguity (e.g., `"migrate table drop column backup first"` order unclear without articles/conjunctions)
|
|
75
|
+
- User asks to clarify or repeats question
|
|
76
|
+
|
|
77
|
+
Resume caveman after clear part done.
|
|
78
|
+
|
|
79
|
+
Example shows FORMAT only write warning in session language, not example's.
|
|
80
|
+
|
|
81
|
+
Example destructive op:
|
|
82
|
+
> **Warning:** This will permanently delete all rows in the `users` table and cannot be undone.
|
|
83
|
+
> ```sql
|
|
84
|
+
> DROP TABLE users;
|
|
85
|
+
> ```
|
|
86
|
+
> Caveman resume. Verify backup exist first.
|
|
87
|
+
|
|
88
|
+
## Boundaries
|
|
89
|
+
|
|
90
|
+
Persisted outside chat: write normal prose code, comments, commits, docs, issue/PR/MR/defect/ticket/bug-report text, memory files, third-party messages (/caveman-compress exempt). "Open a defect" or "file a bug" mean the same as "open issue": body go to other humans, so body normal English. "stop caveman" or "normal mode": revert. Level persist until changed or session end.
|
|
@@ -4,11 +4,10 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
6
|
import { activeModesSummary, asPromptArray, COMBO_LEVELS, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, normalizeComboLevel, paintableCtx, reconcileSharedComboEntries, sessionEntries, setSharedComboLevel, setSharedComboListener, setSharedComboMode, systemPromptIncludes, } from '../shared/session-state.js';
|
|
7
|
-
import { readComboDefault, readPonytailDefault } from '../shared/plugin-settings.js';
|
|
7
|
+
import { isComboSetupComplete, readComboDefault, readPonytailDefault, saveComboSetup, } from '../shared/plugin-settings.js';
|
|
8
8
|
const require = createRequire(import.meta.url);
|
|
9
9
|
const PONYTAIL_FALLBACK_INTENSITY = {
|
|
10
10
|
lite: 'Prefer the simplest correct solution.',
|
|
11
|
-
review: 'Review only for avoidable complexity; recommend the smallest correct replacement.',
|
|
12
11
|
};
|
|
13
12
|
function ponytailFallback(mode) {
|
|
14
13
|
const intensity = PONYTAIL_FALLBACK_INTENSITY[mode] ?? 'Use the minimum correct solution. Delete or reuse before adding.';
|
|
@@ -30,6 +29,7 @@ function loadPonytailInstructions(mode) {
|
|
|
30
29
|
export default function comboToggleExtension(pi) {
|
|
31
30
|
pi.setLabel?.('Combo session toggle (all 3 add-ons)');
|
|
32
31
|
let lastCtx = undefined;
|
|
32
|
+
let setupPrompted = false;
|
|
33
33
|
function syncStatus(ctx) {
|
|
34
34
|
lastCtx = paintableCtx(lastCtx, ctx);
|
|
35
35
|
const c = lastCtx;
|
|
@@ -115,10 +115,28 @@ export default function comboToggleExtension(pi) {
|
|
|
115
115
|
ctx?.ui?.notify?.(level === 'off' ? `Combo off — all tersio modes inactive for this session. Active: ${active}.` : `Combo ${level} on — ${active} active for this session.`, 'info');
|
|
116
116
|
},
|
|
117
117
|
});
|
|
118
|
+
async function runFirstRunSetup(ctx) {
|
|
119
|
+
if (setupPrompted || !ctx?.hasUI || !ctx.ui?.select || isComboSetupComplete())
|
|
120
|
+
return;
|
|
121
|
+
setupPrompted = true;
|
|
122
|
+
const choice = await ctx.ui.select('Session-start defaults — Combo preset', [
|
|
123
|
+
{ label: 'off', description: 'Keep every Tersio mode inactive' },
|
|
124
|
+
{ label: 'medium', description: 'caveman=lite, rtk=on, ponytail=lite' },
|
|
125
|
+
{ label: 'balanced', description: 'caveman=full, rtk=on, ponytail=full' },
|
|
126
|
+
{ label: 'max', description: 'caveman=ultra, rtk=on, ponytail=ultra' },
|
|
127
|
+
], { selectionMarker: 'radio', helpText: 'You can change this later with /tersio settings.' });
|
|
128
|
+
const level = choice || 'off';
|
|
129
|
+
if (saveComboSetup(level) && level !== 'off') {
|
|
130
|
+
persistPreset(level);
|
|
131
|
+
useState(setSharedComboLevel(level), ctx);
|
|
132
|
+
ctx.ui.notify?.(`Combo default saved: ${level} — ${activeModesSummary(getSharedComboState())} will activate on fresh sessions.`, 'info');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
118
135
|
pi.on('session_start', async (_event, ctx) => {
|
|
119
136
|
track(ctx);
|
|
120
137
|
if (!ctx?.hasUI)
|
|
121
138
|
syncStatus(ctx);
|
|
139
|
+
await runFirstRunSetup(ctx);
|
|
122
140
|
// Installer/user-configured default applies only when no persisted *mode*
|
|
123
141
|
// state exists — unrelated session entries must not block it, or the
|
|
124
142
|
// combo bar never paints on sessions that already carry other entries.
|
|
@@ -148,6 +166,8 @@ export default function comboToggleExtension(pi) {
|
|
|
148
166
|
for (const event of ['session_branch', 'session_tree', 'agent_start']) {
|
|
149
167
|
pi.on(event, async (_event, ctx) => {
|
|
150
168
|
track(ctx);
|
|
169
|
+
if (event === 'agent_start')
|
|
170
|
+
await runFirstRunSetup(ctx);
|
|
151
171
|
});
|
|
152
172
|
}
|
|
153
173
|
pi.on('before_agent_start', async (event, ctx) => {
|
|
@@ -20,14 +20,18 @@ import {
|
|
|
20
20
|
setSharedComboMode,
|
|
21
21
|
systemPromptIncludes,
|
|
22
22
|
} from '../shared/session-state.ts';
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
isComboSetupComplete,
|
|
25
|
+
readComboDefault,
|
|
26
|
+
readPonytailDefault,
|
|
27
|
+
saveComboSetup,
|
|
28
|
+
} from '../shared/plugin-settings.ts';
|
|
24
29
|
import type { ComboState, ExtensionApi, ExtensionCtx, SystemPromptEvent } from '../shared/types.ts';
|
|
25
30
|
|
|
26
31
|
const require = createRequire(import.meta.url);
|
|
27
32
|
|
|
28
33
|
const PONYTAIL_FALLBACK_INTENSITY: Record<string, string> = {
|
|
29
34
|
lite: 'Prefer the simplest correct solution.',
|
|
30
|
-
review: 'Review only for avoidable complexity; recommend the smallest correct replacement.',
|
|
31
35
|
};
|
|
32
36
|
|
|
33
37
|
function ponytailFallback(mode: string): string {
|
|
@@ -61,6 +65,7 @@ export default function comboToggleExtension(pi: ExtensionApi): void {
|
|
|
61
65
|
pi.setLabel?.('Combo session toggle (all 3 add-ons)');
|
|
62
66
|
|
|
63
67
|
let lastCtx: ExtensionCtx | undefined = undefined;
|
|
68
|
+
let setupPrompted = false;
|
|
64
69
|
|
|
65
70
|
function syncStatus(ctx?: ExtensionCtx): void {
|
|
66
71
|
lastCtx = paintableCtx(lastCtx, ctx);
|
|
@@ -166,9 +171,27 @@ export default function comboToggleExtension(pi: ExtensionApi): void {
|
|
|
166
171
|
},
|
|
167
172
|
});
|
|
168
173
|
|
|
174
|
+
async function runFirstRunSetup(ctx?: ExtensionCtx): Promise<void> {
|
|
175
|
+
if (setupPrompted || !ctx?.hasUI || !ctx.ui?.select || isComboSetupComplete()) return;
|
|
176
|
+
setupPrompted = true;
|
|
177
|
+
const choice = await ctx.ui.select('Session-start defaults — Combo preset', [
|
|
178
|
+
{ label: 'off', description: 'Keep every Tersio mode inactive' },
|
|
179
|
+
{ label: 'medium', description: 'caveman=lite, rtk=on, ponytail=lite' },
|
|
180
|
+
{ label: 'balanced', description: 'caveman=full, rtk=on, ponytail=full' },
|
|
181
|
+
{ label: 'max', description: 'caveman=ultra, rtk=on, ponytail=ultra' },
|
|
182
|
+
], { selectionMarker: 'radio', helpText: 'You can change this later with /tersio settings.' });
|
|
183
|
+
const level = choice || 'off';
|
|
184
|
+
if (saveComboSetup(level) && level !== 'off') {
|
|
185
|
+
persistPreset(level);
|
|
186
|
+
useState(setSharedComboLevel(level), ctx);
|
|
187
|
+
ctx.ui.notify?.(`Combo default saved: ${level} — ${activeModesSummary(getSharedComboState())} will activate on fresh sessions.`, 'info');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
169
191
|
pi.on('session_start', async (_event, ctx) => {
|
|
170
192
|
track(ctx);
|
|
171
193
|
if (!ctx?.hasUI) syncStatus(ctx);
|
|
194
|
+
await runFirstRunSetup(ctx);
|
|
172
195
|
// Installer/user-configured default applies only when no persisted *mode*
|
|
173
196
|
// state exists — unrelated session entries must not block it, or the
|
|
174
197
|
// combo bar never paints on sessions that already carry other entries.
|
|
@@ -200,6 +223,7 @@ export default function comboToggleExtension(pi: ExtensionApi): void {
|
|
|
200
223
|
for (const event of ['session_branch', 'session_tree', 'agent_start']) {
|
|
201
224
|
pi.on(event, async (_event, ctx) => {
|
|
202
225
|
track(ctx);
|
|
226
|
+
if (event === 'agent_start') await runFirstRunSetup(ctx);
|
|
203
227
|
});
|
|
204
228
|
}
|
|
205
229
|
|
package/extensions/lib/utils.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Shared helpers for the installer and the AI add-ons updater.
|
|
2
2
|
// Node built-ins only — this module must stay dependency-free.
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
|
-
import { createWriteStream, existsSync, mkdirSync, renameSync } from 'node:fs';
|
|
4
|
+
import { accessSync, constants, createWriteStream, existsSync, mkdirSync, renameSync, statSync } from 'node:fs';
|
|
5
5
|
import fs from 'node:fs/promises';
|
|
6
6
|
import https from 'node:https';
|
|
7
7
|
import os from 'node:os';
|
|
@@ -15,7 +15,7 @@ export function withResolvers() {
|
|
|
15
15
|
return { promise, resolve, reject };
|
|
16
16
|
}
|
|
17
17
|
export const RTK_RELEASE_API = 'https://api.github.com/repos/rtk-ai/rtk/releases/latest';
|
|
18
|
-
export const CAVEMAN_REMOTE_RULE = 'https://raw.githubusercontent.com/JuliusBrussee/caveman/main/
|
|
18
|
+
export const CAVEMAN_REMOTE_RULE = 'https://raw.githubusercontent.com/JuliusBrussee/caveman/main/skills/caveman/SKILL.md';
|
|
19
19
|
const RTK_PLATFORM_SPECS = {
|
|
20
20
|
'win32/x64': { triple: 'x86_64-pc-windows-msvc', ext: '.zip', binary: 'rtk.exe' },
|
|
21
21
|
'linux/x64': { triple: 'x86_64-unknown-linux-musl', ext: '.tar.gz', binary: 'rtk' },
|
|
@@ -23,6 +23,22 @@ const RTK_PLATFORM_SPECS = {
|
|
|
23
23
|
'darwin/x64': { triple: 'x86_64-apple-darwin', ext: '.tar.gz', binary: 'rtk' },
|
|
24
24
|
'darwin/arm64': { triple: 'aarch64-apple-darwin', ext: '.tar.gz', binary: 'rtk' },
|
|
25
25
|
};
|
|
26
|
+
export function resolveRtkBinary(managedDir = path.join(os.homedir(), '.bun', 'bin')) {
|
|
27
|
+
const name = process.platform === 'win32' ? 'rtk.exe' : 'rtk';
|
|
28
|
+
const candidates = (process.env.PATH || '').split(path.delimiter).filter(Boolean).map((dir) => path.join(dir, name));
|
|
29
|
+
if (managedDir)
|
|
30
|
+
candidates.push(path.join(managedDir, name));
|
|
31
|
+
for (const candidate of candidates) {
|
|
32
|
+
try {
|
|
33
|
+
if (!statSync(candidate).isFile())
|
|
34
|
+
continue;
|
|
35
|
+
accessSync(candidate, constants.X_OK);
|
|
36
|
+
return candidate;
|
|
37
|
+
}
|
|
38
|
+
catch { /* keep searching */ }
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
26
42
|
export function rtkPlatformSpec(platform = process.platform, arch = process.arch) {
|
|
27
43
|
return RTK_PLATFORM_SPECS[`${platform}/${arch}`] || null;
|
|
28
44
|
}
|
|
@@ -36,7 +52,7 @@ function redirectNext(res, url) {
|
|
|
36
52
|
export function httpsGet(url, opts = {}) {
|
|
37
53
|
const { promise, resolve, reject } = withResolvers();
|
|
38
54
|
const maxRedirects = opts.maxRedirects ?? 5;
|
|
39
|
-
const req = https.get(url, { headers: { 'User-Agent': 'tersio', Accept: 'application/json,*/*' } }, (res) => {
|
|
55
|
+
const req = https.get(url, { headers: { 'User-Agent': 'tersio', Accept: 'application/json,*/*' }, signal: opts.signal }, (res) => {
|
|
40
56
|
const next = redirectNext(res, url);
|
|
41
57
|
if (next) {
|
|
42
58
|
if (maxRedirects <= 0) {
|
|
@@ -45,7 +61,7 @@ export function httpsGet(url, opts = {}) {
|
|
|
45
61
|
return;
|
|
46
62
|
}
|
|
47
63
|
res.resume();
|
|
48
|
-
resolve(httpsGet(next, { maxRedirects: maxRedirects - 1 }));
|
|
64
|
+
resolve(httpsGet(next, { maxRedirects: maxRedirects - 1, signal: opts.signal }));
|
|
49
65
|
return;
|
|
50
66
|
}
|
|
51
67
|
if (res.statusCode !== 200) {
|
package/extensions/lib/utils.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Node built-ins only — this module must stay dependency-free.
|
|
3
3
|
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
|
-
import { createWriteStream, existsSync, mkdirSync, renameSync } from 'node:fs';
|
|
5
|
+
import { accessSync, constants, createWriteStream, existsSync, mkdirSync, renameSync, statSync } from 'node:fs';
|
|
6
6
|
import fs from 'node:fs/promises';
|
|
7
7
|
import https from 'node:https';
|
|
8
8
|
import os from 'node:os';
|
|
@@ -10,6 +10,7 @@ import path from 'node:path';
|
|
|
10
10
|
|
|
11
11
|
export interface HttpOptions {
|
|
12
12
|
maxRedirects?: number;
|
|
13
|
+
signal?: AbortSignal;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
// Promise.withResolvers is Node 22+; the package supports Node 18+.
|
|
@@ -22,7 +23,7 @@ export function withResolvers<T>(): PromiseWithResolvers<T> {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export const RTK_RELEASE_API = 'https://api.github.com/repos/rtk-ai/rtk/releases/latest';
|
|
25
|
-
export const CAVEMAN_REMOTE_RULE = 'https://raw.githubusercontent.com/JuliusBrussee/caveman/main/
|
|
26
|
+
export const CAVEMAN_REMOTE_RULE = 'https://raw.githubusercontent.com/JuliusBrussee/caveman/main/skills/caveman/SKILL.md';
|
|
26
27
|
|
|
27
28
|
export interface RtkPlatformSpec {
|
|
28
29
|
triple: string;
|
|
@@ -47,6 +48,19 @@ const RTK_PLATFORM_SPECS: Record<string, RtkPlatformSpec> = {
|
|
|
47
48
|
'darwin/x64': { triple: 'x86_64-apple-darwin', ext: '.tar.gz', binary: 'rtk' },
|
|
48
49
|
'darwin/arm64': { triple: 'aarch64-apple-darwin', ext: '.tar.gz', binary: 'rtk' },
|
|
49
50
|
};
|
|
51
|
+
export function resolveRtkBinary(managedDir = path.join(os.homedir(), '.bun', 'bin')): string | null {
|
|
52
|
+
const name = process.platform === 'win32' ? 'rtk.exe' : 'rtk';
|
|
53
|
+
const candidates = (process.env.PATH || '').split(path.delimiter).filter(Boolean).map((dir) => path.join(dir, name));
|
|
54
|
+
if (managedDir) candidates.push(path.join(managedDir, name));
|
|
55
|
+
for (const candidate of candidates) {
|
|
56
|
+
try {
|
|
57
|
+
if (!statSync(candidate).isFile()) continue;
|
|
58
|
+
accessSync(candidate, constants.X_OK);
|
|
59
|
+
return candidate;
|
|
60
|
+
} catch { /* keep searching */ }
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
50
64
|
|
|
51
65
|
export function rtkPlatformSpec(platform: string = process.platform, arch: string = process.arch): RtkPlatformSpec | null {
|
|
52
66
|
return RTK_PLATFORM_SPECS[`${platform}/${arch}`] || null;
|
|
@@ -63,12 +77,12 @@ function redirectNext(res: { statusCode?: number; headers: { location?: string }
|
|
|
63
77
|
export function httpsGet(url: string, opts: HttpOptions = {}): Promise<string> {
|
|
64
78
|
const { promise, resolve, reject } = withResolvers<string>();
|
|
65
79
|
const maxRedirects = opts.maxRedirects ?? 5;
|
|
66
|
-
const req = https.get(url, { headers: { 'User-Agent': 'tersio', Accept: 'application/json,*/*' } }, (res) => {
|
|
80
|
+
const req = https.get(url, { headers: { 'User-Agent': 'tersio', Accept: 'application/json,*/*' }, signal: opts.signal }, (res) => {
|
|
67
81
|
const next = redirectNext(res, url);
|
|
68
82
|
if (next) {
|
|
69
83
|
if (maxRedirects <= 0) { res.resume(); reject(new Error(`Too many redirects fetching ${url}`)); return; }
|
|
70
84
|
res.resume();
|
|
71
|
-
resolve(httpsGet(next, { maxRedirects: maxRedirects - 1 }));
|
|
85
|
+
resolve(httpsGet(next, { maxRedirects: maxRedirects - 1, signal: opts.signal }));
|
|
72
86
|
return;
|
|
73
87
|
}
|
|
74
88
|
if (res.statusCode !== 200) { res.resume(); reject(new Error(`HTTP ${res.statusCode} for ${url}`)); return; }
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import path from 'node:path';
|
|
3
1
|
import { activeModesSummary, asPromptArray, getSharedComboState, isComboPresetActive, isOmpSubagentPrompt, lastCustomValue, normalizeInputCommand, paintStatusBar, paintableCtx, reconcileSharedComboEntries, sessionEntries, setSharedComboListener, setSharedComboMode } from '../shared/session-state.js';
|
|
4
2
|
import { readRtkDefault } from '../shared/plugin-settings.js';
|
|
5
3
|
const DEFAULT_ENABLED = false;
|
|
@@ -15,12 +13,13 @@ function asBoolean(value) {
|
|
|
15
13
|
function resolveEnabled(entries) {
|
|
16
14
|
return lastCustomValue(entries, 'rtk-mode', (data) => asBoolean(data?.enabled));
|
|
17
15
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
function setRtkProcessEnabled(enabled) {
|
|
17
|
+
if (enabled)
|
|
18
|
+
delete process.env.RTK_DISABLED;
|
|
19
|
+
else
|
|
20
|
+
process.env.RTK_DISABLED = '1';
|
|
21
|
+
}
|
|
22
|
+
const RTK_PROMPT = `RTK guidance active. RTK automatically rewrites eligible Bash calls through the installed rtk hook. Prefer rtk for noisy shell output, but use exact raw output for state changes, checksums, patches, and diagnostics that need full bytes.`;
|
|
24
23
|
export default function rtkSessionExtension(pi) {
|
|
25
24
|
const { z } = pi.zod;
|
|
26
25
|
let enabled = DEFAULT_ENABLED;
|
|
@@ -42,7 +41,7 @@ export default function rtkSessionExtension(pi) {
|
|
|
42
41
|
enabled = Boolean(next);
|
|
43
42
|
pi.appendEntry?.('rtk-mode', { enabled });
|
|
44
43
|
setSharedComboMode('rtk', enabled);
|
|
45
|
-
|
|
44
|
+
setRtkProcessEnabled(enabled);
|
|
46
45
|
const active = activeModesSummary(getSharedComboState());
|
|
47
46
|
ctx?.ui?.notify?.(enabled ? `RTK on — compact shell output for this session. Active: ${active}.` : `RTK off. Active: ${active}.`, 'info');
|
|
48
47
|
}
|
|
@@ -51,6 +50,7 @@ export default function rtkSessionExtension(pi) {
|
|
|
51
50
|
// it at once so the next turn and the rtk_run gate see it, no reload.
|
|
52
51
|
function syncFromShared(state) {
|
|
53
52
|
enabled = state.rtk === 'on';
|
|
53
|
+
setRtkProcessEnabled(enabled);
|
|
54
54
|
syncStatus();
|
|
55
55
|
}
|
|
56
56
|
setSharedComboListener(syncFromShared);
|
|
@@ -89,7 +89,7 @@ export default function rtkSessionExtension(pi) {
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
onUpdate?.({ content: [{ type: 'text', text: `rtk ${params.args.join(' ')}` }], details: { phase: 'start' } });
|
|
92
|
-
const result = await pi.exec(
|
|
92
|
+
const result = await pi.exec('rtk', params.args, { signal, cwd: ctx?.cwd || pi.cwd });
|
|
93
93
|
const text = [result.stdout, result.stderr].filter(Boolean).join('\n');
|
|
94
94
|
return {
|
|
95
95
|
isError: result.code !== 0,
|
|
@@ -118,6 +118,7 @@ export default function rtkSessionExtension(pi) {
|
|
|
118
118
|
// installer/user-configured default (off unless configured).
|
|
119
119
|
const persisted = resolveEnabled(entries);
|
|
120
120
|
enabled = typeof persisted === 'boolean' ? persisted : readRtkDefault();
|
|
121
|
+
setRtkProcessEnabled(enabled);
|
|
121
122
|
syncStatus(ctx);
|
|
122
123
|
}
|
|
123
124
|
pi.on('session_start', async (_event, ctx) => {
|