aimux-cli 0.1.3 → 0.1.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/README.md +20 -19
- package/dist/agent-tracker.js +15 -13
- package/dist/agent-tracker.js.map +1 -1
- package/dist/context/context-bridge.js +8 -1
- package/dist/context/context-bridge.js.map +1 -1
- package/dist/daemon.js +8 -1
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard-pending-actions.js +1 -1
- package/dist/dashboard-pending-actions.js.map +1 -1
- package/dist/dashboard-session-actions.d.ts +4 -4
- package/dist/dashboard-session-actions.js.map +1 -1
- package/dist/dashboard.d.ts +2 -2
- package/dist/key-parser.js +7 -0
- package/dist/key-parser.js.map +1 -1
- package/dist/main.js +227 -41
- package/dist/main.js.map +1 -1
- package/dist/metadata-server.d.ts +18 -0
- package/dist/metadata-server.js +22 -0
- package/dist/metadata-server.js.map +1 -1
- package/dist/multiplexer.d.ts +14 -0
- package/dist/multiplexer.js +329 -41
- package/dist/multiplexer.js.map +1 -1
- package/dist/notification-context.d.ts +1 -0
- package/dist/notification-context.js +12 -0
- package/dist/notification-context.js.map +1 -1
- package/dist/plugin-runtime.js +8 -2
- package/dist/plugin-runtime.js.map +1 -1
- package/dist/project-scanner.js +14 -3
- package/dist/project-scanner.js.map +1 -1
- package/dist/shell-hooks.d.ts +27 -0
- package/dist/shell-hooks.js +137 -0
- package/dist/shell-hooks.js.map +1 -0
- package/dist/tmux-doctor.d.ts +10 -0
- package/dist/tmux-doctor.js +62 -0
- package/dist/tmux-doctor.js.map +1 -1
- package/dist/tmux-runtime-manager.d.ts +8 -0
- package/dist/tmux-runtime-manager.js +51 -12
- package/dist/tmux-runtime-manager.js.map +1 -1
- package/dist/tmux-statusline.js +1 -1
- package/dist/tmux-statusline.js.map +1 -1
- package/dist/tool-output-watchers.js +128 -33
- package/dist/tool-output-watchers.js.map +1 -1
- package/dist/tui/screens/dashboard-renderers.js +2 -1
- package/dist/tui/screens/dashboard-renderers.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -5,13 +5,13 @@ import { homedir } from "node:os";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { Multiplexer } from "./multiplexer.js";
|
|
7
7
|
import { llmCompact } from "./context/compactor.js";
|
|
8
|
-
import { initProject } from "./config.js";
|
|
8
|
+
import { initProject, loadConfig } from "./config.js";
|
|
9
9
|
import { initPaths, getHistoryDir, getGraveyardPath, getStatePath, getContextDir } from "./paths.js";
|
|
10
10
|
import { loadTeamConfig, saveTeamConfig, getDefaultTeamConfig } from "./team.js";
|
|
11
11
|
import { createWorktree, findMainRepo, listWorktrees } from "./worktree.js";
|
|
12
12
|
import { TmuxRuntimeManager } from "./tmux-runtime-manager.js";
|
|
13
|
-
import { buildTmuxDoctorReport, renderTmuxDoctorReport } from "./tmux-doctor.js";
|
|
14
|
-
import { loadMetadataEndpoint, resolveProjectServiceEndpoint as resolveStoredProjectServiceEndpoint, updateSessionMetadata, clearSessionLogs, removeMetadataEndpoint, } from "./metadata-store.js";
|
|
13
|
+
import { buildTmuxDoctorReport, renderTmuxDoctorReport, renderTmuxRepairResult, repairTmuxRuntime, } from "./tmux-doctor.js";
|
|
14
|
+
import { loadMetadataEndpoint, loadMetadataState, resolveProjectServiceEndpoint as resolveStoredProjectServiceEndpoint, updateSessionMetadata, clearSessionLogs, removeMetadataEndpoint, } from "./metadata-store.js";
|
|
15
15
|
import { AgentTracker } from "./agent-tracker.js";
|
|
16
16
|
import { AimuxDaemon, ensureDaemonRunning, ensureProjectService, loadDaemonInfo, loadDaemonState, projectServiceStatus, requestDaemonJson, stopDaemon, stopProjectService, } from "./daemon.js";
|
|
17
17
|
import { getProjectServiceManifest, manifestsMatch } from "./project-service-manifest.js";
|
|
@@ -37,7 +37,6 @@ class ProjectServiceVersionError extends Error {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
function renderProjectServiceVersionHelp(error) {
|
|
40
|
-
const quotedProject = JSON.stringify(error.projectRoot);
|
|
41
40
|
const lines = [
|
|
42
41
|
"aimux: the running project service is from a different local build.",
|
|
43
42
|
"",
|
|
@@ -45,11 +44,11 @@ function renderProjectServiceVersionHelp(error) {
|
|
|
45
44
|
`Expected build: ${error.expected.buildStamp}`,
|
|
46
45
|
`Running build: ${error.actual?.buildStamp ?? "unknown"}`,
|
|
47
46
|
"",
|
|
48
|
-
"Restart the
|
|
49
|
-
|
|
50
|
-
` aimux daemon project-ensure --project ${quotedProject}`,
|
|
47
|
+
"Restart the project runtime, then retry:",
|
|
48
|
+
" aimux restart-runtime --open",
|
|
51
49
|
"",
|
|
52
|
-
"
|
|
50
|
+
"If the mismatch persists, use the advanced daemon restart path:",
|
|
51
|
+
" aimux daemon restart",
|
|
53
52
|
];
|
|
54
53
|
return lines.join("\n");
|
|
55
54
|
}
|
|
@@ -233,6 +232,51 @@ async function ensureDaemonProjectSpawned(projectRoot) {
|
|
|
233
232
|
await ensureDaemonRunning();
|
|
234
233
|
await ensureProjectService(projectRoot);
|
|
235
234
|
}
|
|
235
|
+
function listManagedProjectSessionNames(tmux, projectRoot) {
|
|
236
|
+
const hostSession = tmux.getProjectSession(projectRoot).sessionName;
|
|
237
|
+
return tmux
|
|
238
|
+
.listSessionNames()
|
|
239
|
+
.filter((sessionName) => sessionName === hostSession || sessionName.startsWith(`${hostSession}-client-`))
|
|
240
|
+
.sort((a, b) => {
|
|
241
|
+
const aIsHost = a === hostSession ? 1 : 0;
|
|
242
|
+
const bIsHost = b === hostSession ? 1 : 0;
|
|
243
|
+
return aIsHost - bIsHost;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
function stopProjectTmuxRuntime(tmux, projectRoot) {
|
|
247
|
+
const killed = [];
|
|
248
|
+
for (const sessionName of listManagedProjectSessionNames(tmux, projectRoot)) {
|
|
249
|
+
if (!tmux.hasSession(sessionName))
|
|
250
|
+
continue;
|
|
251
|
+
tmux.killSession(sessionName);
|
|
252
|
+
killed.push(sessionName);
|
|
253
|
+
}
|
|
254
|
+
return killed;
|
|
255
|
+
}
|
|
256
|
+
async function stopProjectRuntime(projectRoot) {
|
|
257
|
+
const projectService = await stopProjectService(projectRoot);
|
|
258
|
+
removeMetadataEndpoint(projectRoot);
|
|
259
|
+
const tmux = new TmuxRuntimeManager();
|
|
260
|
+
const tmuxSessionsKilled = tmux.isAvailable() ? stopProjectTmuxRuntime(tmux, projectRoot) : [];
|
|
261
|
+
return {
|
|
262
|
+
projectServiceStopped: Boolean(projectService),
|
|
263
|
+
tmuxSessionsKilled,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
async function restartProjectRuntime(projectRoot, opts = {}) {
|
|
267
|
+
await stopProjectRuntime(projectRoot);
|
|
268
|
+
await ensureDaemonProjectSpawned(projectRoot);
|
|
269
|
+
const tmux = new TmuxRuntimeManager();
|
|
270
|
+
ensureTmuxAvailable(tmux);
|
|
271
|
+
const resolved = resolveDashboardTarget(projectRoot, tmux, { forceReload: true });
|
|
272
|
+
if (opts.open) {
|
|
273
|
+
tmux.openTarget(resolved.dashboardTarget, { insideTmux: tmux.isInsideTmux(), alreadyResolved: true });
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
dashboardSessionName: resolved.dashboardSession.sessionName,
|
|
277
|
+
dashboardTarget: resolved.dashboardTarget,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
236
280
|
function resolveProjectRoot(cwd) {
|
|
237
281
|
try {
|
|
238
282
|
return findMainRepo(cwd);
|
|
@@ -366,7 +410,7 @@ program
|
|
|
366
410
|
});
|
|
367
411
|
program
|
|
368
412
|
.command("dashboard-reload")
|
|
369
|
-
.description("
|
|
413
|
+
.description("Recreate and optionally reopen the dashboard window only")
|
|
370
414
|
.option("--open", "Open the dashboard after reloading")
|
|
371
415
|
.action(async (opts) => {
|
|
372
416
|
try {
|
|
@@ -392,10 +436,93 @@ program
|
|
|
392
436
|
process.exit(1);
|
|
393
437
|
}
|
|
394
438
|
});
|
|
395
|
-
|
|
439
|
+
program
|
|
440
|
+
.command("stop [sessionId]")
|
|
441
|
+
.description("Stop the current project runtime, or stop a specific running agent by session ID")
|
|
442
|
+
.option("--project <path>", "Project path")
|
|
443
|
+
.option("--json", "Emit JSON")
|
|
444
|
+
.action(async (sessionId, opts) => {
|
|
445
|
+
try {
|
|
446
|
+
if (sessionId) {
|
|
447
|
+
const projectRoot = await prepareProjectContext(opts.project);
|
|
448
|
+
const mux = new Multiplexer();
|
|
449
|
+
const result = await mux.stopAgent(sessionId);
|
|
450
|
+
if (opts.json) {
|
|
451
|
+
console.log(JSON.stringify({
|
|
452
|
+
ok: true,
|
|
453
|
+
projectRoot,
|
|
454
|
+
sessionId: result.sessionId,
|
|
455
|
+
status: result.status,
|
|
456
|
+
}, null, 2));
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
console.log(`stopped ${result.sessionId}`);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const projectRoot = resolveProjectRoot(opts.project ?? process.cwd());
|
|
463
|
+
await initPaths(projectRoot);
|
|
464
|
+
const result = await stopProjectRuntime(projectRoot);
|
|
465
|
+
if (opts.json) {
|
|
466
|
+
console.log(JSON.stringify({
|
|
467
|
+
ok: true,
|
|
468
|
+
projectRoot,
|
|
469
|
+
projectServiceStopped: result.projectServiceStopped,
|
|
470
|
+
tmuxSessionsKilled: result.tmuxSessionsKilled,
|
|
471
|
+
}, null, 2));
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
console.log(`Stopped project runtime for ${projectRoot}`);
|
|
475
|
+
if (result.tmuxSessionsKilled.length > 0) {
|
|
476
|
+
console.log(`Removed tmux sessions: ${result.tmuxSessionsKilled.join(", ")}`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
catch (err) {
|
|
480
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
481
|
+
console.error(`Error: ${msg}`);
|
|
482
|
+
process.exit(1);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
program
|
|
486
|
+
.command("restart-runtime")
|
|
487
|
+
.description("Hard restart the current project runtime and rebuild its managed tmux topology")
|
|
488
|
+
.option("--project-root <path>", "Project root", process.cwd())
|
|
489
|
+
.option("--open", "Open the dashboard after restarting the runtime")
|
|
490
|
+
.option("--json", "Emit JSON")
|
|
491
|
+
.action(async (opts) => {
|
|
492
|
+
try {
|
|
493
|
+
const projectRoot = resolveProjectRoot(opts.projectRoot);
|
|
494
|
+
await initPaths(projectRoot);
|
|
495
|
+
const result = await restartProjectRuntime(projectRoot, { open: opts.open });
|
|
496
|
+
if (opts.open)
|
|
497
|
+
return;
|
|
498
|
+
if (opts.json) {
|
|
499
|
+
console.log(JSON.stringify({
|
|
500
|
+
ok: true,
|
|
501
|
+
projectRoot,
|
|
502
|
+
dashboardSession: result.dashboardSessionName,
|
|
503
|
+
dashboardTarget: result.dashboardTarget,
|
|
504
|
+
}, null, 2));
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
console.log(`Restarted project runtime for ${projectRoot}`);
|
|
508
|
+
console.log(`Dashboard: ${result.dashboardSessionName}:${result.dashboardTarget.windowIndex}`);
|
|
509
|
+
}
|
|
510
|
+
catch (err) {
|
|
511
|
+
if (err instanceof ProjectServiceVersionError) {
|
|
512
|
+
console.error(renderProjectServiceVersionHelp(err));
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
515
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
516
|
+
console.error(`Error: ${msg}`);
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
const hostCmd = program
|
|
521
|
+
.command("host")
|
|
522
|
+
.description("Advanced compatibility wrappers for legacy daemon-managed project services");
|
|
396
523
|
program
|
|
397
524
|
.command("serve")
|
|
398
|
-
.description("
|
|
525
|
+
.description("Advanced: ensure the legacy daemon-backed project control service is running")
|
|
399
526
|
.action(async () => {
|
|
400
527
|
const projectRoot = resolveProjectRoot(process.cwd());
|
|
401
528
|
if (projectRoot !== process.cwd()) {
|
|
@@ -639,7 +766,7 @@ hostCmd
|
|
|
639
766
|
hostCmd.action(() => {
|
|
640
767
|
console.log("`aimux host` is a compatibility alias for daemon-managed project services.");
|
|
641
768
|
});
|
|
642
|
-
const daemonCmd = program.command("daemon").description("
|
|
769
|
+
const daemonCmd = program.command("daemon").description("Advanced: manage the global aimux control-plane daemon");
|
|
643
770
|
daemonCmd
|
|
644
771
|
.command("run")
|
|
645
772
|
.description("Internal daemon entrypoint")
|
|
@@ -1667,33 +1794,6 @@ graveyardCmd
|
|
|
1667
1794
|
process.exit(1);
|
|
1668
1795
|
}
|
|
1669
1796
|
});
|
|
1670
|
-
program
|
|
1671
|
-
.command("stop <sessionId>")
|
|
1672
|
-
.description("Stop a running agent and move it to offline state")
|
|
1673
|
-
.option("--project <path>", "Project path")
|
|
1674
|
-
.option("--json", "Emit JSON")
|
|
1675
|
-
.action(async (sessionId, opts) => {
|
|
1676
|
-
try {
|
|
1677
|
-
const projectRoot = await prepareProjectContext(opts.project);
|
|
1678
|
-
const mux = new Multiplexer();
|
|
1679
|
-
const result = await mux.stopAgent(sessionId);
|
|
1680
|
-
if (opts.json) {
|
|
1681
|
-
console.log(JSON.stringify({
|
|
1682
|
-
ok: true,
|
|
1683
|
-
projectRoot,
|
|
1684
|
-
sessionId: result.sessionId,
|
|
1685
|
-
status: result.status,
|
|
1686
|
-
}, null, 2));
|
|
1687
|
-
return;
|
|
1688
|
-
}
|
|
1689
|
-
console.log(`stopped ${result.sessionId}`);
|
|
1690
|
-
}
|
|
1691
|
-
catch (err) {
|
|
1692
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1693
|
-
console.error(`Error: ${msg}`);
|
|
1694
|
-
process.exit(1);
|
|
1695
|
-
}
|
|
1696
|
-
});
|
|
1697
1797
|
program
|
|
1698
1798
|
.command("rename <sessionId>")
|
|
1699
1799
|
.description("Rename an agent label in running or offline state")
|
|
@@ -1808,10 +1908,11 @@ program
|
|
|
1808
1908
|
});
|
|
1809
1909
|
// ── Statusline commands ────────────────────────────────────────────
|
|
1810
1910
|
const statuslineCmd = program.command("statusline").description("Manage Claude Code statusline integration");
|
|
1811
|
-
const doctorCmd = program.command("doctor").description("Inspect aimux runtime
|
|
1911
|
+
const doctorCmd = program.command("doctor").description("Inspect aimux runtime state");
|
|
1912
|
+
const repairCmd = program.command("repair").description("Repair the current project runtime in place");
|
|
1812
1913
|
doctorCmd
|
|
1813
1914
|
.command("tmux")
|
|
1814
|
-
.description("Inspect managed tmux
|
|
1915
|
+
.description("Inspect managed tmux runtime state")
|
|
1815
1916
|
.option("--project-root <path>", "Project root", process.cwd())
|
|
1816
1917
|
.option("--session <name>", "Managed tmux session name override")
|
|
1817
1918
|
.option("--window-id <id>", "Specific tmux window id to inspect")
|
|
@@ -1830,6 +1931,28 @@ doctorCmd
|
|
|
1830
1931
|
}
|
|
1831
1932
|
console.log(renderTmuxDoctorReport(report));
|
|
1832
1933
|
});
|
|
1934
|
+
repairCmd
|
|
1935
|
+
.option("--project-root <path>", "Project root", process.cwd())
|
|
1936
|
+
.option("--open", "Open the repaired dashboard after fixing runtime state")
|
|
1937
|
+
.option("--json", "Emit JSON")
|
|
1938
|
+
.action(async (opts) => {
|
|
1939
|
+
const projectRoot = resolveProjectRoot(opts.projectRoot);
|
|
1940
|
+
await initPaths(projectRoot);
|
|
1941
|
+
await ensureDaemonProjectSpawned(projectRoot);
|
|
1942
|
+
const tmux = new TmuxRuntimeManager();
|
|
1943
|
+
ensureTmuxAvailable(tmux);
|
|
1944
|
+
const result = repairTmuxRuntime(tmux, { projectRoot });
|
|
1945
|
+
if (opts.open) {
|
|
1946
|
+
const { dashboardTarget } = resolveDashboardTarget(projectRoot, tmux);
|
|
1947
|
+
tmux.openTarget(dashboardTarget, { insideTmux: tmux.isInsideTmux(), alreadyResolved: true });
|
|
1948
|
+
return;
|
|
1949
|
+
}
|
|
1950
|
+
if (opts.json) {
|
|
1951
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1952
|
+
return;
|
|
1953
|
+
}
|
|
1954
|
+
console.log(renderTmuxRepairResult(result));
|
|
1955
|
+
});
|
|
1833
1956
|
const metadataCmd = program.command("metadata").description("Push metadata into aimux tmux status integration");
|
|
1834
1957
|
const metadataTracker = new AgentTracker();
|
|
1835
1958
|
metadataCmd
|
|
@@ -2006,6 +2129,69 @@ program
|
|
|
2006
2129
|
}
|
|
2007
2130
|
console.log("OK");
|
|
2008
2131
|
});
|
|
2132
|
+
program
|
|
2133
|
+
.command("shell-hook <state>")
|
|
2134
|
+
.description("Internal generic shell-state adapter modeled after cmux")
|
|
2135
|
+
.requiredOption("--session <sessionId>", "Aimux session id")
|
|
2136
|
+
.requiredOption("--project <path>", "Project path")
|
|
2137
|
+
.option("--tool <tool>", "Tool label", "shell")
|
|
2138
|
+
.option("--json", "Emit JSON output")
|
|
2139
|
+
.action(async (state, opts) => {
|
|
2140
|
+
const projectRoot = resolveProjectRoot(pathResolve(opts.project));
|
|
2141
|
+
await initPaths(projectRoot);
|
|
2142
|
+
const sessionId = opts.session.trim();
|
|
2143
|
+
const tool = opts.tool?.trim() || "shell";
|
|
2144
|
+
const previous = loadMetadataState(projectRoot).sessions[sessionId]?.derived;
|
|
2145
|
+
const previousActivity = previous?.activity;
|
|
2146
|
+
const result = { ok: true, state, sessionId, tool };
|
|
2147
|
+
const setActivity = async (activity) => postLiveProjectServiceJsonOrLocal(projectRoot, "/set-activity", { session: sessionId, activity }, () => metadataTracker.setActivity(sessionId, activity, projectRoot));
|
|
2148
|
+
const setAttention = async (attention) => postLiveProjectServiceJsonOrLocal(projectRoot, "/set-attention", { session: sessionId, attention }, () => metadataTracker.setAttention(sessionId, attention, projectRoot));
|
|
2149
|
+
const clearSessionNotifications = async () => postLiveProjectServiceJsonOrLocal(projectRoot, "/notifications/clear", { sessionId }, () => ({
|
|
2150
|
+
ok: true,
|
|
2151
|
+
cleared: clearNotifications({ sessionId }),
|
|
2152
|
+
}));
|
|
2153
|
+
if (state === "running" || state === "command" || state === "busy") {
|
|
2154
|
+
if (previousActivity !== "running") {
|
|
2155
|
+
await clearSessionNotifications();
|
|
2156
|
+
await setActivity("running");
|
|
2157
|
+
await setAttention("normal");
|
|
2158
|
+
await postLiveProjectServiceJsonOrLocal(projectRoot, "/mark-seen", { session: sessionId }, () => metadataTracker.markSeen(sessionId, projectRoot));
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
else if (state === "prompt" || state === "idle") {
|
|
2162
|
+
if (previousActivity !== "idle") {
|
|
2163
|
+
await setActivity("idle");
|
|
2164
|
+
await setAttention("normal");
|
|
2165
|
+
}
|
|
2166
|
+
const config = loadConfig().notifications;
|
|
2167
|
+
if (config.enabled && config.onComplete && previousActivity === "running") {
|
|
2168
|
+
await postLiveProjectServiceJsonOrLocal(projectRoot, "/notify", {
|
|
2169
|
+
title: tool,
|
|
2170
|
+
subtitle: "Command complete",
|
|
2171
|
+
message: "Shell returned to a prompt.",
|
|
2172
|
+
sessionId,
|
|
2173
|
+
kind: "task_done",
|
|
2174
|
+
}, () => ({
|
|
2175
|
+
ok: true,
|
|
2176
|
+
notification: addNotification({
|
|
2177
|
+
title: tool,
|
|
2178
|
+
subtitle: "Command complete",
|
|
2179
|
+
body: "Shell returned to a prompt.",
|
|
2180
|
+
sessionId,
|
|
2181
|
+
kind: "task_done",
|
|
2182
|
+
}),
|
|
2183
|
+
}));
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
else {
|
|
2187
|
+
throw new Error(`Unsupported shell hook state: ${state}`);
|
|
2188
|
+
}
|
|
2189
|
+
if (opts.json) {
|
|
2190
|
+
console.log(JSON.stringify(result));
|
|
2191
|
+
return;
|
|
2192
|
+
}
|
|
2193
|
+
console.log("OK");
|
|
2194
|
+
});
|
|
2009
2195
|
program
|
|
2010
2196
|
.command("list-notifications")
|
|
2011
2197
|
.description("List project notifications")
|