@krtclcdy/tersio 1.0.1 → 1.0.2
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/extensions/ai-addons-updater/index.js +398 -0
- package/extensions/amanai-reward/index.js +24 -0
- package/extensions/amanai-reward/pi.js +32 -0
- package/extensions/caveman-session/index.js +153 -0
- package/extensions/combo-toggle/index.js +157 -0
- package/extensions/lib/utils.js +101 -0
- package/extensions/rtk-session/index.js +151 -0
- package/package.json +2 -1
- package/tersio.js +1143 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// /combo session toggle — set all three (caveman, rtk, ponytail) at once.
|
|
2
|
+
// Modes: off | medium | balanced | max
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { COMBO_LEVELS, getSharedComboState, isOmpSubagentPrompt, normalizeComboLevel, reconcileSharedComboEntries, setSharedComboLevel, setSharedComboListener, } from "../shared/session-state.js";
|
|
7
|
+
import { readComboDefault } from "../shared/plugin-settings.js";
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
function ponytailFallback(mode) {
|
|
10
|
+
const intensity = mode === "lite"
|
|
11
|
+
? "Prefer the simplest correct solution."
|
|
12
|
+
: mode === "review"
|
|
13
|
+
? "Review only for avoidable complexity; recommend the smallest correct replacement."
|
|
14
|
+
: "Use the minimum correct solution. Delete or reuse before adding.";
|
|
15
|
+
return `🦥 PONYTAIL MODE ACTIVE — level: ${mode}\n${intensity} Understand the path first and fix root causes, not symptoms. Prefer the standard library and YAGNI. Avoid speculative abstractions and dependencies. Preserve correctness. Verify changed behavior.`;
|
|
16
|
+
}
|
|
17
|
+
function entriesFrom(ctx) {
|
|
18
|
+
return ctx?.sessionManager?.getBranch?.() || ctx?.sessionManager?.getEntries?.() || [];
|
|
19
|
+
}
|
|
20
|
+
function levelSummary(state) {
|
|
21
|
+
return `caveman=${state.caveman} rtk=${state.rtk} ponytail=${state.ponytail}`;
|
|
22
|
+
}
|
|
23
|
+
function hasPonytailInstructions(systemPrompt) {
|
|
24
|
+
const prompts = Array.isArray(systemPrompt) ? systemPrompt : [systemPrompt];
|
|
25
|
+
return prompts.some((prompt) => typeof prompt === "string" && prompt.includes("PONYTAIL MODE ACTIVE"));
|
|
26
|
+
}
|
|
27
|
+
function loadPonytailInstructions(mode) {
|
|
28
|
+
try {
|
|
29
|
+
const installed = path.join(os.homedir(), ".omp", "plugins", "node_modules", "@dietrichgebert", "ponytail", "hooks", "ponytail-instructions.js");
|
|
30
|
+
const { getPonytailInstructions } = require(installed);
|
|
31
|
+
if (typeof getPonytailInstructions === "function")
|
|
32
|
+
return getPonytailInstructions(mode);
|
|
33
|
+
}
|
|
34
|
+
catch { }
|
|
35
|
+
return ponytailFallback(mode);
|
|
36
|
+
}
|
|
37
|
+
export default function comboToggleExtension(pi) {
|
|
38
|
+
pi.setLabel?.("Combo session toggle (all 3 add-ons)");
|
|
39
|
+
let currentState = getSharedComboState();
|
|
40
|
+
let lastCtx = undefined;
|
|
41
|
+
function syncStatus(ctx) {
|
|
42
|
+
if (ctx)
|
|
43
|
+
lastCtx = ctx;
|
|
44
|
+
const c = ctx || lastCtx;
|
|
45
|
+
if (!c?.ui?.setStatus)
|
|
46
|
+
return;
|
|
47
|
+
// Single unified bar replaces the three per-extension bars while a preset is
|
|
48
|
+
// active. In custom/off, the individual bars come back and combo stays clear.
|
|
49
|
+
if (currentState.level !== "medium" && currentState.level !== "balanced" && currentState.level !== "max") {
|
|
50
|
+
c.ui.setStatus("combo", undefined);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const theme = c.ui.theme;
|
|
54
|
+
const c0 = currentState.caveman.toUpperCase();
|
|
55
|
+
const r = currentState.rtk.toUpperCase();
|
|
56
|
+
const p = currentState.ponytail.toUpperCase();
|
|
57
|
+
const lvl = currentState.level.toUpperCase();
|
|
58
|
+
const label = `combo ${lvl}: 🪨caveman=${c0} ⚡rtk=${r} 🦥ponytail=${p}`;
|
|
59
|
+
c.ui.setStatus("combo", theme?.fg ? `${theme.fg("accent", "🧩")} ${theme.fg("muted", label)}` : `🧩 ${label}`);
|
|
60
|
+
// Clobber any sibling bars another extension may have painted in a race
|
|
61
|
+
// during session_start — our bar is canonical for the duration of the preset.
|
|
62
|
+
c.ui.setStatus("caveman", undefined);
|
|
63
|
+
c.ui.setStatus("rtk", undefined);
|
|
64
|
+
c.ui.setStatus("ponytail", undefined);
|
|
65
|
+
}
|
|
66
|
+
function useState(state, ctx) {
|
|
67
|
+
currentState = state;
|
|
68
|
+
syncStatus(ctx);
|
|
69
|
+
return state;
|
|
70
|
+
}
|
|
71
|
+
function reconcile(ctx) {
|
|
72
|
+
if (!ctx?.hasUI)
|
|
73
|
+
return currentState;
|
|
74
|
+
return useState(reconcileSharedComboEntries(entriesFrom(ctx)), ctx);
|
|
75
|
+
}
|
|
76
|
+
function listen(ctx) {
|
|
77
|
+
if (ctx?.hasUI)
|
|
78
|
+
setSharedComboListener((state) => useState(state));
|
|
79
|
+
}
|
|
80
|
+
pi.registerCommand?.("combo", {
|
|
81
|
+
description: "Toggle all 3 OMP add-ons at once. Usage: /combo <off|medium|balanced|max|status>",
|
|
82
|
+
handler: async (args, ctx) => {
|
|
83
|
+
listen(ctx);
|
|
84
|
+
const arg = String(args || "").trim().toLowerCase();
|
|
85
|
+
if (!arg || arg === "status") {
|
|
86
|
+
const state = reconcile(ctx);
|
|
87
|
+
ctx?.ui?.notify?.(`Combo: ${state.level === "custom" ? "INACTIVE" : state.level.toUpperCase()} (${levelSummary(state)})`, "info");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (arg === "help") {
|
|
91
|
+
ctx?.ui?.notify?.("/combo off — disables all 3 (caveman, rtk, ponytail)\n" +
|
|
92
|
+
"/combo medium — light: caveman=lite, rtk=on, ponytail=lite\n" +
|
|
93
|
+
"/combo balanced — middle: caveman=full, rtk=on, ponytail=full\n" +
|
|
94
|
+
"/combo max — aggressive: caveman=ultra, rtk=on, ponytail=ultra", "info");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const level = normalizeComboLevel(arg);
|
|
98
|
+
if (!level) {
|
|
99
|
+
ctx?.ui?.notify?.(`Unknown combo level: ${arg}. Use: off | medium | balanced | max`, "warning");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const modes = COMBO_LEVELS[level];
|
|
103
|
+
// Persist per-extension state so they restore on session_start
|
|
104
|
+
pi.appendEntry?.("caveman-mode", { mode: modes.caveman });
|
|
105
|
+
pi.appendEntry?.("rtk-mode", { enabled: modes.rtk === "on" });
|
|
106
|
+
pi.appendEntry?.("ponytail-mode", { mode: modes.ponytail });
|
|
107
|
+
pi.appendEntry?.("combo-level", { level });
|
|
108
|
+
useState(setSharedComboLevel(level), ctx);
|
|
109
|
+
ctx?.ui?.notify?.(`Combo ${level} applied: ${levelSummary(currentState)}`, "info");
|
|
110
|
+
if (ctx?.reload)
|
|
111
|
+
await ctx.reload();
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
115
|
+
listen(ctx);
|
|
116
|
+
if (ctx?.hasUI)
|
|
117
|
+
reconcile(ctx);
|
|
118
|
+
else
|
|
119
|
+
syncStatus(ctx);
|
|
120
|
+
// Installer/user-configured default applies only to a fresh session with
|
|
121
|
+
// no persisted state — real session state always wins.
|
|
122
|
+
if (getSharedComboState().level === "off" && !(ctx?.sessionManager?.getBranch?.() || ctx?.sessionManager?.getEntries?.() || []).length) {
|
|
123
|
+
const fallback = readComboDefault();
|
|
124
|
+
if (fallback !== "off") {
|
|
125
|
+
useState(setSharedComboLevel(fallback), ctx);
|
|
126
|
+
ctx?.ui?.notify?.(`Combo default applied: ${fallback}`, "info");
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
pi.on("session_branch", async (_event, ctx) => {
|
|
131
|
+
listen(ctx);
|
|
132
|
+
if (ctx?.hasUI)
|
|
133
|
+
reconcile(ctx);
|
|
134
|
+
});
|
|
135
|
+
pi.on("session_tree", async (_event, ctx) => {
|
|
136
|
+
listen(ctx);
|
|
137
|
+
if (ctx?.hasUI)
|
|
138
|
+
reconcile(ctx);
|
|
139
|
+
});
|
|
140
|
+
pi.on("agent_start", async (_event, ctx) => {
|
|
141
|
+
listen(ctx);
|
|
142
|
+
if (ctx?.hasUI)
|
|
143
|
+
reconcile(ctx);
|
|
144
|
+
});
|
|
145
|
+
pi.on("before_agent_start", async (event, ctx) => {
|
|
146
|
+
if (ctx?.hasUI)
|
|
147
|
+
reconcile(ctx);
|
|
148
|
+
if (!isOmpSubagentPrompt(event.systemPrompt))
|
|
149
|
+
return;
|
|
150
|
+
const mode = getSharedComboState().ponytail;
|
|
151
|
+
if (mode === "off" || hasPonytailInstructions(event.systemPrompt))
|
|
152
|
+
return;
|
|
153
|
+
const base = Array.isArray(event.systemPrompt) ? event.systemPrompt : [event.systemPrompt];
|
|
154
|
+
return { systemPrompt: [...base, loadPonytailInstructions(mode)] };
|
|
155
|
+
});
|
|
156
|
+
// Slash commands only; natural-language input caused accidental toggles and has no reload context.
|
|
157
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Shared helpers for the installer and the AI add-ons updater.
|
|
2
|
+
// Node built-ins only — this module must stay dependency-free.
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { createWriteStream } from "node:fs";
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import https from "node:https";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
// Promise.withResolvers is Node 22+; the package supports Node 18+.
|
|
9
|
+
// ponytail: swap for the built-in when engines bumps to >=22.
|
|
10
|
+
function withResolvers() {
|
|
11
|
+
let resolve;
|
|
12
|
+
let reject;
|
|
13
|
+
const promise = new Promise((res, rej) => { resolve = res; reject = rej; });
|
|
14
|
+
return { promise, resolve, reject };
|
|
15
|
+
}
|
|
16
|
+
export function httpsGet(url, opts = {}) {
|
|
17
|
+
const { promise, resolve, reject } = withResolvers();
|
|
18
|
+
const maxRedirects = opts.maxRedirects ?? 5;
|
|
19
|
+
const req = https.get(url, { headers: { "User-Agent": "tersio", Accept: "application/json,*/*" } }, (res) => {
|
|
20
|
+
if (res.statusCode !== undefined && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
21
|
+
if (maxRedirects <= 0) {
|
|
22
|
+
res.resume();
|
|
23
|
+
reject(new Error(`Too many redirects fetching ${url}`));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
res.resume();
|
|
27
|
+
const next = new URL(res.headers.location, url).href;
|
|
28
|
+
resolve(httpsGet(next, { maxRedirects: maxRedirects - 1 }));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (res.statusCode !== 200) {
|
|
32
|
+
res.resume();
|
|
33
|
+
reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let body = "";
|
|
37
|
+
// ponytail: streamed accumulation — fine for tens of KB; stream-pipe if assets ever exceed a few MB.
|
|
38
|
+
res.setEncoding("utf8");
|
|
39
|
+
res.on("data", (chunk) => { body += chunk; });
|
|
40
|
+
res.on("end", () => resolve(body));
|
|
41
|
+
});
|
|
42
|
+
req.on("error", reject);
|
|
43
|
+
req.setTimeout(30000, () => req.destroy(new Error(`Timeout fetching ${url}`)));
|
|
44
|
+
return promise;
|
|
45
|
+
}
|
|
46
|
+
export function httpsDownload(url, dest, opts = {}) {
|
|
47
|
+
const { promise, resolve, reject } = withResolvers();
|
|
48
|
+
const maxRedirects = opts.maxRedirects ?? 5;
|
|
49
|
+
const req = https.get(url, { headers: { "User-Agent": "tersio", Accept: "*/*" } }, (res) => {
|
|
50
|
+
if (res.statusCode !== undefined && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
51
|
+
if (maxRedirects <= 0) {
|
|
52
|
+
res.resume();
|
|
53
|
+
reject(new Error(`Too many redirects downloading ${url}`));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
res.resume();
|
|
57
|
+
const next = new URL(res.headers.location, url).href;
|
|
58
|
+
resolve(httpsDownload(next, dest, { maxRedirects: maxRedirects - 1 }));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (res.statusCode !== 200) {
|
|
62
|
+
res.resume();
|
|
63
|
+
reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const file = createWriteStream(dest);
|
|
67
|
+
res.pipe(file);
|
|
68
|
+
file.on("finish", () => file.close(() => resolve()));
|
|
69
|
+
file.on("error", reject);
|
|
70
|
+
});
|
|
71
|
+
req.on("error", reject);
|
|
72
|
+
req.setTimeout(120000, () => req.destroy(new Error(`Timeout downloading ${url}`)));
|
|
73
|
+
return promise;
|
|
74
|
+
}
|
|
75
|
+
export function sha256Hex(text) {
|
|
76
|
+
return createHash("sha256").update(text, "utf8").digest("hex");
|
|
77
|
+
}
|
|
78
|
+
export async function sha256File(filePath) {
|
|
79
|
+
const buf = await fs.readFile(filePath);
|
|
80
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
81
|
+
}
|
|
82
|
+
export function parseChecksum(checksumsText, assetName) {
|
|
83
|
+
const target = path.basename(assetName);
|
|
84
|
+
for (const line of checksumsText.split(/\r?\n/)) {
|
|
85
|
+
const m = line.match(/^([0-9a-fA-F]{64})\s+\*?(.+)$/);
|
|
86
|
+
if (m && path.basename(m[2]) === target)
|
|
87
|
+
return m[1].toLowerCase();
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
export async function readTextIfExists(p) {
|
|
92
|
+
try {
|
|
93
|
+
return await fs.readFile(p, "utf8");
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export function normalizeRtkVersion(value) {
|
|
100
|
+
return String(value || "").replace(/^rtk\s+/i, "").replace(/^v/i, "").trim();
|
|
101
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { COMBO_LEVELS, getSharedComboState, isOmpSubagentPrompt, setSharedComboMode } from "../shared/session-state.js";
|
|
4
|
+
import { readRtkDefault } from "../shared/plugin-settings.js";
|
|
5
|
+
const DEFAULT_ENABLED = false;
|
|
6
|
+
function asBoolean(value) {
|
|
7
|
+
if (typeof value === "boolean")
|
|
8
|
+
return value;
|
|
9
|
+
if (value === "on" || value === "true")
|
|
10
|
+
return true;
|
|
11
|
+
if (value === "off" || value === "false")
|
|
12
|
+
return false;
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
function resolveEnabled(entries) {
|
|
16
|
+
if (!Array.isArray(entries))
|
|
17
|
+
return null;
|
|
18
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
19
|
+
const entry = entries[i];
|
|
20
|
+
if (entry?.type !== "custom" || entry?.customType !== "rtk-mode")
|
|
21
|
+
continue;
|
|
22
|
+
return asBoolean(entry?.data?.enabled);
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const IS_WINDOWS = process.platform === "win32";
|
|
27
|
+
const HOME = os.homedir();
|
|
28
|
+
const RTK_BINARY = path.join(HOME, ".bun", "bin", IS_WINDOWS ? "rtk.exe" : "rtk");
|
|
29
|
+
const RTK_PROMPT = `RTK mode active for this session.
|
|
30
|
+
Use Rust Token Killer for shell output that would otherwise be noisy. Prefer explicit RTK commands in bash: \`rtk git status\`, \`rtk git diff\`, \`rtk read <file>\`, \`rtk grep <pattern> <path>\`, \`rtk find <glob> <path>\`, \`rtk test <cmd...>\`, \`rtk tsc\`, \`rtk lint\`, or \`rtk <tool> ...\` for supported dev commands.
|
|
31
|
+
Do not use RTK when exact raw output is required, when a specialized OMP tool is required by system policy, or when the command changes state and RTK would hide important confirmation text. Specialized OMP tools still win: read/glob/grep/edit/lsp stay preferred over shell equivalents.`;
|
|
32
|
+
export default function rtkSessionExtension(pi) {
|
|
33
|
+
const { z } = pi.zod;
|
|
34
|
+
let enabled = DEFAULT_ENABLED;
|
|
35
|
+
let isActive = false;
|
|
36
|
+
let lastCtx = undefined;
|
|
37
|
+
function syncStatus(ctx) {
|
|
38
|
+
if (ctx)
|
|
39
|
+
lastCtx = ctx;
|
|
40
|
+
const c = ctx || lastCtx;
|
|
41
|
+
if (!c?.ui?.setStatus)
|
|
42
|
+
return;
|
|
43
|
+
// Combo owns the bar when any preset is active; keep ours empty to avoid duplication.
|
|
44
|
+
if (getSharedComboState().level in COMBO_LEVELS && getSharedComboState().level !== "off") {
|
|
45
|
+
c.ui.setStatus("rtk", undefined);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (!enabled) {
|
|
49
|
+
c.ui.setStatus("rtk", undefined);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const theme = c.ui.theme;
|
|
53
|
+
const indicator = isActive && theme?.fg ? theme.fg("accent", "⚡") : "⚡";
|
|
54
|
+
const label = "rtk: ON";
|
|
55
|
+
c.ui.setStatus("rtk", theme?.fg ? `${indicator} ${theme.fg("muted", label)}` : `${indicator} ${label}`);
|
|
56
|
+
}
|
|
57
|
+
function setEnabled(next, ctx) {
|
|
58
|
+
enabled = Boolean(next);
|
|
59
|
+
pi.appendEntry?.("rtk-mode", { enabled });
|
|
60
|
+
setSharedComboMode("rtk", enabled);
|
|
61
|
+
syncStatus(ctx);
|
|
62
|
+
ctx?.ui?.notify?.(`RTK mode ${enabled ? "on" : "off"}.`, "info");
|
|
63
|
+
}
|
|
64
|
+
pi.setLabel?.("RTK session toggle");
|
|
65
|
+
pi.registerCommand?.("rtk", {
|
|
66
|
+
description: "Toggle RTK compact shell-output guidance for this session",
|
|
67
|
+
handler: async (args, ctx) => {
|
|
68
|
+
const arg = String(args || "").trim().toLowerCase();
|
|
69
|
+
if (!arg || arg === "status") {
|
|
70
|
+
ctx?.ui?.notify?.(`RTK: ${enabled ? "on" : "off"}`, "info");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (["on", "enable", "enabled", "true"].includes(arg)) {
|
|
74
|
+
setEnabled(true, ctx);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (["off", "disable", "disabled", "false"].includes(arg)) {
|
|
78
|
+
setEnabled(false, ctx);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
ctx?.ui?.notify?.("Usage: /rtk [on|off|status]", "warning");
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
pi.registerTool?.({
|
|
85
|
+
name: "rtk_run",
|
|
86
|
+
label: "RTK Run",
|
|
87
|
+
description: "Run the installed `rtk` binary for compact command output when RTK mode is enabled.",
|
|
88
|
+
parameters: z.object({
|
|
89
|
+
args: z.array(z.string()).min(1).describe("Arguments passed to rtk, e.g. ['git','status'] or ['read','src/index.ts']"),
|
|
90
|
+
}),
|
|
91
|
+
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
92
|
+
if (!enabled) {
|
|
93
|
+
return {
|
|
94
|
+
isError: true,
|
|
95
|
+
content: [{ type: "text", text: "RTK mode is off. Run /rtk on for this session, or use bash explicitly." }],
|
|
96
|
+
details: { enabled },
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
onUpdate?.({ content: [{ type: "text", text: `rtk ${params.args.join(" ")}` }], details: { phase: "start" } });
|
|
100
|
+
const result = await pi.exec(RTK_BINARY, params.args, { signal, cwd: ctx?.cwd || pi.cwd });
|
|
101
|
+
const text = [result.stdout, result.stderr].filter(Boolean).join(result.stdout && result.stderr ? "\n" : "");
|
|
102
|
+
return {
|
|
103
|
+
isError: result.code !== 0,
|
|
104
|
+
content: [{ type: "text", text: text || `rtk exited ${result.code}` }],
|
|
105
|
+
details: { code: result.code, killed: result.killed, enabled },
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
pi.on("input", async (event) => {
|
|
110
|
+
if (event?.source === "extension")
|
|
111
|
+
return;
|
|
112
|
+
const t = String(event?.text || "").trim().toLowerCase().replace(/[.!?\s]+$/, "");
|
|
113
|
+
if (t === "rtk on" || t === "use rtk")
|
|
114
|
+
setEnabled(true);
|
|
115
|
+
if (t === "rtk off" || t === "stop rtk")
|
|
116
|
+
setEnabled(false);
|
|
117
|
+
});
|
|
118
|
+
function restoreEnabled(ctx) {
|
|
119
|
+
const entries = ctx?.sessionManager?.getBranch?.() || ctx?.sessionManager?.getEntries?.() || [];
|
|
120
|
+
// Persisted session state wins; a fresh session falls back to the
|
|
121
|
+
// installer/user-configured default (off unless configured).
|
|
122
|
+
const persisted = resolveEnabled(entries);
|
|
123
|
+
enabled = typeof persisted === "boolean" ? persisted : readRtkDefault();
|
|
124
|
+
syncStatus(ctx);
|
|
125
|
+
}
|
|
126
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
127
|
+
restoreEnabled(ctx);
|
|
128
|
+
ctx?.ui?.notify?.(`RTK loaded: ${enabled ? "on" : "off"}`, "info");
|
|
129
|
+
});
|
|
130
|
+
pi.on("session_branch", async (_event, ctx) => {
|
|
131
|
+
restoreEnabled(ctx);
|
|
132
|
+
});
|
|
133
|
+
pi.on("session_tree", async (_event, ctx) => {
|
|
134
|
+
restoreEnabled(ctx);
|
|
135
|
+
});
|
|
136
|
+
pi.on("agent_start", async (_event, ctx) => {
|
|
137
|
+
isActive = true;
|
|
138
|
+
syncStatus(ctx);
|
|
139
|
+
});
|
|
140
|
+
pi.on("agent_end", async (_event, ctx) => {
|
|
141
|
+
isActive = false;
|
|
142
|
+
syncStatus(ctx);
|
|
143
|
+
});
|
|
144
|
+
pi.on("before_agent_start", async (event) => {
|
|
145
|
+
const active = isOmpSubagentPrompt(event.systemPrompt) ? getSharedComboState().rtk === "on" : enabled;
|
|
146
|
+
if (!active)
|
|
147
|
+
return;
|
|
148
|
+
const base = Array.isArray(event.systemPrompt) ? event.systemPrompt : [event.systemPrompt];
|
|
149
|
+
return { systemPrompt: [...base, RTK_PROMPT] };
|
|
150
|
+
});
|
|
151
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@krtclcdy/tersio",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Amanai reward detector and three toggleable OMP extensions for terse replies, compact shell output, and minimal code decisions — with a manual updater and dry-run support.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"omp",
|
|
@@ -109,6 +109,7 @@
|
|
|
109
109
|
"scripts": {
|
|
110
110
|
"clean": "rm -f tersio.js extensions/**/*.js",
|
|
111
111
|
"build": "tsc",
|
|
112
|
+
"prepublishOnly": "npm run build",
|
|
112
113
|
"check": "tsc --noEmit",
|
|
113
114
|
"doctor": "node tersio.js --doctor",
|
|
114
115
|
"dry-run": "node tersio.js --dry-run --scope user --yes",
|