codedeck 0.2.0 → 0.4.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 +45 -16
- package/dist/cli/charts/bar-chart.js +104 -0
- package/dist/cli/charts/bar-chart.js.map +1 -0
- package/dist/cli/charts/sparkline.js +18 -0
- package/dist/cli/charts/sparkline.js.map +1 -0
- package/dist/cli/charts/stacked-bar.js +59 -0
- package/dist/cli/charts/stacked-bar.js.map +1 -0
- package/dist/cli/cli-name.js +20 -0
- package/dist/cli/cli-name.js.map +1 -0
- package/dist/cli/commands/claims.js +170 -0
- package/dist/cli/commands/claims.js.map +1 -0
- package/dist/cli/commands/doctor.js +22 -1
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/models.js +5 -5
- package/dist/cli/commands/models.js.map +1 -1
- package/dist/cli/commands/open.js +309 -315
- package/dist/cli/commands/open.js.map +1 -1
- package/dist/cli/commands/ps.js +226 -23
- package/dist/cli/commands/ps.js.map +1 -1
- package/dist/cli/commands/rename.js +29 -0
- package/dist/cli/commands/rename.js.map +1 -0
- package/dist/cli/commands/run.js +50 -18
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/setup.js +989 -72
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/usage.js +192 -0
- package/dist/cli/commands/usage.js.map +1 -0
- package/dist/cli/commands/wait.js +3 -2
- package/dist/cli/commands/wait.js.map +1 -1
- package/dist/cli/commands/web.js +224 -0
- package/dist/cli/commands/web.js.map +1 -0
- package/dist/cli/index.js +36 -21
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/picker-state.js +75 -8
- package/dist/cli/picker-state.js.map +1 -1
- package/dist/cli/picker.js +13 -3
- package/dist/cli/picker.js.map +1 -1
- package/dist/cli/ui.js +54 -4
- package/dist/cli/ui.js.map +1 -1
- package/dist/cli/usage/dashboard.js +330 -0
- package/dist/cli/usage/dashboard.js.map +1 -0
- package/dist/cli/usage/snapshot.js +112 -0
- package/dist/cli/usage/snapshot.js.map +1 -0
- package/dist/config/config.js +326 -5
- package/dist/config/config.js.map +1 -1
- package/dist/config/orchestrator-mode.js +72 -0
- package/dist/config/orchestrator-mode.js.map +1 -0
- package/dist/config/paths.js +28 -9
- package/dist/config/paths.js.map +1 -1
- package/dist/core/autocompact.js +77 -0
- package/dist/core/autocompact.js.map +1 -0
- package/dist/core/driver.js.map +1 -1
- package/dist/core/models.js +166 -3
- package/dist/core/models.js.map +1 -1
- package/dist/core/pricing.js +83 -0
- package/dist/core/pricing.js.map +1 -0
- package/dist/core/roles.js +14 -3
- package/dist/core/roles.js.map +1 -1
- package/dist/core/run-usage.js +46 -0
- package/dist/core/run-usage.js.map +1 -0
- package/dist/core/session.js +18 -0
- package/dist/core/session.js.map +1 -1
- package/dist/daemon/daemon.js +286 -8
- package/dist/daemon/daemon.js.map +1 -1
- package/dist/daemon/ipc.js +10 -3
- package/dist/daemon/ipc.js.map +1 -1
- package/dist/drivers/antigravity/driver.js +158 -0
- package/dist/drivers/antigravity/driver.js.map +1 -0
- package/dist/drivers/antigravity/parser.js +168 -0
- package/dist/drivers/antigravity/parser.js.map +1 -0
- package/dist/drivers/claude/driver.js +13 -3
- package/dist/drivers/claude/driver.js.map +1 -1
- package/dist/drivers/opencode/driver.js +1 -114
- package/dist/drivers/opencode/driver.js.map +1 -1
- package/dist/drivers/opencode/parser.js +86 -0
- package/dist/drivers/opencode/parser.js.map +1 -0
- package/dist/drivers/registry.js +2 -0
- package/dist/drivers/registry.js.map +1 -1
- package/dist/drivers/session-driver.js +6 -4
- package/dist/drivers/session-driver.js.map +1 -1
- package/dist/drivers/session-runtime.js +1 -0
- package/dist/drivers/session-runtime.js.map +1 -1
- package/dist/git/worktree.js +1 -1
- package/dist/git/worktree.js.map +1 -1
- package/dist/open/contract.js +116 -0
- package/dist/open/contract.js.map +1 -0
- package/dist/open/injection.js +60 -0
- package/dist/open/injection.js.map +1 -0
- package/dist/open/launchers/claude.js +215 -0
- package/dist/open/launchers/claude.js.map +1 -0
- package/dist/open/launchers/opencode.js +213 -0
- package/dist/open/launchers/opencode.js.map +1 -0
- package/dist/open/orchestrator-prose.js +12 -0
- package/dist/open/orchestrator-prose.js.map +1 -0
- package/dist/open/pty.js +281 -0
- package/dist/open/pty.js.map +1 -0
- package/dist/open/runtime.js +632 -0
- package/dist/open/runtime.js.map +1 -0
- package/dist/plugin/agents/auditor.md +7 -1
- package/dist/plugin/agents/general.md +12 -1
- package/dist/plugin/agents/orchestrator-edit.md +67 -0
- package/dist/plugin/agents/orchestrator-read.md +67 -0
- package/dist/plugin/agents/orchestrator.md +74 -17
- package/dist/plugin/agents/reviewer.md +3 -0
- package/dist/plugin/commands/autonomous.md +58 -0
- package/dist/plugin/hooks/hooks.json +24 -0
- package/dist/plugin/hooks/session-id.sh +23 -0
- package/dist/plugin/hooks/session-name-lib.mjs +40 -0
- package/dist/plugin/hooks/session-name-worker.mjs +124 -0
- package/dist/plugin/hooks/session-name.mjs +66 -0
- package/dist/plugin/hooks/session-name.sh +7 -0
- package/dist/plugin/pty-shim.mjs +114 -0
- package/dist/plugin/statusline.sh +214 -22
- package/dist/plugin/themes/codedeck-rage.json +74 -0
- package/dist/plugin/themes/codedeck-ultra.json +52 -44
- package/dist/store/claims.js +152 -0
- package/dist/store/claims.js.map +1 -0
- package/dist/store/database.js +50 -15
- package/dist/store/database.js.map +1 -1
- package/dist/store/sessions.js +234 -3
- package/dist/store/sessions.js.map +1 -1
- package/dist/web/canvas-page.js +921 -0
- package/dist/web/canvas-page.js.map +1 -0
- package/package.json +3 -2
- package/dist/plugin/settings.json +0 -7
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdtempSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
12
|
+
|
|
13
|
+
import { slugFromPrompt } from "./session-name-lib.mjs";
|
|
14
|
+
|
|
15
|
+
const worker = fileURLToPath(new URL("./session-name-worker.mjs", import.meta.url));
|
|
16
|
+
|
|
17
|
+
function writeFallback(sidecar, prompt) {
|
|
18
|
+
try {
|
|
19
|
+
writeFileSync(sidecar, slugFromPrompt(prompt), { encoding: "utf8", flag: "wx" });
|
|
20
|
+
} catch {}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let payload;
|
|
24
|
+
try {
|
|
25
|
+
payload = JSON.parse(readFileSync(0, "utf8"));
|
|
26
|
+
} catch {
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (
|
|
31
|
+
payload === null ||
|
|
32
|
+
typeof payload !== "object" ||
|
|
33
|
+
Array.isArray(payload) ||
|
|
34
|
+
typeof process.env.CODEDECK_SESSION_FILE !== "string" ||
|
|
35
|
+
process.env.CODEDECK_SESSION_FILE.length === 0 ||
|
|
36
|
+
typeof payload.session_id !== "string" ||
|
|
37
|
+
!/^[0-9a-fA-F-]{8,}$/.test(payload.session_id)
|
|
38
|
+
) process.exit(0);
|
|
39
|
+
|
|
40
|
+
const sessionFile = process.env.CODEDECK_SESSION_FILE;
|
|
41
|
+
const sessionId = payload.session_id;
|
|
42
|
+
const sidecar = `${sessionFile}.${sessionId}.name`;
|
|
43
|
+
if (existsSync(sidecar)) process.exit(0);
|
|
44
|
+
|
|
45
|
+
const prompt = typeof payload.prompt === "string" ? payload.prompt : "";
|
|
46
|
+
// The worker gets one temp directory it can validate; the sidecar path and the
|
|
47
|
+
// prompt travel inside job.json rather than on argv, so the worker never opens a
|
|
48
|
+
// path handed to it as a command-line argument.
|
|
49
|
+
let jobDir;
|
|
50
|
+
try {
|
|
51
|
+
jobDir = mkdtempSync(join(tmpdir(), "codedeck-session-name-"));
|
|
52
|
+
writeFileSync(join(jobDir, "job.json"), JSON.stringify({ sidecar, prompt }), {
|
|
53
|
+
encoding: "utf8",
|
|
54
|
+
flag: "wx",
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const child = spawn(process.execPath, [worker, jobDir], {
|
|
58
|
+
detached: true,
|
|
59
|
+
stdio: "ignore",
|
|
60
|
+
});
|
|
61
|
+
child.unref();
|
|
62
|
+
process.exit(0);
|
|
63
|
+
} catch {
|
|
64
|
+
if (jobDir) rmSync(jobDir, { recursive: true, force: true });
|
|
65
|
+
writeFallback(sidecar, prompt);
|
|
66
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Roda DENTRO do pty criado pelo `script`, entre ele e o harness.
|
|
4
|
+
*
|
|
5
|
+
* Existe por causa de dois buracos que o processo pai não consegue tapar de
|
|
6
|
+
* fora:
|
|
7
|
+
*
|
|
8
|
+
* - `script` só dimensiona o pty quando o stdin dele é um terminal. No nosso
|
|
9
|
+
* desenho o stdin do `script` é um pipe (é por ali que a injeção do
|
|
10
|
+
* `/rename` entra), então o pty nasce 0x0 e qualquer TUI fullscreen desenha
|
|
11
|
+
* errado.
|
|
12
|
+
* - Node não expõe ioctl, então TIOCSWINSZ só é alcançável por um binário que
|
|
13
|
+
* faça a chamada: `stty`, que herda o slave do pty no fd 0.
|
|
14
|
+
*/
|
|
15
|
+
import net from "node:net";
|
|
16
|
+
import os from "node:os";
|
|
17
|
+
import { accessSync, constants } from "node:fs";
|
|
18
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
19
|
+
|
|
20
|
+
// The command comes from `codedeck open`, one argv entry per argument, and is
|
|
21
|
+
// run without a shell. The guard is there so a value that could only have been
|
|
22
|
+
// mangled on the way in — a control character, an empty binary — stops here
|
|
23
|
+
// rather than becoming a process.
|
|
24
|
+
const SAFE_BINARY = /^[^\u0000-\u001f]+$/;
|
|
25
|
+
const target = process.argv.slice(2);
|
|
26
|
+
if (target.length === 0 || !SAFE_BINARY.test(target[0])) {
|
|
27
|
+
process.stderr.write("pty-shim: missing or unusable target command\n");
|
|
28
|
+
process.exit(64);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Named by absolute path rather than looked up on PATH: this runs with the
|
|
33
|
+
* environment of whoever opened the session, and a PATH entry they can write
|
|
34
|
+
* to would decide what "stty" means.
|
|
35
|
+
*/
|
|
36
|
+
const STTY = ["/bin/stty", "/usr/bin/stty"].find((candidate) => {
|
|
37
|
+
try {
|
|
38
|
+
accessSync(candidate, constants.X_OK);
|
|
39
|
+
return true;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Applying the size on the slave is also what makes the kernel raise SIGWINCH
|
|
46
|
+
// on the session's foreground group, so the TUI redraws without ever knowing
|
|
47
|
+
// this shim exists.
|
|
48
|
+
function applySize(rows, cols) {
|
|
49
|
+
if (STTY === undefined) return false;
|
|
50
|
+
if (!Number.isInteger(rows) || !Number.isInteger(cols) || rows <= 0 || cols <= 0) return false;
|
|
51
|
+
const done = spawnSync(STTY, ["rows", String(rows), "cols", String(cols)], {
|
|
52
|
+
stdio: ["inherit", "ignore", "ignore"],
|
|
53
|
+
});
|
|
54
|
+
return done.status === 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
applySize(Number(process.env.CODEDECK_PTY_ROWS), Number(process.env.CODEDECK_PTY_COLS));
|
|
58
|
+
|
|
59
|
+
const [binary, ...args] = target;
|
|
60
|
+
const child = spawn(binary, args, { stdio: "inherit", shell: false });
|
|
61
|
+
|
|
62
|
+
// Ctrl+C typed by the user reaches the whole foreground group, this shim
|
|
63
|
+
// included. The harness owns that key (Claude Code draws its own interrupt),
|
|
64
|
+
// so the shim stays alive and lets the child decide when to die.
|
|
65
|
+
process.on("SIGINT", () => {});
|
|
66
|
+
process.on("SIGTERM", () => {});
|
|
67
|
+
|
|
68
|
+
let server;
|
|
69
|
+
const control = process.env.CODEDECK_PTY_CONTROL;
|
|
70
|
+
if (control) {
|
|
71
|
+
server = net.createServer((conn) => {
|
|
72
|
+
let buffer = "";
|
|
73
|
+
conn.setEncoding("utf8");
|
|
74
|
+
conn.on("data", (chunk) => {
|
|
75
|
+
buffer += chunk;
|
|
76
|
+
let index;
|
|
77
|
+
while ((index = buffer.indexOf("\n")) >= 0) {
|
|
78
|
+
const line = buffer.slice(0, index).trim();
|
|
79
|
+
buffer = buffer.slice(index + 1);
|
|
80
|
+
if (line.length === 0) continue;
|
|
81
|
+
let message;
|
|
82
|
+
try {
|
|
83
|
+
message = JSON.parse(line);
|
|
84
|
+
} catch {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (message?.type === "resize") applySize(Number(message.rows), Number(message.cols));
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
conn.on("error", () => {});
|
|
91
|
+
});
|
|
92
|
+
server.on("error", () => {});
|
|
93
|
+
server.listen(control);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const closeServer = () => {
|
|
97
|
+
try {
|
|
98
|
+
server?.close();
|
|
99
|
+
} catch {}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
child.on("error", (error) => {
|
|
103
|
+
closeServer();
|
|
104
|
+
process.stderr.write(`pty-shim: ${error.message}\n`);
|
|
105
|
+
process.exit(126);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// The exit status has to survive both layers so `codedeck open` still reports
|
|
109
|
+
// what the harness actually did.
|
|
110
|
+
child.on("exit", (code, signal) => {
|
|
111
|
+
closeServer();
|
|
112
|
+
if (signal) process.exit(128 + (os.constants.signals[signal] ?? 0));
|
|
113
|
+
process.exit(code ?? 0);
|
|
114
|
+
});
|
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
# Claude Code sends the status payload on stdin. Keep parsing in Node so this
|
|
4
4
|
# plugin does not require jq or Python on the host.
|
|
5
|
+
#
|
|
6
|
+
# The JS below is single quoted, so it must not contain a single quote anywhere.
|
|
5
7
|
set -u
|
|
6
8
|
|
|
7
9
|
node --input-type=module -e '
|
|
8
10
|
import { execFileSync } from "node:child_process";
|
|
9
|
-
import { readFileSync } from "node:fs";
|
|
11
|
+
import { readFileSync, writeSync } from "node:fs";
|
|
12
|
+
import { basename } from "node:path";
|
|
10
13
|
|
|
11
14
|
let payload;
|
|
12
15
|
try {
|
|
@@ -14,6 +17,23 @@ try {
|
|
|
14
17
|
} catch {
|
|
15
18
|
process.exit(0);
|
|
16
19
|
}
|
|
20
|
+
if (payload === null || typeof payload !== "object") process.exit(0);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Claude Code dims every status row before printing it, so the palette here is
|
|
24
|
+
* the bright end of the theme on purpose: a mid tone arrives on screen as grey.
|
|
25
|
+
* SGR survives, which is why these are raw escapes and not a library.
|
|
26
|
+
*/
|
|
27
|
+
const BLOOD = "\x1b[38;2;225;29;72m";
|
|
28
|
+
const EMBER = "\x1b[38;2;251;146;60m";
|
|
29
|
+
const TEXT = "\x1b[38;2;247;237;238m";
|
|
30
|
+
const MUTED = "\x1b[38;2;163;139;143m";
|
|
31
|
+
const GREEN = "\x1b[38;2;74;222;128m";
|
|
32
|
+
const AMBER = "\x1b[38;2;251;191;36m";
|
|
33
|
+
const RED = "\x1b[38;2;255;77;109m";
|
|
34
|
+
const OFF = "\x1b[0m";
|
|
35
|
+
|
|
36
|
+
const paint = (color, value) => color + value + OFF;
|
|
17
37
|
|
|
18
38
|
const text = (value) => {
|
|
19
39
|
if (typeof value !== "string") return undefined;
|
|
@@ -23,29 +43,55 @@ const text = (value) => {
|
|
|
23
43
|
|
|
24
44
|
const firstText = (...values) => values.map(text).find(Boolean);
|
|
25
45
|
|
|
26
|
-
|
|
46
|
+
/**
|
|
47
|
+
* A row is one line, so anything that could carry a newline is flattened before
|
|
48
|
+
* it reaches the terminal.
|
|
49
|
+
*/
|
|
50
|
+
const clean = (value) => value.replace(/[\t\r\n]/g, " ");
|
|
51
|
+
|
|
52
|
+
const tail = (value) => {
|
|
27
53
|
const candidate = text(value);
|
|
28
54
|
if (!candidate) return undefined;
|
|
29
55
|
const parts = candidate.split(/[·:]/).map((part) => part.trim()).filter(Boolean);
|
|
30
56
|
return parts.at(-1) ?? candidate;
|
|
31
57
|
};
|
|
32
58
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
) ??
|
|
39
|
-
|
|
40
|
-
);
|
|
41
|
-
|
|
59
|
+
/**
|
|
60
|
+
* `codedeck open` puts the role at the end of session_name, so the tail remains
|
|
61
|
+
* the source for ordinary sessions. agent.name is only a fallback.
|
|
62
|
+
*/
|
|
63
|
+
const role =
|
|
64
|
+
tail(text(payload.session_name)?.match(/CodeDeck\s*[·:]\s*(.+)$/i)?.[1]) ??
|
|
65
|
+
tail(payload.agent?.name) ??
|
|
66
|
+
tail(payload.session_name);
|
|
67
|
+
|
|
68
|
+
const taskName = (() => {
|
|
69
|
+
const sessionFile = text(process.env.CODEDECK_SESSION_FILE);
|
|
70
|
+
const sessionId = payload.session_id;
|
|
71
|
+
if (
|
|
72
|
+
!sessionFile ||
|
|
73
|
+
typeof sessionId !== "string" ||
|
|
74
|
+
!/^[0-9a-fA-F-]{8,}$/.test(sessionId)
|
|
75
|
+
) return undefined;
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const value = readFileSync(`${sessionFile}.${sessionId}.name`, "utf8")
|
|
79
|
+
.replace(/\s+/g, " ")
|
|
80
|
+
.trim()
|
|
81
|
+
.slice(0, 30);
|
|
82
|
+
return value || undefined;
|
|
83
|
+
} catch {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
|
|
42
88
|
const cwd = firstText(payload.workspace?.current_dir, payload.cwd) ?? process.cwd();
|
|
89
|
+
const project = text(basename(cwd)) ?? (cwd === "/" ? "/" : undefined);
|
|
43
90
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
);
|
|
91
|
+
/**
|
|
92
|
+
* Claude supplies a branch for its own worktrees. Git fills the ordinary case.
|
|
93
|
+
*/
|
|
94
|
+
let branch = text(payload.worktree?.branch);
|
|
49
95
|
if (!branch) {
|
|
50
96
|
try {
|
|
51
97
|
branch = text(execFileSync("git", ["-C", cwd, "branch", "--show-current"], {
|
|
@@ -57,12 +103,158 @@ if (!branch) {
|
|
|
57
103
|
}
|
|
58
104
|
}
|
|
59
105
|
|
|
60
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Context is reported as remaining, not used. Colour keeps the existing bands.
|
|
108
|
+
*/
|
|
109
|
+
const contextField = () => {
|
|
110
|
+
const remaining = payload.context_window?.remaining_percentage;
|
|
111
|
+
if (typeof remaining !== "number" || !Number.isFinite(remaining)) return undefined;
|
|
112
|
+
const value = Math.max(0, Math.min(100, Math.round(remaining)));
|
|
113
|
+
const color = value <= 15 ? RED : value <= 35 ? AMBER : GREEN;
|
|
114
|
+
return paint(MUTED, "ctx ") + paint(color, value + "%");
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const nonNegativeNumber = (value) =>
|
|
118
|
+
typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
119
|
+
|
|
120
|
+
const compactTokens = (value) => {
|
|
121
|
+
const total = Math.max(0, Math.round(value));
|
|
122
|
+
if (total < 1000) return String(total);
|
|
123
|
+
if (total >= 1_000_000) {
|
|
124
|
+
const millions = total / 1_000_000;
|
|
125
|
+
return millions.toFixed(millions < 10 ? 1 : 0).replace(/\.0$/, "") + "M";
|
|
126
|
+
}
|
|
127
|
+
const thousands = total / 1000;
|
|
128
|
+
const roundedThousands = Number(thousands.toFixed(thousands < 100 ? 1 : 0));
|
|
129
|
+
if (roundedThousands >= 1000) return "1M";
|
|
130
|
+
return String(roundedThousands).replace(/\.0$/, "") + "k";
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Since Claude Code 2.1.132, these fields describe the latest context window,
|
|
135
|
+
* not cumulative session totals. The aggregate token field therefore stays
|
|
136
|
+
* worker-only instead of adding a repeated local window to the run total.
|
|
137
|
+
* Degraded mode may show this current local snapshot when it is available.
|
|
138
|
+
*/
|
|
139
|
+
const localTokens = () => {
|
|
140
|
+
const context = payload.context_window;
|
|
141
|
+
const totalInput = nonNegativeNumber(context?.total_input_tokens);
|
|
142
|
+
const totalOutput = nonNegativeNumber(context?.total_output_tokens);
|
|
143
|
+
if (totalInput !== undefined && totalOutput !== undefined) return totalInput + totalOutput;
|
|
144
|
+
|
|
145
|
+
const current = context?.current_usage;
|
|
146
|
+
const input = nonNegativeNumber(current?.input_tokens);
|
|
147
|
+
const output = nonNegativeNumber(current?.output_tokens);
|
|
148
|
+
const cacheCreation = nonNegativeNumber(current?.cache_creation_input_tokens);
|
|
149
|
+
const cacheRead = nonNegativeNumber(current?.cache_read_input_tokens);
|
|
150
|
+
if (
|
|
151
|
+
input !== undefined &&
|
|
152
|
+
output !== undefined &&
|
|
153
|
+
cacheCreation !== undefined &&
|
|
154
|
+
cacheRead !== undefined
|
|
155
|
+
) return input + output + cacheCreation + cacheRead;
|
|
156
|
+
return undefined;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const COST_DISPLAY_THRESHOLD = 0.01;
|
|
160
|
+
const COST_TEXT_THRESHOLD = 1;
|
|
161
|
+
const COST_EMBER_THRESHOLD = 5;
|
|
162
|
+
const COST_BLOOD_THRESHOLD = 10;
|
|
163
|
+
|
|
164
|
+
const costColor = (total) =>
|
|
165
|
+
total < COST_TEXT_THRESHOLD ? MUTED :
|
|
166
|
+
total < COST_EMBER_THRESHOLD ? TEXT :
|
|
167
|
+
total < COST_BLOOD_THRESHOLD ? EMBER : BLOOD;
|
|
168
|
+
|
|
169
|
+
const costAmount = (total, incomplete = false) => {
|
|
170
|
+
const value = total >= COST_DISPLAY_THRESHOLD ? "$" + total.toFixed(2) : "$";
|
|
171
|
+
const amount = paint(costColor(total), value);
|
|
172
|
+
return incomplete ? amount + paint(AMBER, "?") : amount;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const localCost = () => {
|
|
176
|
+
const total = payload.cost?.total_cost_usd;
|
|
177
|
+
return typeof total === "number" && Number.isFinite(total) ? total : undefined;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const getRunUsage = () => {
|
|
181
|
+
const runId = text(process.env.CODEDECK_RUN_ID);
|
|
182
|
+
if (!runId) return undefined;
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const output = execFileSync("codedeck", ["usage", runId, "--json"], {
|
|
186
|
+
encoding: "utf8",
|
|
187
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
188
|
+
timeout: 1000,
|
|
189
|
+
maxBuffer: 1024 * 1024,
|
|
190
|
+
});
|
|
191
|
+
const usage = JSON.parse(output);
|
|
192
|
+
if (
|
|
193
|
+
usage === null ||
|
|
194
|
+
typeof usage !== "object" ||
|
|
195
|
+
usage.runId !== runId ||
|
|
196
|
+
typeof usage.inputTokens !== "number" ||
|
|
197
|
+
!Number.isFinite(usage.inputTokens) ||
|
|
198
|
+
typeof usage.outputTokens !== "number" ||
|
|
199
|
+
!Number.isFinite(usage.outputTokens) ||
|
|
200
|
+
typeof usage.cachedTokens !== "number" ||
|
|
201
|
+
!Number.isFinite(usage.cachedTokens) ||
|
|
202
|
+
typeof usage.costUsd !== "number" ||
|
|
203
|
+
!Number.isFinite(usage.costUsd) ||
|
|
204
|
+
typeof usage.sessionCount !== "number" ||
|
|
205
|
+
!Number.isFinite(usage.sessionCount) ||
|
|
206
|
+
typeof usage.activeSessionCount !== "number" ||
|
|
207
|
+
!Number.isFinite(usage.activeSessionCount) ||
|
|
208
|
+
typeof usage.costComplete !== "boolean" ||
|
|
209
|
+
typeof usage.sessionsWithoutCost !== "number" ||
|
|
210
|
+
!Number.isFinite(usage.sessionsWithoutCost)
|
|
211
|
+
) return undefined;
|
|
212
|
+
return usage;
|
|
213
|
+
} catch {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const local = localCost();
|
|
219
|
+
const runUsage = getRunUsage();
|
|
220
|
+
const workerTokens = runUsage
|
|
221
|
+
? runUsage.inputTokens + runUsage.outputTokens + runUsage.cachedTokens
|
|
222
|
+
: undefined;
|
|
223
|
+
const tokenField = () => {
|
|
224
|
+
const total = workerTokens ?? (runUsage ? undefined : localTokens());
|
|
225
|
+
return total === undefined ? undefined : paint(MUTED, compactTokens(total) + " tok");
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const runField = () => {
|
|
229
|
+
if (!runUsage) return undefined;
|
|
230
|
+
const total = (local ?? 0) + runUsage.costUsd;
|
|
231
|
+
const incomplete = !runUsage.costComplete;
|
|
232
|
+
if (!incomplete && total < COST_DISPLAY_THRESHOLD) return undefined;
|
|
233
|
+
return paint(MUTED, "run ") + costAmount(total, incomplete);
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const localField = () => {
|
|
237
|
+
if (local === undefined || local < COST_DISPLAY_THRESHOLD) return undefined;
|
|
238
|
+
return costAmount(local);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const projectBranch = project && branch
|
|
242
|
+
? paint(TEXT, clean(project)) + paint(MUTED, "/") + paint(BLOOD, clean(branch))
|
|
243
|
+
: project
|
|
244
|
+
? paint(TEXT, clean(project))
|
|
245
|
+
: branch
|
|
246
|
+
? paint(BLOOD, clean(branch))
|
|
247
|
+
: undefined;
|
|
248
|
+
|
|
61
249
|
const fields = [
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
250
|
+
taskName && paint(EMBER, clean(taskName)),
|
|
251
|
+
role && paint(EMBER, clean(role)),
|
|
252
|
+
projectBranch,
|
|
253
|
+
contextField(),
|
|
254
|
+
tokenField(),
|
|
255
|
+
runUsage ? runField() : localField(),
|
|
256
|
+
runUsage && paint(TEXT, String(Math.max(0, Math.round(runUsage.activeSessionCount))) + " agents"),
|
|
65
257
|
].filter(Boolean);
|
|
66
258
|
|
|
67
|
-
|
|
68
|
-
'
|
|
259
|
+
writeSync(1, fields.join(paint(MUTED, " · ")));
|
|
260
|
+
'
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/theme.json",
|
|
3
|
+
"defs": {
|
|
4
|
+
"blood": "#E11D48",
|
|
5
|
+
"shimmer": "#C026D3",
|
|
6
|
+
"ember": "#FB923C",
|
|
7
|
+
"amber": "#FBBF24",
|
|
8
|
+
"remember": "#FACC15",
|
|
9
|
+
"green": "#4ADE80",
|
|
10
|
+
"red": "#FF4D6D",
|
|
11
|
+
"text": "#F7EDEE",
|
|
12
|
+
"muted": "#A38B8F",
|
|
13
|
+
"ink": "#140406",
|
|
14
|
+
"panel": "#1F0A0E",
|
|
15
|
+
"element": "#2A0710",
|
|
16
|
+
"rose": "#FDA4AF",
|
|
17
|
+
"pink": "#F472B6",
|
|
18
|
+
"blue": "#60A5FA",
|
|
19
|
+
"cyan": "#22D3EE",
|
|
20
|
+
"mint": "#34D399",
|
|
21
|
+
"purple": "#C084FC",
|
|
22
|
+
"deepGreen": "#14532D",
|
|
23
|
+
"deepRed": "#7F1D1D"
|
|
24
|
+
},
|
|
25
|
+
"theme": {
|
|
26
|
+
"primary": { "dark": "blood", "light": "blood" },
|
|
27
|
+
"secondary": { "dark": "rose", "light": "blood" },
|
|
28
|
+
"accent": { "dark": "shimmer", "light": "shimmer" },
|
|
29
|
+
"error": { "dark": "red", "light": "red" },
|
|
30
|
+
"warning": { "dark": "amber", "light": "amber" },
|
|
31
|
+
"success": { "dark": "green", "light": "green" },
|
|
32
|
+
"info": { "dark": "blue", "light": "blue" },
|
|
33
|
+
"text": { "dark": "text", "light": "ink" },
|
|
34
|
+
"textMuted": { "dark": "muted", "light": "muted" },
|
|
35
|
+
"background": { "dark": "ink", "light": "text" },
|
|
36
|
+
"backgroundPanel": { "dark": "panel", "light": "rose" },
|
|
37
|
+
"backgroundElement": { "dark": "element", "light": "rose" },
|
|
38
|
+
"border": { "dark": "blood", "light": "blood" },
|
|
39
|
+
"borderActive": { "dark": "ember", "light": "ember" },
|
|
40
|
+
"borderSubtle": { "dark": "muted", "light": "muted" },
|
|
41
|
+
"diffAdded": { "dark": "green", "light": "deepGreen" },
|
|
42
|
+
"diffRemoved": { "dark": "red", "light": "deepRed" },
|
|
43
|
+
"diffContext": { "dark": "muted", "light": "muted" },
|
|
44
|
+
"diffHunkHeader": { "dark": "cyan", "light": "blue" },
|
|
45
|
+
"diffHighlightAdded": { "dark": "green", "light": "deepGreen" },
|
|
46
|
+
"diffHighlightRemoved": { "dark": "red", "light": "deepRed" },
|
|
47
|
+
"diffAddedBg": { "dark": "deepGreen", "light": "green" },
|
|
48
|
+
"diffRemovedBg": { "dark": "deepRed", "light": "red" },
|
|
49
|
+
"diffContextBg": { "dark": "ink", "light": "text" },
|
|
50
|
+
"diffLineNumber": { "dark": "muted", "light": "muted" },
|
|
51
|
+
"diffAddedLineNumberBg": { "dark": "deepGreen", "light": "green" },
|
|
52
|
+
"diffRemovedLineNumberBg": { "dark": "deepRed", "light": "red" },
|
|
53
|
+
"markdownText": { "dark": "text", "light": "ink" },
|
|
54
|
+
"markdownHeading": { "dark": "blood", "light": "blood" },
|
|
55
|
+
"markdownLink": { "dark": "cyan", "light": "blue" },
|
|
56
|
+
"markdownLinkText": { "dark": "cyan", "light": "blue" },
|
|
57
|
+
"markdownCode": { "dark": "ember", "light": "ember" },
|
|
58
|
+
"markdownBlockQuote": { "dark": "muted", "light": "muted" },
|
|
59
|
+
"markdownEmph": { "dark": "rose", "light": "blood" },
|
|
60
|
+
"markdownStrong": { "dark": "text", "light": "ink" },
|
|
61
|
+
"markdownHorizontalRule": { "dark": "blood", "light": "blood" },
|
|
62
|
+
"markdownListItem": { "dark": "ember", "light": "ember" },
|
|
63
|
+
"markdownCodeBlock": { "dark": "element", "light": "rose" },
|
|
64
|
+
"syntaxComment": { "dark": "muted", "light": "muted" },
|
|
65
|
+
"syntaxKeyword": { "dark": "blood", "light": "blood" },
|
|
66
|
+
"syntaxFunction": { "dark": "blue", "light": "blue" },
|
|
67
|
+
"syntaxVariable": { "dark": "cyan", "light": "blue" },
|
|
68
|
+
"syntaxString": { "dark": "green", "light": "deepGreen" },
|
|
69
|
+
"syntaxNumber": { "dark": "ember", "light": "ember" },
|
|
70
|
+
"syntaxType": { "dark": "purple", "light": "shimmer" },
|
|
71
|
+
"syntaxOperator": { "dark": "pink", "light": "blood" },
|
|
72
|
+
"syntaxPunctuation": { "dark": "muted", "light": "muted" }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -1,47 +1,55 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "CodeDeck
|
|
2
|
+
"name": "CodeDeck Rage",
|
|
3
3
|
"base": "dark",
|
|
4
4
|
"overrides": {
|
|
5
|
-
"claude": "#
|
|
6
|
-
"claudeShimmer": "#
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
5
|
+
"claude": "#E11D48",
|
|
6
|
+
"claudeShimmer": "#C026D3",
|
|
7
|
+
"claudeBlue_FOR_SYSTEM_SPINNER": "#E11D48",
|
|
8
|
+
"claudeBlueShimmer_FOR_SYSTEM_SPINNER": "#C026D3",
|
|
9
|
+
"autoAcceptShimmer": "#FCA5A5",
|
|
10
|
+
"clawd_body": "#881337",
|
|
11
|
+
"clawd_background": "#140406",
|
|
12
|
+
"background": "#140406",
|
|
13
|
+
"composerSidebarBackground": "#1F0A0E",
|
|
14
|
+
"professionalBlue": "#F43F5E",
|
|
15
|
+
"text": "#F7EDEE",
|
|
16
|
+
"inverseText": "#140406",
|
|
17
|
+
"inactive": "#A38B8F",
|
|
18
|
+
"inactiveShimmer": "#D6C2C5",
|
|
19
|
+
"subtle": "#EC6A5C",
|
|
20
|
+
"suggestion": "#FCA5A5",
|
|
21
|
+
"permission": "#FB923C",
|
|
22
|
+
"permissionShimmer": "#FED7AA",
|
|
15
23
|
"remember": "#FACC15",
|
|
16
|
-
"success": "#
|
|
17
|
-
"error": "#
|
|
24
|
+
"success": "#4ADE80",
|
|
25
|
+
"error": "#FF4D6D",
|
|
18
26
|
"warning": "#FBBF24",
|
|
19
27
|
"warningShimmer": "#FDE68A",
|
|
20
|
-
"merged": "#
|
|
21
|
-
"promptBorder": "#
|
|
22
|
-
"promptBorderShimmer": "#
|
|
23
|
-
"planMode": "#
|
|
24
|
-
"autoAccept": "#
|
|
28
|
+
"merged": "#F43F5E",
|
|
29
|
+
"promptBorder": "#E11D48",
|
|
30
|
+
"promptBorderShimmer": "#C026D3",
|
|
31
|
+
"planMode": "#FDA4AF",
|
|
32
|
+
"autoAccept": "#4ADE80",
|
|
25
33
|
"bashBorder": "#F59E0B",
|
|
26
|
-
"ide": "#
|
|
27
|
-
"fastMode": "#
|
|
28
|
-
"fastModeShimmer": "#
|
|
29
|
-
"effortUltra": "#
|
|
34
|
+
"ide": "#F43F5E",
|
|
35
|
+
"fastMode": "#FF4D6D",
|
|
36
|
+
"fastModeShimmer": "#FFD1D8",
|
|
37
|
+
"effortUltra": "#FB7185",
|
|
30
38
|
"diffAdded": "#14532D",
|
|
31
39
|
"diffRemoved": "#7F1D1D",
|
|
32
40
|
"diffAddedDimmed": "#166534",
|
|
33
41
|
"diffRemovedDimmed": "#991B1B",
|
|
34
42
|
"diffAddedWord": "#22C55E",
|
|
35
43
|
"diffRemovedWord": "#EF4444",
|
|
36
|
-
"userMessageBackground": "#
|
|
37
|
-
"userMessageBackgroundHover": "#
|
|
44
|
+
"userMessageBackground": "#2A0710",
|
|
45
|
+
"userMessageBackgroundHover": "#4C0D1C",
|
|
38
46
|
"bashMessageBackgroundColor": "#451A03",
|
|
39
|
-
"memoryBackgroundColor": "#
|
|
40
|
-
"selectionBg": "#
|
|
41
|
-
"rate_limit_fill": "#
|
|
42
|
-
"rate_limit_empty": "#
|
|
43
|
-
"briefLabelYou": "#
|
|
44
|
-
"briefLabelClaude": "#
|
|
47
|
+
"memoryBackgroundColor": "#291B1D",
|
|
48
|
+
"selectionBg": "#4C1D24",
|
|
49
|
+
"rate_limit_fill": "#E11D48",
|
|
50
|
+
"rate_limit_empty": "#3F1E24",
|
|
51
|
+
"briefLabelYou": "#FCA5A5",
|
|
52
|
+
"briefLabelClaude": "#E11D48",
|
|
45
53
|
"red_FOR_SUBAGENTS_ONLY": "#FB7185",
|
|
46
54
|
"blue_FOR_SUBAGENTS_ONLY": "#60A5FA",
|
|
47
55
|
"green_FOR_SUBAGENTS_ONLY": "#34D399",
|
|
@@ -50,19 +58,19 @@
|
|
|
50
58
|
"orange_FOR_SUBAGENTS_ONLY": "#FB923C",
|
|
51
59
|
"pink_FOR_SUBAGENTS_ONLY": "#F472B6",
|
|
52
60
|
"cyan_FOR_SUBAGENTS_ONLY": "#22D3EE",
|
|
53
|
-
"rainbow_red": "#
|
|
54
|
-
"rainbow_red_shimmer": "#
|
|
55
|
-
"rainbow_orange": "#
|
|
56
|
-
"rainbow_orange_shimmer": "#
|
|
57
|
-
"rainbow_yellow": "#
|
|
58
|
-
"rainbow_yellow_shimmer": "#
|
|
59
|
-
"rainbow_green": "#
|
|
60
|
-
"rainbow_green_shimmer": "#
|
|
61
|
-
"rainbow_blue": "#
|
|
62
|
-
"rainbow_blue_shimmer": "#
|
|
63
|
-
"rainbow_indigo": "#
|
|
64
|
-
"rainbow_indigo_shimmer": "#
|
|
65
|
-
"rainbow_violet": "#
|
|
66
|
-
"rainbow_violet_shimmer": "#
|
|
61
|
+
"rainbow_red": "#7F1D1D",
|
|
62
|
+
"rainbow_red_shimmer": "#B91C1C",
|
|
63
|
+
"rainbow_orange": "#B91C1C",
|
|
64
|
+
"rainbow_orange_shimmer": "#E11D48",
|
|
65
|
+
"rainbow_yellow": "#E11D48",
|
|
66
|
+
"rainbow_yellow_shimmer": "#F43F5E",
|
|
67
|
+
"rainbow_green": "#F43F5E",
|
|
68
|
+
"rainbow_green_shimmer": "#FB7185",
|
|
69
|
+
"rainbow_blue": "#FB7185",
|
|
70
|
+
"rainbow_blue_shimmer": "#FB923C",
|
|
71
|
+
"rainbow_indigo": "#FB923C",
|
|
72
|
+
"rainbow_indigo_shimmer": "#FBBF24",
|
|
73
|
+
"rainbow_violet": "#FBBF24",
|
|
74
|
+
"rainbow_violet_shimmer": "#FDE68A"
|
|
67
75
|
}
|
|
68
76
|
}
|