killeros 1.0.1 → 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 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,
@@ -28,30 +30,25 @@ import {
28
30
  import { Type } from "typebox";
29
31
 
30
32
  const BRAND_RGB = "215;119;87";
31
- const LEFT_PANEL_WIDTH = 42;
32
- const LOGO_CELL = "███";
33
- const LOGO_ANIMATION_INTERVAL_MS = 120;
34
- const TIP_ROTATION_INTERVAL_MS = 5_000;
35
33
  const FOOTER_REFRESH_INTERVAL_MS = 1_000;
34
+ const COMPACT_HEADER_MAX_WIDTH = 76;
36
35
 
37
36
  const brand = (text: string): string => `\x1B[38;2;${BRAND_RGB}m${text}\x1B[39m`;
38
37
 
39
- const ORCA_ART = [
40
- ".....g......",
41
- "....ggg.....",
42
- ".gggggggg..g",
43
- "gbggwwgggggg",
44
- ".ggwwgggg..g",
45
- "...gggg.....",
46
- ".....gg.....",
47
- ] as const;
48
- const ORCA_WIDTH = ORCA_ART[0].length;
49
- const LOGO_FRAME_COUNT = ORCA_WIDTH + 3;
50
-
51
- function extractProvider(model: ExtensionContext["model"]): string {
52
- return model?.provider ?? "";
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
+ }
53
48
  }
54
49
 
50
+ const KILLEROS_VERSION = readPackageMetadata(new URL("./package.json", import.meta.url)).version;
51
+
55
52
  function formatCwd(cwd: string): string {
56
53
  const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
57
54
  if (!home) return cwd;
@@ -64,226 +61,125 @@ function formatCwd(cwd: string): string {
64
61
  : cwd;
65
62
  }
66
63
 
67
- function center(text: string, width: number): string {
68
- if (width <= 0) return "";
69
- const textWidth = visibleWidth(text);
70
- if (textWidth >= width) return truncateToWidth(text, width, "");
71
- return `${" ".repeat(Math.floor((width - textWidth) / 2))}${text}`;
72
- }
73
-
74
64
  function padRight(text: string, width: number): string {
75
65
  if (width <= 0) return "";
76
66
  const clipped = truncateToWidth(text, width, "");
77
67
  return clipped + " ".repeat(Math.max(0, width - visibleWidth(clipped)));
78
68
  }
79
69
 
80
- type LogoColor = "body" | "white" | "brand" | "panel";
70
+ interface CapabilitySourceInfo {
71
+ path?: string;
72
+ source?: string;
73
+ baseDir?: string;
74
+ }
81
75
 
82
- function colorCell(color: LogoColor): string {
83
- switch (color) {
84
- case "body": return `\x1B[90m${LOGO_CELL}\x1B[39m`;
85
- case "white": return `\x1B[97m${LOGO_CELL}\x1B[39m`;
86
- case "brand": return brand(LOGO_CELL);
87
- default: return " ".repeat(LOGO_CELL.length);
88
- }
76
+ interface CompactCapability {
77
+ label: string;
78
+ version?: string;
89
79
  }
90
80
 
91
- function piLogoFrame(frameIndex: number): string[] {
92
- const visibleColumns = Math.min(ORCA_WIDTH, frameIndex + 1);
93
- const eyeColor: LogoColor = frameIndex === ORCA_WIDTH ? "white" : "brand";
94
- return ORCA_ART.map((row) => [...row].map((cell, index) => {
95
- if (index >= visibleColumns) return colorCell("panel");
96
- if (cell === "g") return colorCell("body");
97
- if (cell === "w") return colorCell("white");
98
- if (cell === "b") return colorCell(eyeColor);
99
- return colorCell("panel");
100
- }).join(""));
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());
101
86
  }
102
87
 
103
- function borderLine(left: string, label: string, right: string, width: number): string {
104
- if (width <= 0) return "";
105
- if (width === 1) return brand(truncateToWidth(left, 1, ""));
106
- const edgeWidth = visibleWidth(left) + visibleWidth(right);
107
- if (width <= edgeWidth) return brand(truncateToWidth(left + right, width, ""));
108
- const available = width - edgeWidth;
109
- const clippedLabel = truncateToWidth(label, Math.max(0, available - 2), "");
110
- const labelWidth = visibleWidth(clippedLabel);
111
- if (labelWidth === 0 || available < labelWidth + 2) {
112
- return `${brand(left)}${brand("".repeat(available))}${brand(right)}`;
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 });
113
113
  }
114
- const fill = available - labelWidth - 2;
115
- const before = Math.min(3, fill);
116
- const after = fill - before;
117
- return `${brand(left)}${brand("─".repeat(before))} ${clippedLabel} ${brand("─".repeat(after))}${brand(right)}`;
114
+ return [...capabilities.values()].sort((left, right) => left.label.localeCompare(right.label));
118
115
  }
119
116
 
120
- function boxedLine(content: string, width: number): string {
117
+ function alignEdges(left: string, right: string, width: number): string {
121
118
  if (width <= 0) return "";
122
- if (width <= 2) return truncateToWidth(content, width, "");
123
- return `${brand("│")}${padRight(content, width - 2)}${brand("│")}`;
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, "");
124
124
  }
125
125
 
126
- function twoColumn(left: string, right: string, leftWidth: number, rightWidth: number): string {
127
- return `${padRight(left, leftWidth)} ${brand("│")} ${padRight(right, rightWidth)}`;
128
- }
129
-
130
- const TIP_SETS = [
131
- [
132
- "",
133
- "Shortcuts & Commands",
134
- "/variants — model reasoning",
135
- "/compact — compress context",
136
- "/model — choose a model",
137
- "────────────────────────",
138
- "Keybindings",
139
- "Shift+Enter — new line",
140
- "Esc — cancel generation",
141
- "Ctrl+C — interrupt agent",
142
- ],
143
- [
144
- "",
145
- "Session",
146
- "/new — start a session",
147
- "/name — name this session",
148
- "/session — usage and stats",
149
- "────────────────────────",
150
- "Workflow",
151
- "Give Pi a goal and constraints",
152
- "Ask it to run the relevant tests",
153
- "Review changes before committing",
154
- ],
155
- [
156
- "",
157
- "Useful Commands",
158
- "/copy — copy last response",
159
- "/tree — navigate branches",
160
- "/reload — reload resources",
161
- "────────────────────────",
162
- "Extension locations",
163
- "Global: ~/.pi/agent/extensions",
164
- "Project: .pi/extensions",
165
- "Reload after making changes",
166
- ],
167
- [
168
- "",
169
- "Navigation",
170
- "Up/Down — command history",
171
- "Tab — autocomplete",
172
- "Ctrl+L — clear the screen",
173
- "────────────────────────",
174
- "Good defaults",
175
- "Keep edits scoped",
176
- "Test after refactoring",
177
- "Verify output before committing",
178
- ],
179
- ] as const;
180
-
181
- function getTipLines(index: number, theme: Theme): string[] {
182
- const selected = TIP_SETS[index % TIP_SETS.length] ?? TIP_SETS[0];
183
- return selected.map((line, lineIndex) => {
184
- if (lineIndex === 1 || lineIndex === 6) return brand(theme.bold(line));
185
- if (line.startsWith("─")) return brand(line);
186
- if (line.startsWith("/")) {
187
- const [command, ...rest] = line.split(" ");
188
- return `${theme.fg("accent", command ?? "")}${theme.fg("dim", ` ${rest.join(" ")}`)}`;
189
- }
190
- return theme.fg(lineIndex > 6 ? "muted" : "dim", line);
191
- });
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", "│")}`;
192
129
  }
193
130
 
194
131
  class PiStartupHeader {
195
132
  private readonly pi: ExtensionAPI;
196
133
  private readonly ctx: ExtensionContext;
197
- private readonly tui: TUI;
198
- private frame = 0;
199
- private tipIndex = 0;
200
- private animationTimer?: ReturnType<typeof setInterval>;
201
- private tipTimer?: ReturnType<typeof setInterval>;
202
- private disposed = false;
203
-
204
- constructor(
205
- pi: ExtensionAPI,
206
- ctx: ExtensionContext,
207
- tui: TUI,
208
- ) {
134
+ private readonly capabilities: CompactCapability[];
135
+
136
+ constructor(pi: ExtensionAPI, ctx: ExtensionContext) {
209
137
  this.pi = pi;
210
138
  this.ctx = ctx;
211
- this.tui = tui;
212
- this.animationTimer = setInterval(() => {
213
- if (this.disposed) return;
214
- if (this.frame >= LOGO_FRAME_COUNT - 1) {
215
- this.stopAnimation();
216
- return;
217
- }
218
- this.frame += 1;
219
- this.tui.requestRender();
220
- if (this.frame >= LOGO_FRAME_COUNT - 1) this.stopAnimation();
221
- }, LOGO_ANIMATION_INTERVAL_MS);
222
- this.animationTimer.unref?.();
223
-
224
- this.tipTimer = setInterval(() => {
225
- if (this.disposed) return;
226
- this.tipIndex = (this.tipIndex + 1) % TIP_SETS.length;
227
- this.tui.requestRender();
228
- }, TIP_ROTATION_INTERVAL_MS);
229
- this.tipTimer.unref?.();
139
+ this.capabilities = collectCompactCapabilities(pi);
230
140
  }
231
141
 
232
- private stopAnimation(): void {
233
- if (!this.animationTimer) return;
234
- clearInterval(this.animationTimer);
235
- this.animationTimer = undefined;
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`);
236
148
  }
237
149
 
238
150
  render(width: number): string[] {
239
151
  if (width <= 0) return [];
240
152
  const theme = this.ctx.ui.theme;
241
- if (width < 16) return [truncateToWidth(theme.fg("accent", `Pi v${VERSION}`), width, "")];
242
-
243
- const innerWidth = width - 2;
244
- const isTwoColumn = innerWidth >= 64;
245
- const provider = extractProvider(this.ctx.model);
246
- const rawModel = this.ctx.model?.id ?? this.ctx.model?.name;
247
- const model = rawModel
248
- ? provider && !rawModel.startsWith(`${provider}/`) ? `${provider}/${rawModel}` : rawModel
249
- : "Default model";
250
- const effort = this.pi.getThinkingLevel();
251
- const cwd = formatCwd(this.ctx.cwd);
252
- const leftWidth = isTwoColumn ? Math.min(LEFT_PANEL_WIDTH, Math.floor(innerWidth * 0.55)) : innerWidth;
253
- const rightWidth = isTwoColumn ? innerWidth - leftWidth - 3 : 0;
254
- const logoLines = leftWidth >= ORCA_WIDTH * visibleWidth(LOGO_CELL)
255
- ? piLogoFrame(this.frame).map((line) => center(line, leftWidth))
256
- : ["", "", center(brand(theme.bold("Pi Coding Agent")), leftWidth), "", "", "", ""];
257
- const modelText = leftWidth < 34 ? `${model} (${effort})` : `${model} with ${effort} effort`;
258
- const leftLines = [
259
- ...logoLines,
260
- center(theme.bold("Let's build something great"), leftWidth),
261
- center(theme.fg("muted", truncateToWidth(modelText, leftWidth, "…")), leftWidth),
262
- 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),
263
169
  ];
264
- const tipLines = isTwoColumn ? getTipLines(this.tipIndex, theme) : [];
265
- const lines = [borderLine("", `${brand("Pi")} v${VERSION}`, "╮", width)];
266
- for (let index = 0; index < leftLines.length; index += 1) {
267
- const content = isTwoColumn
268
- ? twoColumn(leftLines[index] ?? "", tipLines[index] ?? "", leftWidth, rightWidth)
269
- : padRight(leftLines[index] ?? "", leftWidth);
270
- 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));
271
176
  }
272
- lines.push(borderLine("╰", "", "╯", width));
273
- return lines.map((line) => truncateToWidth(line, width, ""));
177
+ lines.push(border("╰", "╯"));
178
+ return lines;
274
179
  }
275
180
 
276
181
  invalidate(): void {}
277
-
278
- dispose(): void {
279
- if (this.disposed) return;
280
- this.disposed = true;
281
- this.stopAnimation();
282
- if (this.tipTimer) {
283
- clearInterval(this.tipTimer);
284
- this.tipTimer = undefined;
285
- }
286
- }
182
+ dispose(): void {}
287
183
  }
288
184
 
289
185
  const ANSI_REGEX = /\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
@@ -372,30 +268,51 @@ function reportError(ctx: ExtensionContext, area: string, error: unknown): void
372
268
  ctx.ui.notify(`${area}: ${message}`, "error");
373
269
  }
374
270
 
271
+ const ACTIVITY_WORDS = ["Brewing", "Pondering", "Tinkering", "Wrangling", "Noodling", "Cooking"] as const;
272
+
375
273
  function registerShellUi(pi: ExtensionAPI): void {
376
274
  let activeHeader: PiStartupHeader | undefined;
275
+ let activityWordIndex = 0;
377
276
 
378
277
  pi.on("session_start", (_event, ctx) => {
379
278
  if (ctx.mode !== "tui") return;
380
279
  try {
381
- ctx.ui.setHeader((tui) => {
280
+ ctx.ui.setTheme("killeros");
281
+ ctx.ui.setHeader(() => {
382
282
  activeHeader?.dispose();
383
- activeHeader = new PiStartupHeader(pi, ctx, tui);
283
+ activeHeader = new PiStartupHeader(pi, ctx);
384
284
  return activeHeader;
385
285
  });
386
286
  ctx.ui.setWorkingIndicator({
387
- frames: ["◐", "◓", "◑", "◒"].map((frame) => ctx.ui.theme.fg("accent", frame)),
388
- intervalMs: 120,
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,
389
294
  });
295
+ ctx.ui.setHiddenThinkingLabel("└ Thinking…");
390
296
  ctx.ui.setEditorComponent((tui, theme, keybindings) => new PiCodeEditor(tui, theme, keybindings));
391
297
  } catch (error) {
392
298
  reportError(ctx, "Killeros UI failed to initialize", error);
393
299
  }
394
300
  });
395
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
+
396
312
  pi.on("session_shutdown", () => {
397
313
  activeHeader?.dispose();
398
314
  activeHeader = undefined;
315
+ activityWordIndex = 0;
399
316
  });
400
317
  }
401
318
 
package/README.md CHANGED
@@ -29,14 +29,16 @@ pi install git:github.com/KyrosHendrix/pi-KillerOS
29
29
  Pin an install to a release:
30
30
 
31
31
  ```bash
32
- pi install git:github.com/KyrosHendrix/pi-KillerOS@v1.0.0
32
+ pi install git:github.com/KyrosHendrix/pi-KillerOS@v1.1.0
33
33
  ```
34
34
 
35
35
  Add `-l` to either command for a project-only install. Restart Pi after installing.
36
36
 
37
37
  ## Features
38
38
 
39
- - Animated startup header with current model, reasoning level, and working-directory context
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
40
42
  - Framed multiline editor with Shift+Enter support
41
43
  - Footer with model, reasoning, context remaining, Git branch, elapsed time, and cost
42
44
  - `/variants` selector and direct reasoning-level arguments
@@ -58,6 +60,8 @@ Supported reasoning levels are `off`, `minimal`, `low`, `medium`, `high`, `xhigh
58
60
 
59
61
  ## Configuration
60
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
+
61
65
  KillerOS displays provider costs in USD.
62
66
 
63
67
  Set a custom footer shortcut hint with:
@@ -83,7 +87,7 @@ npm ci
83
87
  npm run check
84
88
  npm test
85
89
  npm pack --dry-run
86
- pi -e . --mode rpc
90
+ pi -ne -e . --mode rpc
87
91
  ```
88
92
 
89
93
  The package manifest lists Pi’s built-in modules as peer dependencies, so npm does not bundle a second copy.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "killeros",
3
- "version": "1.0.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,7 +21,9 @@
21
21
  },
22
22
  "files": [
23
23
  "Killeros.ts",
24
- "README.md"
24
+ "themes/killeros.json",
25
+ "README.md",
26
+ "CHANGELOG.md"
25
27
  ],
26
28
  "engines": {
27
29
  "node": ">=22.19.0"
@@ -33,6 +35,9 @@
33
35
  "pi": {
34
36
  "extensions": [
35
37
  "./Killeros.ts"
38
+ ],
39
+ "themes": [
40
+ "./themes/killeros.json"
36
41
  ]
37
42
  },
38
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
+ }