@songsid/agend 2.1.1-beta.2 → 2.1.1-beta.21
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/README.zh-TW.md +1 -0
- package/dist/access-path.js +15 -6
- package/dist/access-path.js.map +1 -1
- package/dist/agent-cli-instructions.md +9 -0
- package/dist/backend/antigravity.d.ts +4 -1
- package/dist/backend/antigravity.js +24 -13
- package/dist/backend/antigravity.js.map +1 -1
- package/dist/backend/claude-code.d.ts +37 -0
- package/dist/backend/claude-code.js +47 -8
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex.d.ts +17 -0
- package/dist/backend/codex.js +279 -62
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/gemini-cli.js +4 -7
- package/dist/backend/gemini-cli.js.map +1 -1
- package/dist/backend/grok.d.ts +24 -0
- package/dist/backend/grok.js +57 -8
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/kiro.d.ts +27 -0
- package/dist/backend/kiro.js +96 -15
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/opencode.js +4 -7
- package/dist/backend/opencode.js.map +1 -1
- package/dist/backend/types.d.ts +44 -0
- package/dist/backend/types.js +3 -3
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +15 -0
- package/dist/channel/adapters/discord.js +94 -6
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/adapters/telegram.d.ts +21 -0
- package/dist/channel/adapters/telegram.js +132 -15
- package/dist/channel/adapters/telegram.js.map +1 -1
- package/dist/channel/ipc-bridge.d.ts +17 -1
- package/dist/channel/ipc-bridge.js +53 -17
- package/dist/channel/ipc-bridge.js.map +1 -1
- package/dist/channel/ipc-timeouts.d.ts +40 -0
- package/dist/channel/ipc-timeouts.js +58 -0
- package/dist/channel/ipc-timeouts.js.map +1 -0
- package/dist/channel/mcp-server.js +30 -14
- package/dist/channel/mcp-server.js.map +1 -1
- package/dist/channel/mcp-tools.js +28 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/message-queue.d.ts +1 -0
- package/dist/channel/message-queue.js +49 -17
- package/dist/channel/message-queue.js.map +1 -1
- package/dist/channel/reconnect-backoff.d.ts +17 -0
- package/dist/channel/reconnect-backoff.js +21 -0
- package/dist/channel/reconnect-backoff.js.map +1 -0
- package/dist/channel/types.d.ts +29 -0
- package/dist/classic-channel-manager.js +1 -5
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +148 -60
- package/dist/cli.js.map +1 -1
- package/dist/completion.d.ts +27 -0
- package/dist/completion.js +121 -0
- package/dist/completion.js.map +1 -0
- package/dist/config-validator.js +27 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/cost-guard.d.ts +3 -1
- package/dist/cost-guard.js +3 -1
- package/dist/cost-guard.js.map +1 -1
- package/dist/daemon.d.ts +230 -13
- package/dist/daemon.js +975 -335
- package/dist/daemon.js.map +1 -1
- package/dist/event-log.d.ts +31 -0
- package/dist/event-log.js +96 -0
- package/dist/event-log.js.map +1 -1
- package/dist/fleet-context.d.ts +2 -0
- package/dist/fleet-manager.d.ts +209 -4
- package/dist/fleet-manager.js +813 -84
- package/dist/fleet-manager.js.map +1 -1
- package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +22 -0
- package/dist/general-knowledge/skills/model-discovery/SKILL.md +14 -23
- package/dist/general-knowledge/skills/session-management/SKILL.md +15 -34
- package/dist/hang-detector.d.ts +19 -13
- package/dist/hang-detector.js +19 -49
- package/dist/hang-detector.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +21 -1
- package/dist/instance-lifecycle.js +141 -7
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.d.ts +5 -0
- package/dist/instructions.js +10 -5
- package/dist/instructions.js.map +1 -1
- package/dist/locale.js +3 -0
- package/dist/locale.js.map +1 -1
- package/dist/logger.js +14 -0
- package/dist/logger.js.map +1 -1
- package/dist/mcp-liveness.d.ts +21 -0
- package/dist/mcp-liveness.js +27 -0
- package/dist/mcp-liveness.js.map +1 -0
- package/dist/outbound-handlers.d.ts +16 -0
- package/dist/outbound-handlers.js +121 -27
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +7 -2
- package/dist/outbound-schemas.js +13 -2
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/pane-write-lock.d.ts +48 -0
- package/dist/pane-write-lock.js +73 -0
- package/dist/pane-write-lock.js.map +1 -0
- package/dist/process-memory.d.ts +31 -0
- package/dist/process-memory.js +79 -0
- package/dist/process-memory.js.map +1 -0
- package/dist/quickstart.js +17 -16
- package/dist/quickstart.js.map +1 -1
- package/dist/scheduler/db.js +3 -0
- package/dist/scheduler/db.js.map +1 -1
- package/dist/sd-notify.d.ts +45 -0
- package/dist/sd-notify.js +74 -3
- package/dist/sd-notify.js.map +1 -1
- package/dist/secret-file.d.ts +33 -0
- package/dist/secret-file.js +36 -0
- package/dist/secret-file.js.map +1 -0
- package/dist/setup-wizard.js +9 -7
- package/dist/setup-wizard.js.map +1 -1
- package/dist/tmux-control.d.ts +58 -5
- package/dist/tmux-control.js +107 -13
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.d.ts +47 -2
- package/dist/tmux-manager.js +103 -8
- package/dist/tmux-manager.js.map +1 -1
- package/dist/topic-commands.d.ts +43 -1
- package/dist/topic-commands.js +196 -66
- package/dist/topic-commands.js.map +1 -1
- package/dist/tui-glyphs.d.ts +25 -0
- package/dist/tui-glyphs.js +26 -0
- package/dist/tui-glyphs.js.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/ui/view.html +291 -33
- package/dist/update-check.d.ts +22 -0
- package/dist/update-check.js +44 -0
- package/dist/update-check.js.map +1 -0
- package/dist/usage/providers.d.ts +50 -0
- package/dist/usage/providers.js +611 -0
- package/dist/usage/providers.js.map +1 -0
- package/dist/usage/usage-api.d.ts +50 -0
- package/dist/usage/usage-api.js +111 -0
- package/dist/usage/usage-api.js.map +1 -0
- package/dist/view-api.d.ts +10 -0
- package/dist/view-api.js +67 -14
- package/dist/view-api.js.map +1 -1
- package/dist/web-api.js +5 -2
- package/dist/web-api.js.map +1 -1
- package/package.json +4 -1
- package/templates/systemd.service.ejs +9 -1
- package/dist/channel/tool-tracker.d.ts +0 -13
- package/dist/channel/tool-tracker.js +0 -58
- package/dist/channel/tool-tracker.js.map +0 -1
- package/dist/daemon-entry.d.ts +0 -1
- package/dist/daemon-entry.js +0 -30
- package/dist/daemon-entry.js.map +0 -1
- package/dist/fleet-system-prompt.d.ts +0 -11
- package/dist/fleet-system-prompt.js +0 -61
- package/dist/fleet-system-prompt.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,9 @@ import { spawn, execSync, execFileSync } from "node:child_process";
|
|
|
13
13
|
import { getAgendHome, getTmuxSocketName } from "./paths.js";
|
|
14
14
|
import { readClassicLastActivityAt } from "./classic-channel-manager.js";
|
|
15
15
|
import { hasPausedMarker } from "./pause-marker.js";
|
|
16
|
+
import { PIE_PERCENT_RE } from "./tui-glyphs.js";
|
|
17
|
+
import { COMPLETION_SHELLS, completionScript } from "./completion.js";
|
|
18
|
+
import { getUpdateSelector, lookupTargetVersion, reportUpdateRestart, shouldSkipUpdate, } from "./update-check.js";
|
|
16
19
|
/** Prefix tmux args with -L when socket isolation is active. */
|
|
17
20
|
function tmuxArgs(args) {
|
|
18
21
|
const socket = getTmuxSocketName();
|
|
@@ -66,8 +69,14 @@ fleet
|
|
|
66
69
|
const fleet = loadFleetConfig(FLEET_CONFIG_PATH);
|
|
67
70
|
const port = fleet.health_port ?? 19280;
|
|
68
71
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
// Any HTTP answer means a fleet daemon is listening — including a 503,
|
|
73
|
+
// which /health now returns when the fleet is degraded (no adapter
|
|
74
|
+
// connected, an instance crashed). Checking `.ok` here would treat a
|
|
75
|
+
// degraded fleet as "not running" and start a SECOND fleet on the same
|
|
76
|
+
// data dir, whose startup cleanup kills the first one's tmux windows.
|
|
77
|
+
// Only a connection error means nothing is there (the catch below).
|
|
78
|
+
await fetch(`http://127.0.0.1:${port}/health`);
|
|
79
|
+
{
|
|
71
80
|
try {
|
|
72
81
|
const resp = await fetch(`http://127.0.0.1:${port}/api/instance/${encodeURIComponent(instance)}/start`, { method: "POST" });
|
|
73
82
|
const body = await resp.json();
|
|
@@ -90,6 +99,59 @@ fleet
|
|
|
90
99
|
}
|
|
91
100
|
const { FleetManager } = await import("./fleet-manager.js");
|
|
92
101
|
const fm = new FleetManager(DATA_DIR);
|
|
102
|
+
// Register crash/signal handlers BEFORE startAll(). Startup routinely takes
|
|
103
|
+
// minutes (sequential general spawn, staggered instances, warmup waits), and
|
|
104
|
+
// without these a Ctrl-C or `systemctl stop` in that window got Node's default
|
|
105
|
+
// behaviour: instant death with fleet.pid already written, databases unclosed,
|
|
106
|
+
// and every tmux window orphaned.
|
|
107
|
+
let stopping = false;
|
|
108
|
+
const shutdown = async () => {
|
|
109
|
+
if (stopping)
|
|
110
|
+
return; // SIGINT and SIGTERM share this, and crash paths call stopAll too
|
|
111
|
+
stopping = true;
|
|
112
|
+
console.log("\nStopping fleet...");
|
|
113
|
+
await fm.stopAll().catch(err => console.error("Shutdown error:", err));
|
|
114
|
+
process.exit(0);
|
|
115
|
+
};
|
|
116
|
+
process.on("SIGINT", shutdown);
|
|
117
|
+
process.on("SIGTERM", shutdown);
|
|
118
|
+
process.on("uncaughtException", (err) => {
|
|
119
|
+
// An uncaught exception leaves unknown state — still the one case worth
|
|
120
|
+
// tearing down for.
|
|
121
|
+
console.error("Uncaught exception:", err);
|
|
122
|
+
if (stopping)
|
|
123
|
+
return;
|
|
124
|
+
stopping = true;
|
|
125
|
+
fm.stopAll().catch(() => { }).finally(() => process.exit(1));
|
|
126
|
+
});
|
|
127
|
+
process.on("unhandledRejection", (err) => {
|
|
128
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
129
|
+
// 409 = another bot poller exists — the adapter retries, don't crash.
|
|
130
|
+
if (msg.includes("409") && msg.includes("getUpdates")) {
|
|
131
|
+
console.error("Bot polling conflict (409) — retrying...");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Do NOT take the fleet down. A rejected promise in one instance's tmux
|
|
135
|
+
// call, health tick, or model switch used to stop every other working
|
|
136
|
+
// instance and exit(1) — the widest blast radius in the process. The
|
|
137
|
+
// per-instance health check and crash recovery handle a genuinely broken
|
|
138
|
+
// instance on their own.
|
|
139
|
+
//
|
|
140
|
+
// Surviving quietly would be its own failure mode, so this reports to the
|
|
141
|
+
// General topic as well as the log: the operator must not have to read
|
|
142
|
+
// daemon.log to learn the fleet swallowed a fault. notifyFleetError()
|
|
143
|
+
// throttles repeats, since these usually arrive from a loop.
|
|
144
|
+
console.error("Unhandled rejection (fleet continues):", err);
|
|
145
|
+
if (err instanceof Error && err.stack)
|
|
146
|
+
console.error(err.stack);
|
|
147
|
+
try {
|
|
148
|
+
fm.notifyFleetError(`⚠️ Fleet caught an unhandled error and kept running:\n\`${msg}\`\nSee daemon.log for the stack trace.`);
|
|
149
|
+
}
|
|
150
|
+
catch (notifyErr) {
|
|
151
|
+
// Never let the reporting path itself become an unhandled rejection.
|
|
152
|
+
console.error("Failed to report unhandled rejection:", notifyErr);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
93
155
|
if (instance) {
|
|
94
156
|
const config = fm.loadConfig(FLEET_CONFIG_PATH);
|
|
95
157
|
const inst = config.instances[instance];
|
|
@@ -107,30 +169,6 @@ fleet
|
|
|
107
169
|
await fm.startAll(FLEET_CONFIG_PATH);
|
|
108
170
|
}
|
|
109
171
|
console.log("Fleet started");
|
|
110
|
-
// Keep process alive + clean shutdown on Ctrl+C
|
|
111
|
-
const shutdown = async () => {
|
|
112
|
-
console.log("\nStopping fleet...");
|
|
113
|
-
await fm.stopAll();
|
|
114
|
-
process.exit(0);
|
|
115
|
-
};
|
|
116
|
-
process.on("SIGINT", shutdown);
|
|
117
|
-
process.on("SIGTERM", shutdown);
|
|
118
|
-
process.on("uncaughtException", async (err) => {
|
|
119
|
-
console.error("Uncaught exception:", err);
|
|
120
|
-
await fm.stopAll().catch(() => { });
|
|
121
|
-
process.exit(1);
|
|
122
|
-
});
|
|
123
|
-
process.on("unhandledRejection", async (err) => {
|
|
124
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
125
|
-
// 409 = another bot poller exists — adapter handles retry, don't crash
|
|
126
|
-
if (msg.includes("409") && msg.includes("getUpdates")) {
|
|
127
|
-
console.error("Bot polling conflict (409) — retrying...");
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
console.error("Unhandled rejection:", err);
|
|
131
|
-
await fm.stopAll().catch(() => { });
|
|
132
|
-
process.exit(1);
|
|
133
|
-
});
|
|
134
172
|
});
|
|
135
173
|
fleet
|
|
136
174
|
.command("stop")
|
|
@@ -1088,11 +1126,22 @@ program
|
|
|
1088
1126
|
.description("Update AgEnD to latest version and restart service")
|
|
1089
1127
|
.option("--version <ver>", "Specific version to install")
|
|
1090
1128
|
.option("--beta", "Install beta version")
|
|
1129
|
+
.option("--force", "Force reinstall and restart even when already up to date")
|
|
1091
1130
|
.action(async (opts) => {
|
|
1092
1131
|
const { spawnSync } = await import("node:child_process");
|
|
1093
|
-
const tag =
|
|
1132
|
+
const tag = getUpdateSelector(opts);
|
|
1094
1133
|
const pkg = `@songsid/agend@${tag}`;
|
|
1095
|
-
|
|
1134
|
+
const targetVersion = lookupTargetVersion(tag);
|
|
1135
|
+
if (shouldSkipUpdate(pkgVersion, targetVersion, opts.force)) {
|
|
1136
|
+
console.log(`\n ✓ Already up to date (v${pkgVersion})\n`);
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
if (targetVersion) {
|
|
1140
|
+
console.log(`\n Updating AgEnD v${pkgVersion} → v${targetVersion}...\n`);
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
console.log(`\n Updating AgEnD to ${tag}...\n`);
|
|
1144
|
+
}
|
|
1096
1145
|
// Detect and remove stale npm link (local build that shadows global install)
|
|
1097
1146
|
try {
|
|
1098
1147
|
const agendPath = execSync("which agend", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
@@ -1204,12 +1253,8 @@ program
|
|
|
1204
1253
|
// systemd → user systemd → launchd → detached pid).
|
|
1205
1254
|
console.log(" Restarting fleet...");
|
|
1206
1255
|
const restartResult = spawnSync(agendPath, ["restart"], { encoding: "utf-8", timeout: 30000, stdio: "inherit" });
|
|
1207
|
-
if (restartResult.status
|
|
1208
|
-
|
|
1209
|
-
}
|
|
1210
|
-
else {
|
|
1211
|
-
console.log(" ⚠ Auto-restart failed. Run: agend start");
|
|
1212
|
-
}
|
|
1256
|
+
if (!reportUpdateRestart(restartResult.status))
|
|
1257
|
+
process.exitCode = 1;
|
|
1213
1258
|
});
|
|
1214
1259
|
program
|
|
1215
1260
|
.command("reload")
|
|
@@ -1826,28 +1871,7 @@ async function fuzzyMatch(query, names) {
|
|
|
1826
1871
|
return null;
|
|
1827
1872
|
}
|
|
1828
1873
|
async function resolveInstance(query, config) {
|
|
1829
|
-
const names =
|
|
1830
|
-
// Include classic instances from classicBot.yaml
|
|
1831
|
-
try {
|
|
1832
|
-
const classicPath = join(DATA_DIR, "classicBot.yaml");
|
|
1833
|
-
if (existsSync(classicPath)) {
|
|
1834
|
-
const yamlMod = (await import("js-yaml")).default;
|
|
1835
|
-
const classic = yamlMod.load(readFileSync(classicPath, "utf-8"));
|
|
1836
|
-
if (classic?.channels) {
|
|
1837
|
-
for (const [key, val] of Object.entries(classic.channels)) {
|
|
1838
|
-
// New format persists instanceName; old format keyed by channelId.
|
|
1839
|
-
if (val.instanceName) {
|
|
1840
|
-
names.push(val.instanceName);
|
|
1841
|
-
continue;
|
|
1842
|
-
}
|
|
1843
|
-
const channelId = val.channelId ?? key;
|
|
1844
|
-
const chName = (val.name ?? channelId).toLowerCase().replace(/[^\p{L}\d-]/gu, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "project";
|
|
1845
|
-
names.push(`classic-${chName}-${channelId.slice(-4)}`);
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
}
|
|
1850
|
-
catch { /* ignore */ }
|
|
1874
|
+
const names = await listInstanceNames(config);
|
|
1851
1875
|
const match = await fuzzyMatch(query, names);
|
|
1852
1876
|
if (!match) {
|
|
1853
1877
|
console.error(`No instance matching "${query}". Available: ${names.join(", ")}`);
|
|
@@ -1942,7 +1966,7 @@ function formatTimeSince(isoStr) {
|
|
|
1942
1966
|
* Universal context-% parser for `agend ls` (all backends). Kept in sync with
|
|
1943
1967
|
* parseContextPercent() in topic-commands.ts — same formats, bottom-up so the
|
|
1944
1968
|
* most recent prompt wins:
|
|
1945
|
-
* kiro classic "8% !>" / TUI "
|
|
1969
|
+
* kiro classic "8% !>" / TUI "◑ 1%" (any pie glyph) / bracket "[8%]" / prompt "8% ❯"
|
|
1946
1970
|
* codex "Context N% left" (remaining → 100-N) or "Context N% used"
|
|
1947
1971
|
* opencode "1.2K (6%)"
|
|
1948
1972
|
* grok "12K / 500K" (used tokens / context window)
|
|
@@ -1969,7 +1993,7 @@ const defaultParser = (output) => {
|
|
|
1969
1993
|
return used / total * 100;
|
|
1970
1994
|
}
|
|
1971
1995
|
const m = line.match(/(\d+)%.*[!❯>]/)
|
|
1972
|
-
|| line.match(
|
|
1996
|
+
|| line.match(PIE_PERCENT_RE)
|
|
1973
1997
|
|| line.match(/\[(\d+)%\]/)
|
|
1974
1998
|
|| line.match(/Context\s+(\d+)%\s+used/i)
|
|
1975
1999
|
|| line.match(/\d+(?:\.\d+)?[KM]?\s*\((\d+)%\)/);
|
|
@@ -1980,10 +2004,48 @@ const defaultParser = (output) => {
|
|
|
1980
2004
|
};
|
|
1981
2005
|
/** Optional backend-specific overrides; none needed — defaultParser covers all. */
|
|
1982
2006
|
const contextParsers = {};
|
|
2007
|
+
/**
|
|
2008
|
+
* Every instance name the CLI accepts: fleet.yaml instances plus the
|
|
2009
|
+
* dynamically-created ClassicBot ones. Shared by `resolveInstance` (fuzzy
|
|
2010
|
+
* matching for `attach`) and `ls --names-only` (shell completion), so the
|
|
2011
|
+
* candidates offered by tab-completion are exactly the ones `attach` resolves.
|
|
2012
|
+
*/
|
|
2013
|
+
async function listInstanceNames(config) {
|
|
2014
|
+
const names = Object.keys(config.instances);
|
|
2015
|
+
try {
|
|
2016
|
+
const classicPath = join(DATA_DIR, "classicBot.yaml");
|
|
2017
|
+
if (existsSync(classicPath)) {
|
|
2018
|
+
const yamlMod = (await import("js-yaml")).default;
|
|
2019
|
+
const classic = yamlMod.load(readFileSync(classicPath, "utf-8"));
|
|
2020
|
+
if (classic?.channels) {
|
|
2021
|
+
for (const [key, val] of Object.entries(classic.channels)) {
|
|
2022
|
+
// New format persists instanceName; old format keyed by channelId.
|
|
2023
|
+
if (val.instanceName) {
|
|
2024
|
+
names.push(val.instanceName);
|
|
2025
|
+
continue;
|
|
2026
|
+
}
|
|
2027
|
+
const channelId = val.channelId ?? key;
|
|
2028
|
+
const chName = (val.name ?? channelId).toLowerCase().replace(/[^\p{L}\d-]/gu, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "project";
|
|
2029
|
+
names.push(`classic-${chName}-${channelId.slice(-4)}`);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
catch { /* ignore */ }
|
|
2035
|
+
return names;
|
|
2036
|
+
}
|
|
1983
2037
|
async function lsAction(opts) {
|
|
1984
2038
|
const yaml = (await import("js-yaml")).default;
|
|
1985
2039
|
const config = yaml.load(readFileSync(FLEET_CONFIG_PATH, "utf-8"));
|
|
1986
2040
|
const names = Object.keys(config.instances);
|
|
2041
|
+
// --names-only feeds shell completion, so it must return before any tmux or
|
|
2042
|
+
// status work (a tab press should not wait on the fleet) and must print
|
|
2043
|
+
// nothing but names — a "no instances" notice would become a candidate.
|
|
2044
|
+
if (opts.namesOnly) {
|
|
2045
|
+
for (const n of await listInstanceNames(config))
|
|
2046
|
+
console.log(n);
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
1987
2049
|
// Load classic channels from classicBot.yaml (keyed by channelId)
|
|
1988
2050
|
const classicPath = join(DATA_DIR, "classicBot.yaml");
|
|
1989
2051
|
let classicConfig = null;
|
|
@@ -2269,12 +2331,38 @@ program
|
|
|
2269
2331
|
.command("ls")
|
|
2270
2332
|
.description("List all instances with status, backend, team, and last activity")
|
|
2271
2333
|
.option("--json", "Output as JSON")
|
|
2334
|
+
.option("--names-only", "Print instance names one per line (for shell completion)")
|
|
2272
2335
|
.action(async (opts) => {
|
|
2273
2336
|
await lsAction(opts);
|
|
2274
2337
|
});
|
|
2275
2338
|
program
|
|
2276
|
-
.command("
|
|
2277
|
-
.description("
|
|
2339
|
+
.command("completion")
|
|
2340
|
+
.description("Print a shell completion script (bash or zsh)")
|
|
2341
|
+
.argument("<shell>", `Shell to generate for (${COMPLETION_SHELLS.join(", ")})`)
|
|
2342
|
+
.addHelpText("after", `
|
|
2343
|
+
Install:
|
|
2344
|
+
bash echo 'eval "$(agend completion bash)"' >> ~/.bashrc
|
|
2345
|
+
zsh echo 'eval "$(agend completion zsh)"' >> ~/.zshrc
|
|
2346
|
+
|
|
2347
|
+
Then reload the shell. Tab-completes instance names for \`agend attach\` and
|
|
2348
|
+
\`agend fleet start|stop|restart\`, and subcommands elsewhere.`)
|
|
2349
|
+
.action((shell) => {
|
|
2350
|
+
if (!COMPLETION_SHELLS.includes(shell)) {
|
|
2351
|
+
console.error(`Unsupported shell: ${shell}. Supported: ${COMPLETION_SHELLS.join(", ")}`);
|
|
2352
|
+
process.exit(2);
|
|
2353
|
+
}
|
|
2354
|
+
// Subcommand names come from commander itself, so the script can't drift
|
|
2355
|
+
// out of sync with the CLI the way a hardcoded list would.
|
|
2356
|
+
const fleetCmd = program.commands.find(c => c.name() === "fleet");
|
|
2357
|
+
console.log(completionScript(shell, {
|
|
2358
|
+
topLevel: program.commands.map(c => c.name()),
|
|
2359
|
+
fleetSub: fleetCmd?.commands.map(c => c.name()) ?? [],
|
|
2360
|
+
instanceCommands: ["attach"],
|
|
2361
|
+
fleetInstanceCommands: ["start", "stop", "restart"],
|
|
2362
|
+
}));
|
|
2363
|
+
});
|
|
2364
|
+
program
|
|
2365
|
+
.command("health").description("Fleet health check — shows problems and diagnostics")
|
|
2278
2366
|
.option("--json", "Output as JSON")
|
|
2279
2367
|
.option("-q, --quiet", "One-line summary only")
|
|
2280
2368
|
.action(async (opts) => {
|