codedeck 0.5.3 → 0.6.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 +1 -0
- package/dist/cli/commands/review.js +122 -0
- package/dist/cli/commands/review.js.map +1 -0
- package/dist/cli/index.js +3 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/drivers/opencode/driver.js +24 -2
- package/dist/drivers/opencode/driver.js.map +1 -1
- package/dist/plugin/hooks/pane-toggle.ts +13 -0
- package/dist/plugin/hooks/register.tsx +13 -18
- package/dist/plugin/mods/agents/pane.ts +42 -11
- package/dist/web/review-page.js +1 -2
- package/dist/web/review-page.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,6 +93,7 @@ The daemon owns the sessions. The CLI only follows events — closing the termin
|
|
|
93
93
|
| `npx codedeck send <id> "<msg>"` | Continue a session (new turn) |
|
|
94
94
|
| `npx codedeck stop <id>` | Graceful interrupt → SIGTERM → SIGKILL |
|
|
95
95
|
| `npx codedeck diff <id> [--stat] [--json]` | Git diff against base commit |
|
|
96
|
+
| `npx codedeck review [--port <n>] [--no-open]` | Open a local review of the current git changes |
|
|
96
97
|
|
|
97
98
|
## Open
|
|
98
99
|
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { InvalidArgumentError } from "commander";
|
|
4
|
+
import { REVIEW_PAGE } from "../../web/review-page.js";
|
|
5
|
+
import { getLocalReview } from "../../git/review.js";
|
|
6
|
+
export const DEFAULT_REVIEW_PORT = 3100;
|
|
7
|
+
export function parseReviewPort(raw) {
|
|
8
|
+
if (raw === undefined)
|
|
9
|
+
return DEFAULT_REVIEW_PORT;
|
|
10
|
+
if (!/^\d+$/.test(raw)) {
|
|
11
|
+
throw new InvalidArgumentError("--port must be a positive integer");
|
|
12
|
+
}
|
|
13
|
+
const port = Number(raw);
|
|
14
|
+
if (!Number.isInteger(port) || port <= 0 || port > 65535) {
|
|
15
|
+
throw new InvalidArgumentError("--port must be a positive integer");
|
|
16
|
+
}
|
|
17
|
+
return port;
|
|
18
|
+
}
|
|
19
|
+
export function openBrowser(url) {
|
|
20
|
+
const opener = process.platform === "darwin"
|
|
21
|
+
? "open"
|
|
22
|
+
: process.platform === "win32"
|
|
23
|
+
? "cmd"
|
|
24
|
+
: "xdg-open";
|
|
25
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
26
|
+
try {
|
|
27
|
+
const child = spawn(opener, args, { detached: true, stdio: "ignore" });
|
|
28
|
+
child.unref();
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function sendJson(res, code, value) {
|
|
36
|
+
res.writeHead(code, { "content-type": "application/json; charset=utf-8" });
|
|
37
|
+
res.end(JSON.stringify(value));
|
|
38
|
+
}
|
|
39
|
+
export function createReviewHandler(reviewDeps) {
|
|
40
|
+
return (req, res) => {
|
|
41
|
+
void (async () => {
|
|
42
|
+
try {
|
|
43
|
+
const url = new URL(req.url || "/", "http://127.0.0.1");
|
|
44
|
+
if (req.method === "GET" && (url.pathname === "/" || url.pathname === "/review")) {
|
|
45
|
+
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
46
|
+
res.end(REVIEW_PAGE);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (req.method === "GET" && url.pathname === "/api/review") {
|
|
50
|
+
const ref = url.searchParams.get("ref") || "HEAD";
|
|
51
|
+
const file = url.searchParams.get("file") || undefined;
|
|
52
|
+
const root = reviewDeps?.root ?? process.cwd();
|
|
53
|
+
try {
|
|
54
|
+
const load = reviewDeps?.loadReview ?? ((r, q, f) => getLocalReview(r, { ref: q, file: f }));
|
|
55
|
+
sendJson(res, 200, await load(root, ref, file));
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59
|
+
sendJson(res, message.includes("not a git repository") ? 404 : 500, { error: message });
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
sendJson(res, 404, { error: "not found" });
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
try {
|
|
67
|
+
sendJson(res, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
try {
|
|
71
|
+
res.end();
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// The socket is already unusable.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
})();
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function registerReviewCommand(program) {
|
|
82
|
+
program
|
|
83
|
+
.command("review")
|
|
84
|
+
.description("Open a local review of the current git changes")
|
|
85
|
+
.option("--port <n>", "port to listen on (default: 3100)", String(DEFAULT_REVIEW_PORT))
|
|
86
|
+
.option("--no-open", "serve the review without opening a browser")
|
|
87
|
+
.action(async (opts) => {
|
|
88
|
+
let port;
|
|
89
|
+
try {
|
|
90
|
+
port = parseReviewPort(opts.port);
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
94
|
+
process.exitCode = 1;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const server = http.createServer(createReviewHandler());
|
|
98
|
+
try {
|
|
99
|
+
await new Promise((resolve, reject) => {
|
|
100
|
+
server.once("error", reject);
|
|
101
|
+
server.listen(port, "127.0.0.1", () => resolve());
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
console.error(`Failed to listen on 127.0.0.1:${port}: ${error instanceof Error ? error.message : String(error)}`);
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const url = `http://127.0.0.1:${port}/`;
|
|
110
|
+
console.log(`CodeDeck review on ${url}`);
|
|
111
|
+
if (opts.open !== false && !openBrowser(url)) {
|
|
112
|
+
console.log(`Could not open a browser, visit ${url} manually.`);
|
|
113
|
+
}
|
|
114
|
+
const shutdown = () => {
|
|
115
|
+
server.close(() => process.exit(0));
|
|
116
|
+
setTimeout(() => process.exit(0), 1000).unref?.();
|
|
117
|
+
};
|
|
118
|
+
process.on("SIGINT", shutdown);
|
|
119
|
+
process.on("SIGTERM", shutdown);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../../src/cli/commands/review.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import { registerOpenCommand } from "./commands/open.js";
|
|
|
20
20
|
import { registerProfileCommand } from "./commands/profile.js";
|
|
21
21
|
import { registerSetupCommand } from "./commands/setup.js";
|
|
22
22
|
import { registerUsageCommand } from "./commands/usage.js";
|
|
23
|
-
import {
|
|
23
|
+
import { registerReviewCommand } from "./commands/review.js";
|
|
24
24
|
import { getCliInvocation, getCliName } from "./cli-name.js";
|
|
25
25
|
function getVersion() {
|
|
26
26
|
try {
|
|
@@ -68,7 +68,7 @@ Recommended flow:
|
|
|
68
68
|
$ ${cli} run "task" --bg --json # starts in background
|
|
69
69
|
$ ${cli} wait <id> # waits without ps/show loop
|
|
70
70
|
$ ${cli} logs <id> --follow # inspect progress
|
|
71
|
-
$ ${cli}
|
|
71
|
+
$ ${cli} review # review current git changes in the browser
|
|
72
72
|
|
|
73
73
|
Run '${cli} <command> --help' for command-specific options.
|
|
74
74
|
Docs: https://github.com/4ndreello/run-agent
|
|
@@ -90,7 +90,7 @@ registerOpenCommand(program);
|
|
|
90
90
|
registerProfileCommand(program);
|
|
91
91
|
registerSetupCommand(program);
|
|
92
92
|
registerUsageCommand(program);
|
|
93
|
-
|
|
93
|
+
registerReviewCommand(program);
|
|
94
94
|
// Make `codedeck help` behave like `codedeck --help`
|
|
95
95
|
program.command("help", { hidden: true }).action(() => program.outputHelp());
|
|
96
96
|
program.parseAsync(process.argv).catch((err) => {
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
1
4
|
import { detectBinary, runCommandWithTimeout } from "../helpers.js";
|
|
2
5
|
import { createRuntimeHooks, SessionDriver } from "../session-driver.js";
|
|
3
6
|
import { parseOpencodeLine } from "./parser.js";
|
|
7
|
+
function opencodeV2BinaryPath(homeDir = os.homedir()) {
|
|
8
|
+
return path.join(homeDir, ".opencode", "bin", "opencode");
|
|
9
|
+
}
|
|
10
|
+
function resolveOpencodeCommand(homeDir = os.homedir()) {
|
|
11
|
+
const localBin = opencodeV2BinaryPath(homeDir);
|
|
12
|
+
return fs.existsSync(localBin) ? localBin : "opencode";
|
|
13
|
+
}
|
|
4
14
|
export class OpencodeDriver extends SessionDriver {
|
|
5
15
|
id = "opencode";
|
|
16
|
+
detectedPath;
|
|
6
17
|
hooks = createRuntimeHooks({
|
|
7
18
|
parse: parseOpencodeLine,
|
|
8
19
|
nativeKeys: ["sessionID", "sessionId", "session_id"],
|
|
9
20
|
harness: "Opencode",
|
|
10
21
|
});
|
|
11
22
|
resumeError = "No native session id for Opencode resume";
|
|
23
|
+
getCommand() {
|
|
24
|
+
return this.detectedPath ?? resolveOpencodeCommand();
|
|
25
|
+
}
|
|
12
26
|
buildArgs(options) {
|
|
13
27
|
const args = ["run", "--format", "json"];
|
|
14
28
|
if (options.model)
|
|
@@ -33,9 +47,17 @@ export class OpencodeDriver extends SessionDriver {
|
|
|
33
47
|
};
|
|
34
48
|
}
|
|
35
49
|
async detect() {
|
|
36
|
-
|
|
37
|
-
if (!res.installed)
|
|
50
|
+
let res = await detectBinary("opencode");
|
|
51
|
+
if (!res.installed) {
|
|
52
|
+
const localBin = opencodeV2BinaryPath();
|
|
53
|
+
if (fs.existsSync(localBin))
|
|
54
|
+
res = await detectBinary(localBin);
|
|
55
|
+
}
|
|
56
|
+
if (!res.installed || !res.path) {
|
|
57
|
+
this.detectedPath = undefined;
|
|
38
58
|
return { installed: false, error: "opencode binary not found" };
|
|
59
|
+
}
|
|
60
|
+
this.detectedPath = res.path;
|
|
39
61
|
return { installed: true, path: res.path, version: res.version, details: "opencode run --format json" };
|
|
40
62
|
}
|
|
41
63
|
async listModels(options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver.js","sourceRoot":"","sources":["../../../src/drivers/opencode/driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAIpE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,OAAO,cAAe,SAAQ,aAAa;IACtC,EAAE,GAAG,UAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"driver.js","sourceRoot":"","sources":["../../../src/drivers/opencode/driver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type PaneToggleUi = {
|
|
2
|
+
open: () => Promise<unknown>;
|
|
3
|
+
close: () => Promise<unknown>;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export async function togglePane(isOpen: boolean, ui: PaneToggleUi): Promise<boolean> {
|
|
7
|
+
if (isOpen) {
|
|
8
|
+
await ui.close();
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
await ui.open();
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
@@ -7,6 +7,7 @@ import type { Register } from "claude-code";
|
|
|
7
7
|
import { formatPane, paneButtonLabel, selectPane } from "../mods/agents/pane.js";
|
|
8
8
|
import { parseRows } from "../mods/agents/parse.js";
|
|
9
9
|
import type { PaneSnapshot } from "../mods/agents/types.js";
|
|
10
|
+
import { togglePane } from "./pane-toggle.js";
|
|
10
11
|
|
|
11
12
|
// Stable pane id: 1 to 64 letters, digits, "_" or "-". open carries it into
|
|
12
13
|
// e.requestId on the render event, which is how this module tells its own pane
|
|
@@ -42,6 +43,12 @@ type Engine$ = {
|
|
|
42
43
|
};
|
|
43
44
|
};
|
|
44
45
|
|
|
46
|
+
const toggleAgentsPane = async ($: Engine$, isOpen: boolean): Promise<boolean> =>
|
|
47
|
+
togglePane(isOpen, {
|
|
48
|
+
open: () => $.ui.open({ id: PANE_ID, side: "right" }),
|
|
49
|
+
close: () => $.ui.close({ id: PANE_ID }),
|
|
50
|
+
});
|
|
51
|
+
|
|
45
52
|
// Passing $ into a helper is allowed, verified. What the engine refuses is
|
|
46
53
|
// pulling a namespace off it: `const P = $.process` fails to load the module.
|
|
47
54
|
// So refresh takes $ as a parameter and the state register owns travels beside
|
|
@@ -143,13 +150,7 @@ export const register: Register = (on) => {
|
|
|
143
150
|
// is the one call here not yet verified in a PTY session, paneOpen stays
|
|
144
151
|
// true and the next /band retries the close, instead of the flag and the
|
|
145
152
|
// pane desyncing for the rest of the session.
|
|
146
|
-
|
|
147
|
-
await $.ui.close({ id: PANE_ID });
|
|
148
|
-
paneOpen = false;
|
|
149
|
-
} else {
|
|
150
|
-
await $.ui.open({ id: PANE_ID, side: "right" });
|
|
151
|
-
paneOpen = true;
|
|
152
|
-
}
|
|
153
|
+
paneOpen = await toggleAgentsPane($, paneOpen);
|
|
153
154
|
await $.ui.invalidate("ui.render");
|
|
154
155
|
return { text: paneOpen ? "agents pane open" : "agents pane closed" };
|
|
155
156
|
});
|
|
@@ -175,14 +176,9 @@ export const register: Register = (on) => {
|
|
|
175
176
|
// A $ captured from a past render does work, verified, but it outlives the
|
|
176
177
|
// event it came from and nothing promises how long.
|
|
177
178
|
if ((e as { element?: string }).element === BUTTON_KEY) {
|
|
178
|
-
await
|
|
179
|
-
// Opening an already open pane is a no-op, verified live, so the button
|
|
180
|
-
// never has to know the truth. Re-syncing the flag is the point: the
|
|
181
|
-
// pane's own close box closes it without firing a single hook, and after
|
|
182
|
-
// that one /band would toggle the wrong way.
|
|
183
|
-
paneOpen = true;
|
|
179
|
+
paneOpen = await toggleAgentsPane($, paneOpen);
|
|
184
180
|
await $.ui.invalidate("ui.render");
|
|
185
|
-
void refresh($, state);
|
|
181
|
+
if (paneOpen) void refresh($, state);
|
|
186
182
|
}
|
|
187
183
|
return await next(e);
|
|
188
184
|
});
|
|
@@ -195,10 +191,9 @@ export const register: Register = (on) => {
|
|
|
195
191
|
// wrong shape".
|
|
196
192
|
on("ui.render", async ($, e, next) => {
|
|
197
193
|
if (e.surface !== "terminal") return await next(e);
|
|
198
|
-
// The
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
// is a run behind it.
|
|
194
|
+
// The pane can be closed from its own close box, which fires no hook.
|
|
195
|
+
// Keep one button above the prompt as the visible toggle for the state
|
|
196
|
+
// transitions this module owns.
|
|
202
197
|
if (e.component === "AbovePrompt") {
|
|
203
198
|
if (!state.hasRun) return await next(e);
|
|
204
199
|
const { Box, Button } = await $.ui.resolve(e, "Box", "Button");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Canvas layer of the orchestrator agents pane.
|
|
2
2
|
//
|
|
3
|
-
// The
|
|
3
|
+
// The run canvas, drawn as ASCII boxes for a docked terminal pane.
|
|
4
4
|
// Two pure functions behind one frozen interface: selectPane narrows the
|
|
5
5
|
// daemon's full session list to one run, formatPane turns that snapshot into
|
|
6
6
|
// the lines of the drawing and fits them to the height the pane was given.
|
|
@@ -60,11 +60,14 @@ const STATUS_WORD = vocab({
|
|
|
60
60
|
stopped: "Paused",
|
|
61
61
|
failed: "Failed",
|
|
62
62
|
});
|
|
63
|
+
// Brand marks from the JetBrains Nerd Font and Omarchy icon font. The pane is
|
|
64
|
+
// a terminal surface, so these keep the identity of each harness without
|
|
65
|
+
// spending the width of its full name.
|
|
63
66
|
const HARNESS_GLYPH = vocab({
|
|
64
|
-
claude: "
|
|
65
|
-
opencode: "
|
|
66
|
-
codex: "
|
|
67
|
-
omp: "
|
|
67
|
+
claude: "\uEC82",
|
|
68
|
+
opencode: "\uE902",
|
|
69
|
+
codex: "\uEC81",
|
|
70
|
+
omp: "\uE903",
|
|
68
71
|
});
|
|
69
72
|
const HARNESS_LABEL = vocab({
|
|
70
73
|
claude: "Claude",
|
|
@@ -184,6 +187,29 @@ function statusWord(status: unknown): string {
|
|
|
184
187
|
return STATUS_WORD[text(status)] ?? cell(status);
|
|
185
188
|
}
|
|
186
189
|
|
|
190
|
+
function footerLegend(columns: number): [string, string] {
|
|
191
|
+
const fullStatus = " ● working ◉ waiting ○ finished";
|
|
192
|
+
const fullHarness =
|
|
193
|
+
" " +
|
|
194
|
+
glyph("claude") +
|
|
195
|
+
" Claude " +
|
|
196
|
+
glyph("opencode") +
|
|
197
|
+
" OpenCode " +
|
|
198
|
+
glyph("codex") +
|
|
199
|
+
" Codex " +
|
|
200
|
+
glyph("omp") +
|
|
201
|
+
" OMP";
|
|
202
|
+
if (fullStatus.length <= columns - 2 && fullHarness.length <= columns - 2) {
|
|
203
|
+
return [fullStatus, fullHarness];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const compactStatus = " ● wk ◉ wait ○ done";
|
|
207
|
+
const iconHarness =
|
|
208
|
+
" " + glyph("claude") + " " + glyph("opencode") + " " + glyph("codex") + " " + glyph("omp");
|
|
209
|
+
if (compactStatus.length + 4 <= columns - 2) return [compactStatus, iconHarness];
|
|
210
|
+
return [" ● ◉ ○", iconHarness];
|
|
211
|
+
}
|
|
212
|
+
|
|
187
213
|
function age(iso: unknown, now: number): string {
|
|
188
214
|
if (typeof iso !== "string") return "";
|
|
189
215
|
const then = Date.parse(iso);
|
|
@@ -356,11 +382,12 @@ function draw(snapshot: PaneSnapshot, columns: number, limit: number | undefined
|
|
|
356
382
|
frameSep(),
|
|
357
383
|
frameRow(""),
|
|
358
384
|
];
|
|
385
|
+
const [statusLegend, harnessLegend] = footerLegend(W);
|
|
359
386
|
const tail = [
|
|
360
387
|
frameRow(""),
|
|
361
388
|
frameSep(),
|
|
362
|
-
frameRow(
|
|
363
|
-
frameRow(
|
|
389
|
+
frameRow(statusLegend),
|
|
390
|
+
frameRow(harnessLegend),
|
|
364
391
|
frameBot(),
|
|
365
392
|
];
|
|
366
393
|
const fixed = head.length + tail.length;
|
|
@@ -443,10 +470,14 @@ export function paneButtonLabel(snapshot: PaneSnapshot | undefined): string {
|
|
|
443
470
|
if (!snapshot || typeof snapshot !== "object") return "agents";
|
|
444
471
|
try {
|
|
445
472
|
const rows = Array.isArray(snapshot.rows) ? snapshot.rows : [];
|
|
446
|
-
const
|
|
447
|
-
if (
|
|
448
|
-
|
|
449
|
-
|
|
473
|
+
const workingRows = rows.filter((row) => LIVE.has(row?.status ?? ""));
|
|
474
|
+
if (workingRows.length > 0) {
|
|
475
|
+
return `${workingRows.length} working ${workingRows.map((row) => glyph(row?.agent)).join("")}`;
|
|
476
|
+
}
|
|
477
|
+
const waitingRows = rows.filter((row) => WAIT.has(row?.status ?? ""));
|
|
478
|
+
return waitingRows.length > 0
|
|
479
|
+
? `${waitingRows.length} waiting for you ${waitingRows.map((row) => glyph(row?.agent)).join("")}`
|
|
480
|
+
: "no active agents";
|
|
450
481
|
} catch {
|
|
451
482
|
return "agents";
|
|
452
483
|
}
|
package/dist/web/review-page.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Local staged/unstaged review UI served by `codedeck
|
|
1
|
+
/** Local staged/unstaged review UI served by `codedeck review`.
|
|
2
2
|
*
|
|
3
3
|
* Self-contained page (no external requests besides `api/review`): file
|
|
4
4
|
* tree on the left, unified diff on the right, per-line comment boxes
|
|
@@ -103,7 +103,6 @@ export const REVIEW_PAGE = `<!doctype html>
|
|
|
103
103
|
<span class="spacer"></span>
|
|
104
104
|
<input id="filter" type="search" placeholder="filtrar arquivos…" aria-label="Filtrar arquivos">
|
|
105
105
|
<button id="copyAll" type="button" title="Copiar todos os comentários em markdown">Copiar todos (<span id="nComments">0</span>)</button>
|
|
106
|
-
<a href="/" style="color:#98989f;font-size:12.5px">canvas</a>
|
|
107
106
|
</header>
|
|
108
107
|
<div id="layout">
|
|
109
108
|
<nav id="files" aria-label="Arquivos"><h2>ARQUIVOS</h2><ul id="fileList"></ul></nav>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"review-page.js","sourceRoot":"","sources":["../../src/web/review-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAW
|
|
1
|
+
{"version":3,"file":"review-page.js","sourceRoot":"","sources":["../../src/web/review-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8oB3B,CAAC"}
|
package/package.json
CHANGED