agent-dag 1.0.2 → 1.0.4
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/bin/agent-dag.js +181 -0
- package/dist/web/assets/{index-BgTeq5uZ.js → index-CE9bBmyB.js} +12 -12
- package/dist/web/assets/{index-BbbosdE2.css → index-oVfhlSKU.css} +1 -1
- package/dist/web/index.html +2 -2
- package/hook/hook.js +5 -5
- package/package.json +3 -3
- package/src/server/index.mjs +5 -5
- package/src/server/installer.mjs +8 -8
- package/bin/ccgraph.js +0 -121
package/bin/agent-dag.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// agent-dag CLI entrypoint. Registers hooks, starts server, opens browser.
|
|
3
|
+
import { resolve, dirname, join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const PKG_ROOT = resolve(__dirname, "..");
|
|
10
|
+
|
|
11
|
+
const argv = process.argv.slice(2);
|
|
12
|
+
const flags = parseArgs(argv);
|
|
13
|
+
|
|
14
|
+
if (flags.help) {
|
|
15
|
+
printHelp();
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (flags.uninstall) {
|
|
20
|
+
const { uninstallHooks } = await import(pathToFileURL(join(PKG_ROOT, "src/server/installer.mjs")).href);
|
|
21
|
+
const r = await uninstallHooks();
|
|
22
|
+
console.log(r.changed ? "agent-dag: hooks removed from ~/.claude/settings.json" : "agent-dag: no hooks to remove");
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const port = Number(flags.port ?? process.env.AGENT_DAG_PORT ?? 4317);
|
|
27
|
+
const workspace = flags.all ? "" : (flags.workspace ?? process.cwd());
|
|
28
|
+
const openBrowser = flags.noOpen !== true;
|
|
29
|
+
const persist = flags.noPersist
|
|
30
|
+
? null
|
|
31
|
+
: (flags.history ?? join(homedir(), ".claude", "agent-dag", "events.jsonl"));
|
|
32
|
+
|
|
33
|
+
const { installHooks, writeDiscovery, removeDiscovery } =
|
|
34
|
+
await import(pathToFileURL(join(PKG_ROOT, "src/server/installer.mjs")).href);
|
|
35
|
+
const { startServer } =
|
|
36
|
+
await import(pathToFileURL(join(PKG_ROOT, "src/server/index.mjs")).href);
|
|
37
|
+
|
|
38
|
+
const WEB_DIST = join(PKG_ROOT, "dist", "web", "index.html");
|
|
39
|
+
if (!existsSync(WEB_DIST)) {
|
|
40
|
+
console.error("agent-dag: ui not built. run `npm run build` (or `pnpm build`) first.");
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── ANSI helpers ──────────────────────────────────────────────────────────────
|
|
45
|
+
const tty = process.stdout.isTTY;
|
|
46
|
+
const C = {
|
|
47
|
+
reset: tty ? "\x1b[0m" : "",
|
|
48
|
+
bold: tty ? "\x1b[1m" : "",
|
|
49
|
+
dim: tty ? "\x1b[2m" : "",
|
|
50
|
+
cyan: tty ? "\x1b[36m" : "",
|
|
51
|
+
blue: tty ? "\x1b[34m" : "",
|
|
52
|
+
magenta: tty ? "\x1b[35m" : "",
|
|
53
|
+
yellow: tty ? "\x1b[33m" : "",
|
|
54
|
+
green: tty ? "\x1b[32m" : "",
|
|
55
|
+
white: tty ? "\x1b[97m" : "",
|
|
56
|
+
bCyan: tty ? "\x1b[96m" : "",
|
|
57
|
+
bMag: tty ? "\x1b[95m" : "",
|
|
58
|
+
};
|
|
59
|
+
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
|
60
|
+
|
|
61
|
+
// ── Animated banner ───────────────────────────────────────────────────────────
|
|
62
|
+
async function printBanner() {
|
|
63
|
+
const W = "══════════════════════════════════════";
|
|
64
|
+
const lines = [
|
|
65
|
+
"",
|
|
66
|
+
` ${C.cyan}${C.bold}╔${W}╗${C.reset}`,
|
|
67
|
+
` ${C.blue}${C.bold}║${C.reset} ${C.bCyan}${C.bold}◉ agent-dag${C.reset} ${C.dim}v1.0.4${C.reset} ${C.blue}${C.bold}║${C.reset}`,
|
|
68
|
+
` ${C.magenta}${C.bold}║${C.reset} ${C.dim}live DAG · Claude Code agents${C.reset} ${C.magenta}${C.bold}║${C.reset}`,
|
|
69
|
+
` ${C.bMag}${C.bold}║${C.reset} ${C.yellow}watch agents fork ${C.cyan}→${C.reset} ${C.green}tools fire${C.reset} ${C.bMag}${C.bold}║${C.reset}`,
|
|
70
|
+
` ${C.cyan}${C.bold}╚${W}╝${C.reset}`,
|
|
71
|
+
"",
|
|
72
|
+
];
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
process.stdout.write(line + "\n");
|
|
75
|
+
if (tty) await sleep(45);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Spinner ───────────────────────────────────────────────────────────────────
|
|
80
|
+
function spinner(label) {
|
|
81
|
+
if (!tty) { process.stdout.write(` … ${label}\n`); return { stop: (ok, msg) => process.stdout.write(` ${ok ? "✓" : "✗"} ${msg}\n`) }; }
|
|
82
|
+
const frames = ["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];
|
|
83
|
+
let i = 0;
|
|
84
|
+
const iv = setInterval(() => {
|
|
85
|
+
process.stdout.write(`\r ${C.cyan}${frames[i++ % frames.length]}${C.reset} ${label}`);
|
|
86
|
+
}, 80);
|
|
87
|
+
return {
|
|
88
|
+
stop(ok, msg) {
|
|
89
|
+
clearInterval(iv);
|
|
90
|
+
const icon = ok ? `${C.green}✓${C.reset}` : `${C.yellow}✗${C.reset}`;
|
|
91
|
+
process.stdout.write(`\r ${icon} ${msg}\n`);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
await printBanner();
|
|
97
|
+
|
|
98
|
+
// ── Startup steps ─────────────────────────────────────────────────────────────
|
|
99
|
+
process.stdout.write(` ${C.dim}workspace :${C.reset} ${workspace === "" ? C.yellow + "(all)" + C.reset : workspace}\n`);
|
|
100
|
+
|
|
101
|
+
let sp = spinner("installing hooks…");
|
|
102
|
+
const { settingsPath, hookPath } = await installHooks();
|
|
103
|
+
sp.stop(true, `hooks installed ${C.dim}→ ${hookPath}${C.reset}`);
|
|
104
|
+
|
|
105
|
+
sp = spinner("starting server…");
|
|
106
|
+
const server = await startServer({ port, persist }).catch(err => {
|
|
107
|
+
sp.stop(false, `server failed: ${err.message}`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
});
|
|
110
|
+
const addr = server.address();
|
|
111
|
+
const realPort = typeof addr === "object" && addr ? addr.port : port;
|
|
112
|
+
const url = `http://127.0.0.1:${realPort}`;
|
|
113
|
+
sp.stop(true, `server ready ${C.dim}→ ${C.reset}${C.bCyan}${C.bold}${url}${C.reset}`);
|
|
114
|
+
|
|
115
|
+
if (persist) process.stdout.write(` ${C.dim}log : ${persist}${C.reset}\n`);
|
|
116
|
+
|
|
117
|
+
process.stdout.write(`\n ${C.green}${C.bold}▶ opening browser…${C.reset}\n\n`);
|
|
118
|
+
|
|
119
|
+
const discoveryFile = await writeDiscovery({ port: realPort, workspace });
|
|
120
|
+
|
|
121
|
+
if (openBrowser) {
|
|
122
|
+
try {
|
|
123
|
+
const { default: open } = await import("open");
|
|
124
|
+
await open(url);
|
|
125
|
+
} catch {}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ── Pulse indicator ───────────────────────────────────────────────────────────
|
|
129
|
+
if (tty) {
|
|
130
|
+
const pulseFrames = [`${C.green}●${C.reset}`, `${C.dim}●${C.reset}`];
|
|
131
|
+
let pi = 0;
|
|
132
|
+
setInterval(() => {
|
|
133
|
+
process.stdout.write(`\r ${pulseFrames[pi++ % 2]} ${C.dim}listening — Ctrl+C to stop${C.reset} `);
|
|
134
|
+
}, 800).unref();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const shutdown = async () => {
|
|
138
|
+
if (tty) process.stdout.write(`\n\n ${C.yellow}◉ shutting down…${C.reset}\n`);
|
|
139
|
+
await removeDiscovery(discoveryFile);
|
|
140
|
+
server.close(() => process.exit(0));
|
|
141
|
+
setTimeout(() => process.exit(0), 1500).unref();
|
|
142
|
+
};
|
|
143
|
+
process.on("SIGINT", shutdown);
|
|
144
|
+
process.on("SIGTERM", shutdown);
|
|
145
|
+
process.on("beforeExit", () => removeDiscovery(discoveryFile));
|
|
146
|
+
|
|
147
|
+
// ── helpers ───────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
function parseArgs(args) {
|
|
150
|
+
const out = {};
|
|
151
|
+
for (let i = 0; i < args.length; i++) {
|
|
152
|
+
const a = args[i];
|
|
153
|
+
if (a === "-h" || a === "--help") out.help = true;
|
|
154
|
+
else if (a === "-p" || a === "--port") out.port = args[++i];
|
|
155
|
+
else if (a === "--no-open") out.noOpen = true;
|
|
156
|
+
else if (a === "--uninstall") out.uninstall = true;
|
|
157
|
+
else if (a === "--workspace") out.workspace = args[++i];
|
|
158
|
+
else if (a === "--all") out.all = true;
|
|
159
|
+
else if (a === "--no-persist") out.noPersist = true;
|
|
160
|
+
else if (a === "--history") out.history = args[++i];
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function printHelp() {
|
|
166
|
+
process.stdout.write(`agent-dag — live DAG of Claude Code agents
|
|
167
|
+
|
|
168
|
+
Usage:
|
|
169
|
+
agent-dag [options]
|
|
170
|
+
|
|
171
|
+
Options:
|
|
172
|
+
-p, --port <number> Preferred port (default: 4317; falls back to random 4318–4400)
|
|
173
|
+
--no-open Don't open the browser automatically
|
|
174
|
+
--workspace <path> Workspace root (default: cwd)
|
|
175
|
+
--all Capture sessions from ALL workspaces (machine-wide)
|
|
176
|
+
--history <path> Override events log file (default: ~/.claude/agent-dag/events.jsonl)
|
|
177
|
+
--no-persist Don't write or replay events log (RAM-only)
|
|
178
|
+
--uninstall Remove agent-dag hook entries from ~/.claude/settings.json
|
|
179
|
+
-h, --help Show this help
|
|
180
|
+
`);
|
|
181
|
+
}
|