killeros 1.0.0 → 1.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/CHANGELOG.md +20 -0
- package/Killeros.ts +172 -303
- package/README.md +14 -14
- package/package.json +9 -3
- package/themes/killeros.json +85 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to KillerOS are documented here.
|
|
4
|
+
|
|
5
|
+
## [1.1.0] - 2026-07-31
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Compact startup card with model, reasoning level, working directory, context remaining, and detected package capabilities.
|
|
10
|
+
- Packaged KillerOS theme with coral accents and one neutral tool-call surface across pending, success, and error states.
|
|
11
|
+
- Single-glyph Spark activity indicator with a restrained color pulse.
|
|
12
|
+
- Claude-adjacent activity word bank that advances between agent runs.
|
|
13
|
+
- Static `└ Thinking…` label for hidden reasoning blocks.
|
|
14
|
+
- Responsive header tests across terminal widths from 1 to 100 columns.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Replaced the animated startup illustration and rotating tips with the compact operational card.
|
|
19
|
+
- Standardized product branding on mixed-case `KillerOS` and the `› KillerOS` lockup.
|
|
20
|
+
- Reduced saturated semantic color usage while preserving status text and diff distinctions.
|
package/Killeros.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
1
2
|
import os from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
2
4
|
import {
|
|
3
5
|
CustomEditor,
|
|
4
6
|
DynamicBorder,
|
|
@@ -12,6 +14,7 @@ import {
|
|
|
12
14
|
} from "@earendil-works/pi-coding-agent";
|
|
13
15
|
import {
|
|
14
16
|
Container,
|
|
17
|
+
decodeKittyPrintable,
|
|
15
18
|
Editor,
|
|
16
19
|
Key,
|
|
17
20
|
matchesKey,
|
|
@@ -27,42 +30,24 @@ import {
|
|
|
27
30
|
import { Type } from "typebox";
|
|
28
31
|
|
|
29
32
|
const BRAND_RGB = "215;119;87";
|
|
30
|
-
const LEFT_PANEL_WIDTH = 42;
|
|
31
|
-
const LOGO_CELL = "███";
|
|
32
|
-
const LOGO_ANIMATION_INTERVAL_MS = 120;
|
|
33
|
-
const TIP_ROTATION_INTERVAL_MS = 5_000;
|
|
34
33
|
const FOOTER_REFRESH_INTERVAL_MS = 1_000;
|
|
34
|
+
const COMPACT_HEADER_MAX_WIDTH = 76;
|
|
35
35
|
|
|
36
36
|
const brand = (text: string): string => `\x1B[38;2;${BRAND_RGB}m${text}\x1B[39m`;
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
function readPackageMetadata(path: string | URL): { name?: string; version?: string } {
|
|
39
|
+
try {
|
|
40
|
+
const value = JSON.parse(readFileSync(path, "utf8")) as { name?: unknown; version?: unknown };
|
|
41
|
+
return {
|
|
42
|
+
name: typeof value.name === "string" ? value.name : undefined,
|
|
43
|
+
version: typeof value.version === "string" ? value.version : undefined,
|
|
44
|
+
};
|
|
45
|
+
} catch {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
const
|
|
48
|
-
...Array.from({ length: 4 }, (_, ay): LogoFrame => ({ phase: 0, active: "left", ax: 2, ay, flash: false, white: false })),
|
|
49
|
-
...Array.from({ length: 3 }, (_, ay): LogoFrame => ({ phase: 1, active: "top", ax: 2, ay, flash: false, white: false })),
|
|
50
|
-
...Array.from({ length: 5 }, (_, ay): LogoFrame => ({ phase: 2, active: "right", ax: 5, ay, flash: false, white: false })),
|
|
51
|
-
{ phase: 3, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
52
|
-
{ phase: 3, active: "none", ax: 0, ay: 0, flash: true, white: false },
|
|
53
|
-
{ phase: 3, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
54
|
-
{ phase: 3, active: "none", ax: 0, ay: 0, flash: true, white: false },
|
|
55
|
-
{ phase: 4, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
56
|
-
{ phase: 5, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
57
|
-
{ phase: 5, active: "none", ax: 0, ay: 0, flash: false, white: true },
|
|
58
|
-
{ phase: 5, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
59
|
-
{ phase: 5, active: "none", ax: 0, ay: 0, flash: false, white: true },
|
|
60
|
-
{ phase: 6, active: "none", ax: 0, ay: 0, flash: false, white: false },
|
|
61
|
-
];
|
|
62
|
-
|
|
63
|
-
function extractProvider(model: ExtensionContext["model"]): string {
|
|
64
|
-
return model?.provider ?? "";
|
|
65
|
-
}
|
|
50
|
+
const KILLEROS_VERSION = readPackageMetadata(new URL("./package.json", import.meta.url)).version;
|
|
66
51
|
|
|
67
52
|
function formatCwd(cwd: string): string {
|
|
68
53
|
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
@@ -76,264 +61,125 @@ function formatCwd(cwd: string): string {
|
|
|
76
61
|
: cwd;
|
|
77
62
|
}
|
|
78
63
|
|
|
79
|
-
function center(text: string, width: number): string {
|
|
80
|
-
if (width <= 0) return "";
|
|
81
|
-
const textWidth = visibleWidth(text);
|
|
82
|
-
if (textWidth >= width) return truncateToWidth(text, width, "");
|
|
83
|
-
return `${" ".repeat(Math.floor((width - textWidth) / 2))}${text}`;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
64
|
function padRight(text: string, width: number): string {
|
|
87
65
|
if (width <= 0) return "";
|
|
88
66
|
const clipped = truncateToWidth(text, width, "");
|
|
89
67
|
return clipped + " ".repeat(Math.max(0, width - visibleWidth(clipped)));
|
|
90
68
|
}
|
|
91
69
|
|
|
92
|
-
|
|
93
|
-
|
|
70
|
+
interface CapabilitySourceInfo {
|
|
71
|
+
path?: string;
|
|
72
|
+
source?: string;
|
|
73
|
+
baseDir?: string;
|
|
94
74
|
}
|
|
95
75
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return y === py + dy && x === px + dx;
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
type LogoColor = "cyan" | "red" | "green" | "orange" | "flash" | "white" | "brand" | "panel";
|
|
104
|
-
|
|
105
|
-
function colorCell(color: LogoColor): string {
|
|
106
|
-
switch (color) {
|
|
107
|
-
case "cyan": return `\x1B[36m${LOGO_CELL}\x1B[39m`;
|
|
108
|
-
case "red": return `\x1B[31m${LOGO_CELL}\x1B[39m`;
|
|
109
|
-
case "green": return `\x1B[32m${LOGO_CELL}\x1B[39m`;
|
|
110
|
-
case "orange":
|
|
111
|
-
case "flash": return `\x1B[33m${LOGO_CELL}\x1B[39m`;
|
|
112
|
-
case "white": return `\x1B[97m${LOGO_CELL}\x1B[39m`;
|
|
113
|
-
case "brand": return brand(LOGO_CELL);
|
|
114
|
-
default: return " ".repeat(LOGO_CELL.length);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function logoCellColor(frame: LogoFrame, y: number, x: number): LogoColor {
|
|
119
|
-
if (frame.white) {
|
|
120
|
-
return hasCell(y, x, "3,2 3,3 3,4 4,2 4,4 5,2 5,3 5,5 6,2 6,5") ? "white" : "panel";
|
|
121
|
-
}
|
|
122
|
-
if (frame.flash && y === 6 && x >= 1 && x <= 6) return "flash";
|
|
123
|
-
if (frame.active === "left" && hasPiece(y, x, frame.ay, frame.ax, "0,0 1,0 1,1 2,0")) return "red";
|
|
124
|
-
if (frame.active === "top" && hasPiece(y, x, frame.ay, frame.ax, "0,0 0,1 0,2 1,2")) return "cyan";
|
|
125
|
-
if (frame.active === "right" && hasPiece(y, x, frame.ay, frame.ax, "0,0 1,0 2,0 2,1")) return "green";
|
|
126
|
-
if (frame.phase === 6) {
|
|
127
|
-
return hasCell(y, x, "3,2 3,3 3,4 4,4 4,2 5,2 5,3 5,5 6,2 6,5") ? "brand" : "panel";
|
|
128
|
-
}
|
|
129
|
-
if (frame.phase === 4) {
|
|
130
|
-
if (hasCell(y, x, "2,2 2,3 2,4 3,4")) return "cyan";
|
|
131
|
-
if (hasCell(y, x, "3,2 4,2 4,3 5,2")) return "red";
|
|
132
|
-
if (hasCell(y, x, "4,5 5,5")) return "green";
|
|
133
|
-
return "panel";
|
|
134
|
-
}
|
|
135
|
-
if (frame.phase >= 5) {
|
|
136
|
-
if (hasCell(y, x, "3,2 3,3 3,4 4,4")) return "cyan";
|
|
137
|
-
if (hasCell(y, x, "4,2 5,2 5,3 6,2")) return "red";
|
|
138
|
-
if (hasCell(y, x, "5,5 6,5")) return "green";
|
|
139
|
-
return "panel";
|
|
140
|
-
}
|
|
141
|
-
if (frame.phase <= 3 && hasCell(y, x, "6,1 6,2 6,3 6,4")) return "orange";
|
|
142
|
-
if (frame.phase >= 2 && hasCell(y, x, "2,2 2,3 2,4 3,4")) return "cyan";
|
|
143
|
-
if (frame.phase >= 1 && hasCell(y, x, "3,2 4,2 4,3 5,2")) return "red";
|
|
144
|
-
if (frame.phase >= 3 && hasCell(y, x, "4,5 5,5 6,5 6,6")) return "green";
|
|
145
|
-
return "panel";
|
|
76
|
+
interface CompactCapability {
|
|
77
|
+
label: string;
|
|
78
|
+
version?: string;
|
|
146
79
|
}
|
|
147
80
|
|
|
148
|
-
function
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
for (let x = 1; x <= 8; x += 1) line += colorCell(logoCellColor(frame, y, x));
|
|
154
|
-
lines.push(line);
|
|
155
|
-
}
|
|
156
|
-
return lines;
|
|
81
|
+
function capabilityLabel(packageName: string): string {
|
|
82
|
+
const name = packageName.replace(/^npm:/, "").split("/").at(-1) ?? packageName;
|
|
83
|
+
if (name === "pi-mcp-adapter") return "MCP adapter";
|
|
84
|
+
if (name === "pi-web-access") return "Web access";
|
|
85
|
+
return name.replace(/^pi-/, "").replace(/[-_]+/g, " ").replace(/^\w/, (letter) => letter.toUpperCase());
|
|
157
86
|
}
|
|
158
87
|
|
|
159
|
-
function
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
88
|
+
function collectCompactCapabilities(pi: Pick<ExtensionAPI, "getCommands" | "getAllTools">): CompactCapability[] {
|
|
89
|
+
const sources: CapabilitySourceInfo[] = [];
|
|
90
|
+
try {
|
|
91
|
+
for (const command of pi.getCommands()) {
|
|
92
|
+
if (command.source === "extension" && command.sourceInfo.source !== "inline") sources.push(command.sourceInfo);
|
|
93
|
+
}
|
|
94
|
+
} catch {}
|
|
95
|
+
try {
|
|
96
|
+
for (const tool of pi.getAllTools()) {
|
|
97
|
+
if (tool.sourceInfo.source !== "builtin" && tool.sourceInfo.source !== "sdk") sources.push(tool.sourceInfo);
|
|
98
|
+
}
|
|
99
|
+
} catch {}
|
|
100
|
+
|
|
101
|
+
const capabilities = new Map<string, CompactCapability>();
|
|
102
|
+
for (const source of sources) {
|
|
103
|
+
const directories = [source.baseDir, source.path ? dirname(source.path) : undefined]
|
|
104
|
+
.filter((directory): directory is string => Boolean(directory));
|
|
105
|
+
let metadata: { name?: string; version?: string } = {};
|
|
106
|
+
for (const directory of new Set(directories)) {
|
|
107
|
+
metadata = readPackageMetadata(join(directory, "package.json"));
|
|
108
|
+
if (metadata.name) break;
|
|
109
|
+
}
|
|
110
|
+
const packageName = metadata.name ?? (source.source?.startsWith("npm:") ? source.source.slice(4) : undefined);
|
|
111
|
+
if (!packageName || packageName === "killeros") continue;
|
|
112
|
+
capabilities.set(packageName, { label: capabilityLabel(packageName), version: metadata.version });
|
|
169
113
|
}
|
|
170
|
-
|
|
171
|
-
const before = Math.min(3, fill);
|
|
172
|
-
const after = fill - before;
|
|
173
|
-
return `${brand(left)}${brand("─".repeat(before))} ${clippedLabel} ${brand("─".repeat(after))}${brand(right)}`;
|
|
114
|
+
return [...capabilities.values()].sort((left, right) => left.label.localeCompare(right.label));
|
|
174
115
|
}
|
|
175
116
|
|
|
176
|
-
function
|
|
117
|
+
function alignEdges(left: string, right: string, width: number): string {
|
|
177
118
|
if (width <= 0) return "";
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
return `${padRight(left, leftWidth)} ${brand("│")} ${padRight(right, rightWidth)}`;
|
|
119
|
+
const clippedRight = truncateToWidth(right, width, "");
|
|
120
|
+
const leftWidth = Math.max(0, width - visibleWidth(clippedRight) - 1);
|
|
121
|
+
const clippedLeft = truncateToWidth(left, leftWidth, "…");
|
|
122
|
+
const gap = " ".repeat(Math.max(1, width - visibleWidth(clippedLeft) - visibleWidth(clippedRight)));
|
|
123
|
+
return truncateToWidth(`${clippedLeft}${gap}${clippedRight}`, width, "");
|
|
184
124
|
}
|
|
185
125
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
"Shortcuts & Commands",
|
|
190
|
-
"/variants — model reasoning",
|
|
191
|
-
"/compact — compress context",
|
|
192
|
-
"/model — choose a model",
|
|
193
|
-
"────────────────────────",
|
|
194
|
-
"Keybindings",
|
|
195
|
-
"Shift+Enter — new line",
|
|
196
|
-
"Esc — cancel generation",
|
|
197
|
-
"Ctrl+C — interrupt agent",
|
|
198
|
-
],
|
|
199
|
-
[
|
|
200
|
-
"",
|
|
201
|
-
"Session",
|
|
202
|
-
"/new — start a session",
|
|
203
|
-
"/name — name this session",
|
|
204
|
-
"/session — usage and stats",
|
|
205
|
-
"────────────────────────",
|
|
206
|
-
"Workflow",
|
|
207
|
-
"Give Pi a goal and constraints",
|
|
208
|
-
"Ask it to run the relevant tests",
|
|
209
|
-
"Review changes before committing",
|
|
210
|
-
],
|
|
211
|
-
[
|
|
212
|
-
"",
|
|
213
|
-
"Useful Commands",
|
|
214
|
-
"/copy — copy last response",
|
|
215
|
-
"/tree — navigate branches",
|
|
216
|
-
"/reload — reload resources",
|
|
217
|
-
"────────────────────────",
|
|
218
|
-
"Extension locations",
|
|
219
|
-
"Global: ~/.pi/agent/extensions",
|
|
220
|
-
"Project: .pi/extensions",
|
|
221
|
-
"Reload after making changes",
|
|
222
|
-
],
|
|
223
|
-
[
|
|
224
|
-
"",
|
|
225
|
-
"Navigation",
|
|
226
|
-
"Up/Down — command history",
|
|
227
|
-
"Tab — autocomplete",
|
|
228
|
-
"Ctrl+L — clear the screen",
|
|
229
|
-
"────────────────────────",
|
|
230
|
-
"Good defaults",
|
|
231
|
-
"Keep edits scoped",
|
|
232
|
-
"Test after refactoring",
|
|
233
|
-
"Verify output before committing",
|
|
234
|
-
],
|
|
235
|
-
] as const;
|
|
236
|
-
|
|
237
|
-
function getTipLines(index: number, theme: Theme): string[] {
|
|
238
|
-
const selected = TIP_SETS[index % TIP_SETS.length] ?? TIP_SETS[0];
|
|
239
|
-
return selected.map((line, lineIndex) => {
|
|
240
|
-
if (lineIndex === 1 || lineIndex === 6) return brand(theme.bold(line));
|
|
241
|
-
if (line.startsWith("─")) return brand(line);
|
|
242
|
-
if (line.startsWith("/")) {
|
|
243
|
-
const [command, ...rest] = line.split(" ");
|
|
244
|
-
return `${theme.fg("accent", command ?? "")}${theme.fg("dim", ` ${rest.join(" ")}`)}`;
|
|
245
|
-
}
|
|
246
|
-
return theme.fg(lineIndex > 6 ? "muted" : "dim", line);
|
|
247
|
-
});
|
|
126
|
+
function compactBoxLine(content: string, width: number, theme: Theme): string {
|
|
127
|
+
if (width < 4) return truncateToWidth(content, width, "");
|
|
128
|
+
return `${theme.fg("dim", "│")} ${padRight(content, width - 4)} ${theme.fg("dim", "│")}`;
|
|
248
129
|
}
|
|
249
130
|
|
|
250
131
|
class PiStartupHeader {
|
|
251
|
-
private
|
|
252
|
-
private
|
|
253
|
-
private
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
private readonly ctx: ExtensionContext,
|
|
260
|
-
private readonly tui: TUI,
|
|
261
|
-
) {
|
|
262
|
-
this.animationTimer = setInterval(() => {
|
|
263
|
-
if (this.disposed) return;
|
|
264
|
-
if (this.frame >= LOGO_FRAMES.length - 1) {
|
|
265
|
-
this.stopAnimation();
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
this.frame += 1;
|
|
269
|
-
this.tui.requestRender();
|
|
270
|
-
if (this.frame >= LOGO_FRAMES.length - 1) this.stopAnimation();
|
|
271
|
-
}, LOGO_ANIMATION_INTERVAL_MS);
|
|
272
|
-
this.animationTimer.unref?.();
|
|
273
|
-
|
|
274
|
-
this.tipTimer = setInterval(() => {
|
|
275
|
-
if (this.disposed) return;
|
|
276
|
-
this.tipIndex = (this.tipIndex + 1) % TIP_SETS.length;
|
|
277
|
-
this.tui.requestRender();
|
|
278
|
-
}, TIP_ROTATION_INTERVAL_MS);
|
|
279
|
-
this.tipTimer.unref?.();
|
|
132
|
+
private readonly pi: ExtensionAPI;
|
|
133
|
+
private readonly ctx: ExtensionContext;
|
|
134
|
+
private readonly capabilities: CompactCapability[];
|
|
135
|
+
|
|
136
|
+
constructor(pi: ExtensionAPI, ctx: ExtensionContext) {
|
|
137
|
+
this.pi = pi;
|
|
138
|
+
this.ctx = ctx;
|
|
139
|
+
this.capabilities = collectCompactCapabilities(pi);
|
|
280
140
|
}
|
|
281
141
|
|
|
282
|
-
private
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
142
|
+
private contextText(theme: Theme): string {
|
|
143
|
+
const usage = this.ctx.getContextUsage();
|
|
144
|
+
if (!usage || usage.tokens === null) return theme.fg("dim", "context —");
|
|
145
|
+
const windowSize = usage.contextWindow > 0 ? usage.contextWindow : 128_000;
|
|
146
|
+
const percent = Math.max(0, Math.min(100, Math.round((1 - usage.tokens / windowSize) * 100)));
|
|
147
|
+
return theme.fg(percent < 20 ? "error" : percent <= 50 ? "warning" : "success", `${percent}% context`);
|
|
286
148
|
}
|
|
287
149
|
|
|
288
150
|
render(width: number): string[] {
|
|
289
151
|
if (width <= 0) return [];
|
|
290
152
|
const theme = this.ctx.ui.theme;
|
|
291
|
-
if (width <
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
const
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
const model =
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const modelText = leftWidth < 34 ? `${model} (${effort})` : `${model} with ${effort} effort`;
|
|
308
|
-
const leftLines = [
|
|
309
|
-
...logoLines,
|
|
310
|
-
center(theme.bold("Let's build something great"), leftWidth),
|
|
311
|
-
center(theme.fg("muted", truncateToWidth(modelText, leftWidth, "…")), leftWidth),
|
|
312
|
-
center(theme.fg("dim", truncateToWidth(cwd, leftWidth, "…")), leftWidth),
|
|
153
|
+
if (width < 28) return [truncateToWidth(brand(theme.bold("KillerOS")), width, "")];
|
|
154
|
+
|
|
155
|
+
const panelWidth = Math.min(width, COMPACT_HEADER_MAX_WIDTH);
|
|
156
|
+
const innerWidth = panelWidth - 4;
|
|
157
|
+
const version = KILLEROS_VERSION ? theme.fg("dim", ` ${KILLEROS_VERSION}`) : "";
|
|
158
|
+
const identity = `${brand(theme.bold("› KillerOS"))}${version}`;
|
|
159
|
+
const model = this.ctx.model?.id ?? "default model";
|
|
160
|
+
const agent = `${model} · ${this.pi.getThinkingLevel()}`;
|
|
161
|
+
const directory = formatCwd(this.ctx.cwd);
|
|
162
|
+
const border = (left: string, right: string): string => theme.fg("dim", `${left}${"─".repeat(panelWidth - 2)}${right}`);
|
|
163
|
+
const lines = [
|
|
164
|
+
border("╭", "╮"),
|
|
165
|
+
compactBoxLine(alignEdges(identity, theme.fg("success", "READY"), innerWidth), panelWidth, theme),
|
|
166
|
+
compactBoxLine("", panelWidth, theme),
|
|
167
|
+
compactBoxLine(alignEdges(agent, theme.fg("dim", "/model"), innerWidth), panelWidth, theme),
|
|
168
|
+
compactBoxLine(alignEdges(directory, this.contextText(theme), innerWidth), panelWidth, theme),
|
|
313
169
|
];
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
lines.push(boxedLine(content, width));
|
|
170
|
+
if (this.capabilities.length > 0) {
|
|
171
|
+
lines.push(compactBoxLine(theme.fg("dim", "─".repeat(innerWidth)), panelWidth, theme));
|
|
172
|
+
const capabilityText = this.capabilities
|
|
173
|
+
.map((capability) => `${capability.label}${capability.version ? ` ${capability.version}` : ""}`)
|
|
174
|
+
.join(" · ");
|
|
175
|
+
lines.push(compactBoxLine(theme.fg("dim", capabilityText), panelWidth, theme));
|
|
321
176
|
}
|
|
322
|
-
lines.push(
|
|
323
|
-
return lines
|
|
177
|
+
lines.push(border("╰", "╯"));
|
|
178
|
+
return lines;
|
|
324
179
|
}
|
|
325
180
|
|
|
326
181
|
invalidate(): void {}
|
|
327
|
-
|
|
328
|
-
dispose(): void {
|
|
329
|
-
if (this.disposed) return;
|
|
330
|
-
this.disposed = true;
|
|
331
|
-
this.stopAnimation();
|
|
332
|
-
if (this.tipTimer) {
|
|
333
|
-
clearInterval(this.tipTimer);
|
|
334
|
-
this.tipTimer = undefined;
|
|
335
|
-
}
|
|
336
|
-
}
|
|
182
|
+
dispose(): void {}
|
|
337
183
|
}
|
|
338
184
|
|
|
339
185
|
const ANSI_REGEX = /\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
|
@@ -348,8 +194,11 @@ function isBorderLine(line: string): boolean {
|
|
|
348
194
|
}
|
|
349
195
|
|
|
350
196
|
class PiCodeEditor extends CustomEditor {
|
|
351
|
-
|
|
197
|
+
private readonly appKeybindings: KeybindingsManager;
|
|
198
|
+
|
|
199
|
+
constructor(tui: TUI, theme: EditorTheme, appKeybindings: KeybindingsManager) {
|
|
352
200
|
super(tui, theme, appKeybindings);
|
|
201
|
+
this.appKeybindings = appKeybindings;
|
|
353
202
|
}
|
|
354
203
|
|
|
355
204
|
override handleInput(data: string): void {
|
|
@@ -419,30 +268,51 @@ function reportError(ctx: ExtensionContext, area: string, error: unknown): void
|
|
|
419
268
|
ctx.ui.notify(`${area}: ${message}`, "error");
|
|
420
269
|
}
|
|
421
270
|
|
|
271
|
+
const ACTIVITY_WORDS = ["Brewing", "Pondering", "Tinkering", "Wrangling", "Noodling", "Cooking"] as const;
|
|
272
|
+
|
|
422
273
|
function registerShellUi(pi: ExtensionAPI): void {
|
|
423
274
|
let activeHeader: PiStartupHeader | undefined;
|
|
275
|
+
let activityWordIndex = 0;
|
|
424
276
|
|
|
425
277
|
pi.on("session_start", (_event, ctx) => {
|
|
426
278
|
if (ctx.mode !== "tui") return;
|
|
427
279
|
try {
|
|
428
|
-
ctx.ui.
|
|
280
|
+
ctx.ui.setTheme("killeros");
|
|
281
|
+
ctx.ui.setHeader(() => {
|
|
429
282
|
activeHeader?.dispose();
|
|
430
|
-
activeHeader = new PiStartupHeader(pi, ctx
|
|
283
|
+
activeHeader = new PiStartupHeader(pi, ctx);
|
|
431
284
|
return activeHeader;
|
|
432
285
|
});
|
|
433
286
|
ctx.ui.setWorkingIndicator({
|
|
434
|
-
frames: [
|
|
435
|
-
|
|
287
|
+
frames: [
|
|
288
|
+
ctx.ui.theme.fg("dim", "✻"),
|
|
289
|
+
ctx.ui.theme.fg("muted", "✻"),
|
|
290
|
+
ctx.ui.theme.fg("accent", "✻"),
|
|
291
|
+
ctx.ui.theme.fg("muted", "✻"),
|
|
292
|
+
],
|
|
293
|
+
intervalMs: 180,
|
|
436
294
|
});
|
|
295
|
+
ctx.ui.setHiddenThinkingLabel("└ Thinking…");
|
|
437
296
|
ctx.ui.setEditorComponent((tui, theme, keybindings) => new PiCodeEditor(tui, theme, keybindings));
|
|
438
297
|
} catch (error) {
|
|
439
298
|
reportError(ctx, "Killeros UI failed to initialize", error);
|
|
440
299
|
}
|
|
441
300
|
});
|
|
442
301
|
|
|
302
|
+
pi.on("agent_start", (_event, ctx) => {
|
|
303
|
+
if (ctx.mode !== "tui") return;
|
|
304
|
+
ctx.ui.setWorkingMessage(`${ACTIVITY_WORDS[activityWordIndex]}…`);
|
|
305
|
+
activityWordIndex = (activityWordIndex + 1) % ACTIVITY_WORDS.length;
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
pi.on("agent_end", (_event, ctx) => {
|
|
309
|
+
if (ctx.mode === "tui") ctx.ui.setWorkingMessage();
|
|
310
|
+
});
|
|
311
|
+
|
|
443
312
|
pi.on("session_shutdown", () => {
|
|
444
313
|
activeHeader?.dispose();
|
|
445
314
|
activeHeader = undefined;
|
|
315
|
+
activityWordIndex = 0;
|
|
446
316
|
});
|
|
447
317
|
}
|
|
448
318
|
|
|
@@ -514,7 +384,34 @@ function rememberCustomInput(value: string): void {
|
|
|
514
384
|
}
|
|
515
385
|
|
|
516
386
|
function isPrintableInput(data: string): boolean {
|
|
517
|
-
return data.length > 0 && !/[\u0000-\u001F\u007F]/u.test(data);
|
|
387
|
+
return data.length > 0 && !/[\u0000-\u001F\u007F-\u009F]/u.test(data);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
391
|
+
|
|
392
|
+
function decodeQuestionFilterInput(data: string): string | undefined {
|
|
393
|
+
const kittyPrintable = decodeKittyPrintable(data);
|
|
394
|
+
if (kittyPrintable !== undefined) return isPrintableInput(kittyPrintable) ? kittyPrintable : undefined;
|
|
395
|
+
|
|
396
|
+
const pasteStart = "\x1B[200~";
|
|
397
|
+
const pasteEnd = "\x1B[201~";
|
|
398
|
+
const startIndex = data.indexOf(pasteStart);
|
|
399
|
+
const endIndex = data.indexOf(pasteEnd, startIndex + pasteStart.length);
|
|
400
|
+
if (startIndex >= 0 && endIndex >= 0) {
|
|
401
|
+
return data
|
|
402
|
+
.slice(startIndex + pasteStart.length, endIndex)
|
|
403
|
+
.replace(/\r\n|\r|\n/gu, "")
|
|
404
|
+
.replace(/\t/gu, " ")
|
|
405
|
+
.replace(/[\u0000-\u001F\u007F-\u009F]/gu, "");
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return isPrintableInput(data) ? data : undefined;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function removeLastGrapheme(value: string): string {
|
|
412
|
+
const segments = [...graphemeSegmenter.segment(value)];
|
|
413
|
+
const last = segments.at(-1);
|
|
414
|
+
return last ? value.slice(0, last.index) : "";
|
|
518
415
|
}
|
|
519
416
|
|
|
520
417
|
function registerQuestionTool(pi: ExtensionAPI): void {
|
|
@@ -552,8 +449,6 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
552
449
|
let optionIndex = 0;
|
|
553
450
|
let editMode = false;
|
|
554
451
|
let filterQuery = "";
|
|
555
|
-
let historyIndex = -1;
|
|
556
|
-
let savedDraft = "";
|
|
557
452
|
let cachedWidth: number | undefined;
|
|
558
453
|
let cachedLines: string[] | undefined;
|
|
559
454
|
let completed = false;
|
|
@@ -576,6 +471,7 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
576
471
|
},
|
|
577
472
|
};
|
|
578
473
|
const editor = new Editor(tui, editorTheme);
|
|
474
|
+
customInputHistory.forEach((value) => editor.addToHistory(value));
|
|
579
475
|
|
|
580
476
|
const filteredOptions = (): DisplayOption[] => {
|
|
581
477
|
const query = filterQuery.trim().toLocaleLowerCase();
|
|
@@ -605,15 +501,11 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
605
501
|
}
|
|
606
502
|
editMode = false;
|
|
607
503
|
editor.setText("");
|
|
608
|
-
historyIndex = -1;
|
|
609
|
-
savedDraft = "";
|
|
610
504
|
refresh();
|
|
611
505
|
};
|
|
612
506
|
|
|
613
507
|
const enterCustomMode = (): void => {
|
|
614
508
|
editMode = true;
|
|
615
|
-
historyIndex = -1;
|
|
616
|
-
savedDraft = "";
|
|
617
509
|
refresh();
|
|
618
510
|
};
|
|
619
511
|
|
|
@@ -622,30 +514,6 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
622
514
|
if (matchesKey(data, Key.escape)) {
|
|
623
515
|
editMode = false;
|
|
624
516
|
editor.setText("");
|
|
625
|
-
historyIndex = -1;
|
|
626
|
-
savedDraft = "";
|
|
627
|
-
refresh();
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
if (matchesKey(data, Key.up) && customInputHistory.length > 0) {
|
|
631
|
-
if (historyIndex < 0) {
|
|
632
|
-
savedDraft = editor.getText();
|
|
633
|
-
historyIndex = customInputHistory.length - 1;
|
|
634
|
-
} else if (historyIndex > 0) {
|
|
635
|
-
historyIndex -= 1;
|
|
636
|
-
}
|
|
637
|
-
editor.setText(customInputHistory[historyIndex] ?? "");
|
|
638
|
-
refresh();
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
if (matchesKey(data, Key.down) && historyIndex >= 0) {
|
|
642
|
-
if (historyIndex < customInputHistory.length - 1) {
|
|
643
|
-
historyIndex += 1;
|
|
644
|
-
editor.setText(customInputHistory[historyIndex] ?? "");
|
|
645
|
-
} else {
|
|
646
|
-
historyIndex = -1;
|
|
647
|
-
editor.setText(savedDraft);
|
|
648
|
-
}
|
|
649
517
|
refresh();
|
|
650
518
|
return;
|
|
651
519
|
}
|
|
@@ -685,21 +553,23 @@ function registerQuestionTool(pi: ExtensionAPI): void {
|
|
|
685
553
|
}
|
|
686
554
|
if (matchesKey(data, Key.backspace)) {
|
|
687
555
|
if (filterQuery) {
|
|
688
|
-
filterQuery =
|
|
556
|
+
filterQuery = removeLastGrapheme(filterQuery);
|
|
689
557
|
optionIndex = 0;
|
|
690
558
|
refresh();
|
|
691
559
|
}
|
|
692
560
|
return;
|
|
693
561
|
}
|
|
694
|
-
|
|
695
|
-
|
|
562
|
+
const printableInput = decodeQuestionFilterInput(data);
|
|
563
|
+
const isPasteInput = data.includes("\x1B[200~");
|
|
564
|
+
if (!isPasteInput && printableInput && /^[1-9]$/.test(printableInput)) {
|
|
565
|
+
const selected = visibleOptions[Number(printableInput) - 1];
|
|
696
566
|
if (!selected) return;
|
|
697
567
|
if (selected.isOther) enterCustomMode();
|
|
698
568
|
else finish({ kind: "selected", answer: selected.label, originalIndex: selected.originalIndex });
|
|
699
569
|
return;
|
|
700
570
|
}
|
|
701
|
-
if (
|
|
702
|
-
filterQuery +=
|
|
571
|
+
if (printableInput) {
|
|
572
|
+
filterQuery += printableInput;
|
|
703
573
|
optionIndex = 0;
|
|
704
574
|
refresh();
|
|
705
575
|
}
|
|
@@ -848,7 +718,7 @@ function registerAliases(pi: ExtensionAPI): void {
|
|
|
848
718
|
await ctx.newSession();
|
|
849
719
|
};
|
|
850
720
|
pi.registerCommand("clear", { description: "Start a new session after confirmation", handler: startNewSession });
|
|
851
|
-
pi.registerCommand("
|
|
721
|
+
pi.registerCommand("exit", {
|
|
852
722
|
description: "Quit Pi gracefully",
|
|
853
723
|
handler: async (_args, ctx) => ctx.shutdown(),
|
|
854
724
|
});
|
|
@@ -891,13 +761,9 @@ const COMMAND_SYNTAX_HINTS: Readonly<Record<string, string>> = {
|
|
|
891
761
|
model: "/model [provider/model]",
|
|
892
762
|
"scoped-models": "/scoped-models",
|
|
893
763
|
login: "/login [provider]",
|
|
894
|
-
logout: "/logout [provider]",
|
|
895
764
|
export: "/export [filename]",
|
|
896
765
|
import: "/import [path]",
|
|
897
766
|
name: "/name [session-name]",
|
|
898
|
-
fork: "/fork [name]",
|
|
899
|
-
clone: "/clone [name]",
|
|
900
|
-
resume: "/resume [session-id]",
|
|
901
767
|
};
|
|
902
768
|
|
|
903
769
|
interface TaggedAutocompleteItem extends AutocompleteItem {
|
|
@@ -1173,9 +1039,12 @@ export function formatContextProgress(tokensUsed: number | null, contextWindow:
|
|
|
1173
1039
|
|
|
1174
1040
|
function sumSessionCost(ctx: ExtensionContext): number {
|
|
1175
1041
|
let total = 0;
|
|
1176
|
-
for (const entry of ctx.sessionManager.
|
|
1177
|
-
if (entry.type === "message" && entry.message.role === "assistant")
|
|
1042
|
+
for (const entry of ctx.sessionManager.getEntries()) {
|
|
1043
|
+
if (entry.type === "message" && entry.message.role === "assistant") total += entry.message.usage.cost.total;
|
|
1044
|
+
else if (entry.type === "message" && entry.message.role === "toolResult" && entry.message.usage) {
|
|
1178
1045
|
total += entry.message.usage.cost.total;
|
|
1046
|
+
} else if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage) {
|
|
1047
|
+
total += entry.usage.cost.total;
|
|
1179
1048
|
}
|
|
1180
1049
|
}
|
|
1181
1050
|
return total;
|
package/README.md
CHANGED
|
@@ -14,8 +14,6 @@ The extension is strict TypeScript and uses only packages provided by Pi.
|
|
|
14
14
|
|
|
15
15
|
### npm
|
|
16
16
|
|
|
17
|
-
After the first npm release:
|
|
18
|
-
|
|
19
17
|
```bash
|
|
20
18
|
pi install npm:killeros
|
|
21
19
|
```
|
|
@@ -31,20 +29,22 @@ pi install git:github.com/KyrosHendrix/pi-KillerOS
|
|
|
31
29
|
Pin an install to a release:
|
|
32
30
|
|
|
33
31
|
```bash
|
|
34
|
-
pi install git:github.com/KyrosHendrix/pi-KillerOS@v1.
|
|
32
|
+
pi install git:github.com/KyrosHendrix/pi-KillerOS@v1.1.0
|
|
35
33
|
```
|
|
36
34
|
|
|
37
35
|
Add `-l` to either command for a project-only install. Restart Pi after installing.
|
|
38
36
|
|
|
39
37
|
## Features
|
|
40
38
|
|
|
41
|
-
-
|
|
39
|
+
- Compact KillerOS startup card with model, directory, context, and loaded capability state
|
|
40
|
+
- Cohesive dark theme with coral accents and neutral tool-call containers across pending, success, and error states
|
|
41
|
+
- Coral Spark activity indicator with Claude-adjacent verbs that advance between agent runs and a quiet hidden-thinking label
|
|
42
42
|
- Framed multiline editor with Shift+Enter support
|
|
43
43
|
- Footer with model, reasoning, context remaining, Git branch, elapsed time, and cost
|
|
44
44
|
- `/variants` selector and direct reasoning-level arguments
|
|
45
45
|
- `question` tool with filtering, keyboard selection, custom answers, history, cancellation, and resize-safe rendering
|
|
46
46
|
- Mid-prompt slash completion with current Pi `0.82.1` commands, extensions, prompts, and skills
|
|
47
|
-
- `/clear` for a confirmed new session, plus `/
|
|
47
|
+
- `/clear` for a confirmed new session, plus `/exit` for graceful shutdown
|
|
48
48
|
- Concise system-prompt guidance without modifying completed assistant messages
|
|
49
49
|
|
|
50
50
|
## Commands
|
|
@@ -53,13 +53,15 @@ Add `-l` to either command for a project-only install. Restart Pi after installi
|
|
|
53
53
|
/variants Open the reasoning-level selector
|
|
54
54
|
/variants high Set a reasoning level directly
|
|
55
55
|
/clear Start a new session after confirmation
|
|
56
|
-
/
|
|
56
|
+
/exit Quit Pi gracefully
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
Supported reasoning levels are `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, and `max`. KillerOS limits choices to levels supported by the current model.
|
|
60
60
|
|
|
61
61
|
## Configuration
|
|
62
62
|
|
|
63
|
+
KillerOS activates its packaged `killeros` theme when a TUI session starts. Tool-call backgrounds stay neutral across pending, successful, and failed states; restrained text and icons preserve status visibility.
|
|
64
|
+
|
|
63
65
|
KillerOS displays provider costs in USD.
|
|
64
66
|
|
|
65
67
|
Set a custom footer shortcut hint with:
|
|
@@ -83,8 +85,9 @@ Before release, run:
|
|
|
83
85
|
```bash
|
|
84
86
|
npm ci
|
|
85
87
|
npm run check
|
|
88
|
+
npm test
|
|
86
89
|
npm pack --dry-run
|
|
87
|
-
pi -e . --mode rpc
|
|
90
|
+
pi -ne -e . --mode rpc
|
|
88
91
|
```
|
|
89
92
|
|
|
90
93
|
The package manifest lists Pi’s built-in modules as peer dependencies, so npm does not bundle a second copy.
|
|
@@ -93,16 +96,13 @@ The package manifest lists Pi’s built-in modules as peer dependencies, so npm
|
|
|
93
96
|
|
|
94
97
|
The [`pi-package`](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/packages.md) keyword makes a published npm release visible in Pi’s package catalog.
|
|
95
98
|
|
|
99
|
+
Choose `patch`, `minor`, or `major` for the release, then publish and push the version commit and tag:
|
|
100
|
+
|
|
96
101
|
```bash
|
|
97
102
|
npm login
|
|
103
|
+
npm version patch
|
|
98
104
|
npm publish
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
Create and push a matching Git tag after publication:
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
git tag v1.0.0
|
|
105
|
-
git push origin main v1.0.0
|
|
105
|
+
git push origin main --follow-tags
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
## Security
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "killeros",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A production-hardened TUI and workflow extension for the Pi coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -21,17 +21,23 @@
|
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"Killeros.ts",
|
|
24
|
-
"
|
|
24
|
+
"themes/killeros.json",
|
|
25
|
+
"README.md",
|
|
26
|
+
"CHANGELOG.md"
|
|
25
27
|
],
|
|
26
28
|
"engines": {
|
|
27
29
|
"node": ">=22.19.0"
|
|
28
30
|
},
|
|
29
31
|
"scripts": {
|
|
30
|
-
"check": "tsc --noEmit"
|
|
32
|
+
"check": "tsc --noEmit",
|
|
33
|
+
"test": "node --test --experimental-strip-types test/*.test.js"
|
|
31
34
|
},
|
|
32
35
|
"pi": {
|
|
33
36
|
"extensions": [
|
|
34
37
|
"./Killeros.ts"
|
|
38
|
+
],
|
|
39
|
+
"themes": [
|
|
40
|
+
"./themes/killeros.json"
|
|
35
41
|
]
|
|
36
42
|
},
|
|
37
43
|
"peerDependencies": {
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
|
3
|
+
"name": "killeros",
|
|
4
|
+
"vars": {
|
|
5
|
+
"coral": "#d77757",
|
|
6
|
+
"coralBright": "#e58b6d",
|
|
7
|
+
"canvas": "#090b0e",
|
|
8
|
+
"surface": "#101419",
|
|
9
|
+
"surfaceRaised": "#151a20",
|
|
10
|
+
"line": "#39424b",
|
|
11
|
+
"lineMuted": "#2b333b",
|
|
12
|
+
"text": "#dce1e5",
|
|
13
|
+
"muted": "#8f99a3",
|
|
14
|
+
"dim": "#606b75",
|
|
15
|
+
"success": "#8fa88b",
|
|
16
|
+
"error": "#c8786c",
|
|
17
|
+
"warning": "#bda36c",
|
|
18
|
+
"pink": "#b98aa5"
|
|
19
|
+
},
|
|
20
|
+
"colors": {
|
|
21
|
+
"accent": "coral",
|
|
22
|
+
"border": "line",
|
|
23
|
+
"borderAccent": "coral",
|
|
24
|
+
"borderMuted": "lineMuted",
|
|
25
|
+
"success": "success",
|
|
26
|
+
"error": "error",
|
|
27
|
+
"warning": "warning",
|
|
28
|
+
"muted": "muted",
|
|
29
|
+
"dim": "dim",
|
|
30
|
+
"text": "text",
|
|
31
|
+
"thinkingText": "muted",
|
|
32
|
+
|
|
33
|
+
"selectedBg": "surfaceRaised",
|
|
34
|
+
"userMessageBg": "surface",
|
|
35
|
+
"userMessageText": "text",
|
|
36
|
+
"customMessageBg": "surface",
|
|
37
|
+
"customMessageText": "text",
|
|
38
|
+
"customMessageLabel": "coralBright",
|
|
39
|
+
"toolPendingBg": "surface",
|
|
40
|
+
"toolSuccessBg": "surface",
|
|
41
|
+
"toolErrorBg": "surface",
|
|
42
|
+
"toolTitle": "coralBright",
|
|
43
|
+
"toolOutput": "muted",
|
|
44
|
+
|
|
45
|
+
"mdHeading": "coralBright",
|
|
46
|
+
"mdLink": "coralBright",
|
|
47
|
+
"mdLinkUrl": "dim",
|
|
48
|
+
"mdCode": "coralBright",
|
|
49
|
+
"mdCodeBlock": "text",
|
|
50
|
+
"mdCodeBlockBorder": "lineMuted",
|
|
51
|
+
"mdQuote": "muted",
|
|
52
|
+
"mdQuoteBorder": "line",
|
|
53
|
+
"mdHr": "line",
|
|
54
|
+
"mdListBullet": "coral",
|
|
55
|
+
|
|
56
|
+
"toolDiffAdded": "success",
|
|
57
|
+
"toolDiffRemoved": "error",
|
|
58
|
+
"toolDiffContext": "muted",
|
|
59
|
+
|
|
60
|
+
"syntaxComment": "dim",
|
|
61
|
+
"syntaxKeyword": "coralBright",
|
|
62
|
+
"syntaxFunction": "#c9a48d",
|
|
63
|
+
"syntaxVariable": "#b7c0c8",
|
|
64
|
+
"syntaxString": "#a9b78d",
|
|
65
|
+
"syntaxNumber": "#c5a77a",
|
|
66
|
+
"syntaxType": "#a3afb8",
|
|
67
|
+
"syntaxOperator": "muted",
|
|
68
|
+
"syntaxPunctuation": "muted",
|
|
69
|
+
|
|
70
|
+
"thinkingOff": "dim",
|
|
71
|
+
"thinkingMinimal": "#78685f",
|
|
72
|
+
"thinkingLow": "#98705f",
|
|
73
|
+
"thinkingMedium": "#b27762",
|
|
74
|
+
"thinkingHigh": "coral",
|
|
75
|
+
"thinkingXhigh": "#d58272",
|
|
76
|
+
"thinkingMax": "pink",
|
|
77
|
+
|
|
78
|
+
"bashMode": "warning"
|
|
79
|
+
},
|
|
80
|
+
"export": {
|
|
81
|
+
"pageBg": "#090b0e",
|
|
82
|
+
"cardBg": "#101419",
|
|
83
|
+
"infoBg": "#151a20"
|
|
84
|
+
}
|
|
85
|
+
}
|