@katyella/legio 0.1.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/CHANGELOG.md +422 -0
- package/LICENSE +21 -0
- package/README.md +555 -0
- package/agents/builder.md +141 -0
- package/agents/coordinator.md +351 -0
- package/agents/cto.md +196 -0
- package/agents/gateway.md +276 -0
- package/agents/lead.md +281 -0
- package/agents/merger.md +156 -0
- package/agents/monitor.md +212 -0
- package/agents/reviewer.md +142 -0
- package/agents/scout.md +131 -0
- package/agents/supervisor.md +416 -0
- package/bin/legio.mjs +38 -0
- package/package.json +77 -0
- package/src/agents/checkpoint.test.ts +88 -0
- package/src/agents/checkpoint.ts +102 -0
- package/src/agents/hooks-deployer.test.ts +1820 -0
- package/src/agents/hooks-deployer.ts +574 -0
- package/src/agents/identity.test.ts +614 -0
- package/src/agents/identity.ts +385 -0
- package/src/agents/lifecycle.test.ts +202 -0
- package/src/agents/lifecycle.ts +184 -0
- package/src/agents/manifest.test.ts +558 -0
- package/src/agents/manifest.ts +297 -0
- package/src/agents/overlay.test.ts +592 -0
- package/src/agents/overlay.ts +316 -0
- package/src/beads/client.test.ts +210 -0
- package/src/beads/client.ts +227 -0
- package/src/beads/molecules.test.ts +320 -0
- package/src/beads/molecules.ts +209 -0
- package/src/commands/agents.test.ts +325 -0
- package/src/commands/agents.ts +286 -0
- package/src/commands/clean.test.ts +730 -0
- package/src/commands/clean.ts +653 -0
- package/src/commands/completions.test.ts +346 -0
- package/src/commands/completions.ts +950 -0
- package/src/commands/coordinator.test.ts +1524 -0
- package/src/commands/coordinator.ts +880 -0
- package/src/commands/costs.test.ts +1015 -0
- package/src/commands/costs.ts +473 -0
- package/src/commands/dashboard.test.ts +94 -0
- package/src/commands/dashboard.ts +607 -0
- package/src/commands/doctor.test.ts +295 -0
- package/src/commands/doctor.ts +213 -0
- package/src/commands/down.test.ts +308 -0
- package/src/commands/down.ts +124 -0
- package/src/commands/errors.test.ts +648 -0
- package/src/commands/errors.ts +255 -0
- package/src/commands/feed.test.ts +579 -0
- package/src/commands/feed.ts +368 -0
- package/src/commands/gateway.test.ts +698 -0
- package/src/commands/gateway.ts +419 -0
- package/src/commands/group.test.ts +262 -0
- package/src/commands/group.ts +539 -0
- package/src/commands/hooks.test.ts +292 -0
- package/src/commands/hooks.ts +210 -0
- package/src/commands/init.test.ts +211 -0
- package/src/commands/init.ts +622 -0
- package/src/commands/inspect.test.ts +670 -0
- package/src/commands/inspect.ts +455 -0
- package/src/commands/log.test.ts +1556 -0
- package/src/commands/log.ts +752 -0
- package/src/commands/logs.test.ts +379 -0
- package/src/commands/logs.ts +544 -0
- package/src/commands/mail.test.ts +1726 -0
- package/src/commands/mail.ts +926 -0
- package/src/commands/merge.test.ts +676 -0
- package/src/commands/merge.ts +374 -0
- package/src/commands/metrics.test.ts +444 -0
- package/src/commands/metrics.ts +150 -0
- package/src/commands/monitor.test.ts +151 -0
- package/src/commands/monitor.ts +394 -0
- package/src/commands/nudge.test.ts +230 -0
- package/src/commands/nudge.ts +373 -0
- package/src/commands/prime.test.ts +467 -0
- package/src/commands/prime.ts +386 -0
- package/src/commands/replay.test.ts +742 -0
- package/src/commands/replay.ts +367 -0
- package/src/commands/run.test.ts +443 -0
- package/src/commands/run.ts +365 -0
- package/src/commands/server.test.ts +626 -0
- package/src/commands/server.ts +298 -0
- package/src/commands/sling.test.ts +810 -0
- package/src/commands/sling.ts +700 -0
- package/src/commands/spec.test.ts +206 -0
- package/src/commands/spec.ts +171 -0
- package/src/commands/status.test.ts +276 -0
- package/src/commands/status.ts +339 -0
- package/src/commands/stop.test.ts +357 -0
- package/src/commands/stop.ts +119 -0
- package/src/commands/supervisor.test.ts +186 -0
- package/src/commands/supervisor.ts +544 -0
- package/src/commands/trace.test.ts +746 -0
- package/src/commands/trace.ts +332 -0
- package/src/commands/up.test.ts +597 -0
- package/src/commands/up.ts +275 -0
- package/src/commands/watch.test.ts +152 -0
- package/src/commands/watch.ts +238 -0
- package/src/commands/worktree.test.ts +648 -0
- package/src/commands/worktree.ts +266 -0
- package/src/config.test.ts +496 -0
- package/src/config.ts +616 -0
- package/src/doctor/agents.test.ts +448 -0
- package/src/doctor/agents.ts +396 -0
- package/src/doctor/config-check.test.ts +184 -0
- package/src/doctor/config-check.ts +185 -0
- package/src/doctor/consistency.test.ts +645 -0
- package/src/doctor/consistency.ts +294 -0
- package/src/doctor/databases.test.ts +284 -0
- package/src/doctor/databases.ts +211 -0
- package/src/doctor/dependencies.test.ts +150 -0
- package/src/doctor/dependencies.ts +179 -0
- package/src/doctor/logs.test.ts +244 -0
- package/src/doctor/logs.ts +295 -0
- package/src/doctor/merge-queue.test.ts +210 -0
- package/src/doctor/merge-queue.ts +144 -0
- package/src/doctor/structure.test.ts +285 -0
- package/src/doctor/structure.ts +195 -0
- package/src/doctor/types.ts +37 -0
- package/src/doctor/version.test.ts +130 -0
- package/src/doctor/version.ts +131 -0
- package/src/e2e/chat-flow.test.ts +346 -0
- package/src/e2e/init-sling-lifecycle.test.ts +288 -0
- package/src/errors.test.ts +21 -0
- package/src/errors.ts +246 -0
- package/src/events/store.test.ts +660 -0
- package/src/events/store.ts +344 -0
- package/src/events/tool-filter.test.ts +330 -0
- package/src/events/tool-filter.ts +126 -0
- package/src/global-setup.ts +14 -0
- package/src/index.ts +339 -0
- package/src/insights/analyzer.test.ts +466 -0
- package/src/insights/analyzer.ts +203 -0
- package/src/logging/color.test.ts +118 -0
- package/src/logging/color.ts +71 -0
- package/src/logging/logger.test.ts +812 -0
- package/src/logging/logger.ts +266 -0
- package/src/logging/reporter.test.ts +258 -0
- package/src/logging/reporter.ts +109 -0
- package/src/logging/sanitizer.test.ts +190 -0
- package/src/logging/sanitizer.ts +57 -0
- package/src/mail/broadcast.test.ts +203 -0
- package/src/mail/broadcast.ts +92 -0
- package/src/mail/client.test.ts +873 -0
- package/src/mail/client.ts +236 -0
- package/src/mail/store.test.ts +815 -0
- package/src/mail/store.ts +402 -0
- package/src/merge/queue.test.ts +449 -0
- package/src/merge/queue.ts +262 -0
- package/src/merge/resolver.test.ts +1453 -0
- package/src/merge/resolver.ts +759 -0
- package/src/metrics/store.test.ts +1167 -0
- package/src/metrics/store.ts +511 -0
- package/src/metrics/summary.test.ts +397 -0
- package/src/metrics/summary.ts +178 -0
- package/src/metrics/transcript.test.ts +643 -0
- package/src/metrics/transcript.ts +351 -0
- package/src/mulch/client.test.ts +547 -0
- package/src/mulch/client.ts +416 -0
- package/src/server/audit-store.test.ts +384 -0
- package/src/server/audit-store.ts +257 -0
- package/src/server/headless.test.ts +180 -0
- package/src/server/headless.ts +151 -0
- package/src/server/index.test.ts +241 -0
- package/src/server/index.ts +317 -0
- package/src/server/public/app.js +187 -0
- package/src/server/public/apple-touch-icon.png +0 -0
- package/src/server/public/components/agent-badge.js +37 -0
- package/src/server/public/components/data-table.js +114 -0
- package/src/server/public/components/gateway-chat.js +256 -0
- package/src/server/public/components/issue-card.js +96 -0
- package/src/server/public/components/layout.js +88 -0
- package/src/server/public/components/message-bubble.js +120 -0
- package/src/server/public/components/stat-card.js +26 -0
- package/src/server/public/components/terminal-panel.js +140 -0
- package/src/server/public/favicon-16.png +0 -0
- package/src/server/public/favicon-32.png +0 -0
- package/src/server/public/favicon.ico +0 -0
- package/src/server/public/favicon.png +0 -0
- package/src/server/public/index.html +64 -0
- package/src/server/public/lib/api.js +35 -0
- package/src/server/public/lib/markdown.js +8 -0
- package/src/server/public/lib/preact-setup.js +8 -0
- package/src/server/public/lib/state.js +99 -0
- package/src/server/public/lib/utils.js +309 -0
- package/src/server/public/lib/ws.js +79 -0
- package/src/server/public/views/chat.js +983 -0
- package/src/server/public/views/costs.js +692 -0
- package/src/server/public/views/dashboard.js +781 -0
- package/src/server/public/views/gateway-chat.js +622 -0
- package/src/server/public/views/inspect.js +399 -0
- package/src/server/public/views/issues.js +470 -0
- package/src/server/public/views/setup.js +94 -0
- package/src/server/public/views/task-detail.js +422 -0
- package/src/server/routes.test.ts +3816 -0
- package/src/server/routes.ts +1964 -0
- package/src/server/websocket.test.ts +288 -0
- package/src/server/websocket.ts +196 -0
- package/src/sessions/compat.test.ts +109 -0
- package/src/sessions/compat.ts +17 -0
- package/src/sessions/store.test.ts +969 -0
- package/src/sessions/store.ts +480 -0
- package/src/test-helpers.test.ts +97 -0
- package/src/test-helpers.ts +143 -0
- package/src/types.ts +708 -0
- package/src/watchdog/daemon.test.ts +1233 -0
- package/src/watchdog/daemon.ts +533 -0
- package/src/watchdog/health.test.ts +371 -0
- package/src/watchdog/health.ts +248 -0
- package/src/watchdog/triage.test.ts +162 -0
- package/src/watchdog/triage.ts +193 -0
- package/src/worktree/manager.test.ts +444 -0
- package/src/worktree/manager.ts +224 -0
- package/src/worktree/tmux.test.ts +1238 -0
- package/src/worktree/tmux.ts +644 -0
- package/templates/CLAUDE.md.tmpl +89 -0
- package/templates/hooks.json.tmpl +132 -0
- package/templates/overlay.md.tmpl +79 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command: legio worktree list | clean [--completed] [--all]
|
|
3
|
+
*
|
|
4
|
+
* List shows worktrees with agent status.
|
|
5
|
+
* Clean removes worktree dirs, branch refs (if merged), and tmux sessions.
|
|
6
|
+
* Logs are never auto-deleted.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { access } from "node:fs/promises";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { loadConfig } from "../config.ts";
|
|
12
|
+
import { ValidationError } from "../errors.ts";
|
|
13
|
+
import { createMailStore } from "../mail/store.ts";
|
|
14
|
+
import { openSessionStore } from "../sessions/compat.ts";
|
|
15
|
+
import type { AgentSession } from "../types.ts";
|
|
16
|
+
import { listWorktrees, removeWorktree } from "../worktree/manager.ts";
|
|
17
|
+
import { isSessionAlive, killSession } from "../worktree/tmux.ts";
|
|
18
|
+
|
|
19
|
+
function hasFlag(args: string[], flag: string): boolean {
|
|
20
|
+
return args.includes(flag);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Handle `legio worktree list`.
|
|
25
|
+
*/
|
|
26
|
+
async function handleList(root: string, json: boolean): Promise<void> {
|
|
27
|
+
const worktrees = await listWorktrees(root);
|
|
28
|
+
const legioDir = join(root, ".legio");
|
|
29
|
+
const { store } = openSessionStore(legioDir);
|
|
30
|
+
let sessions: AgentSession[];
|
|
31
|
+
try {
|
|
32
|
+
sessions = store.getAll();
|
|
33
|
+
} finally {
|
|
34
|
+
store.close();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const legioWts = worktrees.filter((wt) => wt.branch.startsWith("legio/"));
|
|
38
|
+
|
|
39
|
+
if (json) {
|
|
40
|
+
const entries = legioWts.map((wt) => {
|
|
41
|
+
const session = sessions.find((s) => s.worktreePath === wt.path);
|
|
42
|
+
return {
|
|
43
|
+
path: wt.path,
|
|
44
|
+
branch: wt.branch,
|
|
45
|
+
head: wt.head,
|
|
46
|
+
agentName: session?.agentName ?? null,
|
|
47
|
+
state: session?.state ?? null,
|
|
48
|
+
beadId: session?.beadId ?? null,
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
process.stdout.write(`${JSON.stringify(entries, null, "\t")}\n`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (legioWts.length === 0) {
|
|
56
|
+
process.stdout.write("No agent worktrees found.\n");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
process.stdout.write(`🌳 Agent worktrees: ${legioWts.length}\n\n`);
|
|
61
|
+
for (const wt of legioWts) {
|
|
62
|
+
const session = sessions.find((s) => s.worktreePath === wt.path);
|
|
63
|
+
const state = session?.state ?? "unknown";
|
|
64
|
+
const agent = session?.agentName ?? "?";
|
|
65
|
+
const bead = session?.beadId ?? "?";
|
|
66
|
+
process.stdout.write(` ${wt.branch}\n`);
|
|
67
|
+
process.stdout.write(` Agent: ${agent} | State: ${state} | Task: ${bead}\n`);
|
|
68
|
+
process.stdout.write(` Path: ${wt.path}\n\n`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Handle `legio worktree clean [--completed] [--all]`.
|
|
74
|
+
*/
|
|
75
|
+
async function handleClean(args: string[], root: string, json: boolean): Promise<void> {
|
|
76
|
+
const all = hasFlag(args, "--all");
|
|
77
|
+
const completedOnly = hasFlag(args, "--completed") || !all;
|
|
78
|
+
|
|
79
|
+
const worktrees = await listWorktrees(root);
|
|
80
|
+
const legioDir = join(root, ".legio");
|
|
81
|
+
const { store } = openSessionStore(legioDir);
|
|
82
|
+
|
|
83
|
+
let sessions: AgentSession[];
|
|
84
|
+
try {
|
|
85
|
+
sessions = store.getAll();
|
|
86
|
+
} catch {
|
|
87
|
+
store.close();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const legioWts = worktrees.filter((wt) => wt.branch.startsWith("legio/"));
|
|
92
|
+
const cleaned: string[] = [];
|
|
93
|
+
const failed: string[] = [];
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
for (const wt of legioWts) {
|
|
97
|
+
const session = sessions.find((s) => s.worktreePath === wt.path);
|
|
98
|
+
|
|
99
|
+
// If --completed (default), only clean worktrees whose agent is done/zombie
|
|
100
|
+
if (completedOnly && session && session.state !== "completed" && session.state !== "zombie") {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// If --all, clean everything
|
|
105
|
+
// Kill tmux session if still alive
|
|
106
|
+
if (session?.tmuxSession) {
|
|
107
|
+
const alive = await isSessionAlive(session.tmuxSession);
|
|
108
|
+
if (alive) {
|
|
109
|
+
try {
|
|
110
|
+
await killSession(session.tmuxSession);
|
|
111
|
+
} catch {
|
|
112
|
+
// Best effort
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Remove worktree and its branch.
|
|
118
|
+
// Always force worktree removal since deployed .claude/ files create untracked
|
|
119
|
+
// files that cause non-forced removal to fail.
|
|
120
|
+
// Always force-delete the branch since we're cleaning up finished/zombie agents
|
|
121
|
+
// whose branches are typically unmerged.
|
|
122
|
+
try {
|
|
123
|
+
await removeWorktree(root, wt.path, { force: true, forceBranch: true });
|
|
124
|
+
cleaned.push(wt.branch);
|
|
125
|
+
|
|
126
|
+
if (!json) {
|
|
127
|
+
process.stdout.write(`🗑️ Removed: ${wt.branch}\n`);
|
|
128
|
+
}
|
|
129
|
+
} catch (err) {
|
|
130
|
+
failed.push(wt.branch);
|
|
131
|
+
if (!json) {
|
|
132
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
133
|
+
process.stderr.write(`⚠️ Failed to remove ${wt.branch}: ${msg}\n`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Purge mail for cleaned agents
|
|
139
|
+
let mailPurged = 0;
|
|
140
|
+
if (cleaned.length > 0) {
|
|
141
|
+
const mailDbPath = join(root, ".legio", "mail.db");
|
|
142
|
+
let mailDbFileExists = false;
|
|
143
|
+
try {
|
|
144
|
+
await access(mailDbPath);
|
|
145
|
+
mailDbFileExists = true;
|
|
146
|
+
} catch {
|
|
147
|
+
/* not found */
|
|
148
|
+
}
|
|
149
|
+
if (mailDbFileExists) {
|
|
150
|
+
const mailStore = createMailStore(mailDbPath);
|
|
151
|
+
try {
|
|
152
|
+
for (const branch of cleaned) {
|
|
153
|
+
const session = sessions.find((s) => s.branchName === branch);
|
|
154
|
+
if (session) {
|
|
155
|
+
mailPurged += mailStore.purge({ agent: session.agentName });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} finally {
|
|
159
|
+
mailStore.close();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Mark cleaned sessions as zombie in the SessionStore
|
|
165
|
+
for (const branch of cleaned) {
|
|
166
|
+
const session = sessions.find((s) => s.branchName === branch);
|
|
167
|
+
if (session) {
|
|
168
|
+
store.updateState(session.agentName, "zombie");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Prune zombie entries whose worktree paths no longer exist on disk.
|
|
173
|
+
// This prevents the session store from growing unbounded with stale entries.
|
|
174
|
+
const remainingWorktrees = await listWorktrees(root);
|
|
175
|
+
const worktreePaths = new Set(remainingWorktrees.map((wt) => wt.path));
|
|
176
|
+
let pruneCount = 0;
|
|
177
|
+
|
|
178
|
+
// Re-read sessions after state updates to get current zombie list
|
|
179
|
+
const currentSessions = store.getAll();
|
|
180
|
+
for (const session of currentSessions) {
|
|
181
|
+
if (session.state === "zombie" && !worktreePaths.has(session.worktreePath)) {
|
|
182
|
+
store.remove(session.agentName);
|
|
183
|
+
pruneCount++;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (json) {
|
|
188
|
+
process.stdout.write(
|
|
189
|
+
`${JSON.stringify({ cleaned, failed, pruned: pruneCount, mailPurged })}\n`,
|
|
190
|
+
);
|
|
191
|
+
} else if (cleaned.length === 0 && pruneCount === 0 && failed.length === 0) {
|
|
192
|
+
process.stdout.write("No worktrees to clean.\n");
|
|
193
|
+
} else {
|
|
194
|
+
if (cleaned.length > 0) {
|
|
195
|
+
process.stdout.write(
|
|
196
|
+
`\nCleaned ${cleaned.length} worktree${cleaned.length === 1 ? "" : "s"}.\n`,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
if (failed.length > 0) {
|
|
200
|
+
process.stdout.write(
|
|
201
|
+
`Failed to clean ${failed.length} worktree${failed.length === 1 ? "" : "s"}.\n`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
if (mailPurged > 0) {
|
|
205
|
+
process.stdout.write(
|
|
206
|
+
`Purged ${mailPurged} mail message${mailPurged === 1 ? "" : "s"} from cleaned agents.\n`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
if (pruneCount > 0) {
|
|
210
|
+
process.stdout.write(
|
|
211
|
+
`Pruned ${pruneCount} zombie session${pruneCount === 1 ? "" : "s"} from store.\n`,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} finally {
|
|
216
|
+
store.close();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Entry point for `legio worktree <subcommand> [flags]`.
|
|
222
|
+
*
|
|
223
|
+
* Subcommands: list, clean.
|
|
224
|
+
*/
|
|
225
|
+
const WORKTREE_HELP = `legio worktree — Manage agent worktrees
|
|
226
|
+
|
|
227
|
+
Usage: legio worktree <subcommand> [flags]
|
|
228
|
+
|
|
229
|
+
Subcommands:
|
|
230
|
+
list List worktrees with agent status
|
|
231
|
+
clean Remove completed worktrees
|
|
232
|
+
[--completed] Only finished agents (default)
|
|
233
|
+
[--all] Force remove all
|
|
234
|
+
|
|
235
|
+
Options:
|
|
236
|
+
--json Output as JSON
|
|
237
|
+
--help, -h Show this help`;
|
|
238
|
+
|
|
239
|
+
export async function worktreeCommand(args: string[]): Promise<void> {
|
|
240
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
241
|
+
process.stdout.write(`${WORKTREE_HELP}\n`);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const subcommand = args[0];
|
|
246
|
+
const subArgs = args.slice(1);
|
|
247
|
+
const jsonFlag = hasFlag(args, "--json");
|
|
248
|
+
|
|
249
|
+
const cwd = process.cwd();
|
|
250
|
+
const config = await loadConfig(cwd);
|
|
251
|
+
const root = config.project.root;
|
|
252
|
+
|
|
253
|
+
switch (subcommand) {
|
|
254
|
+
case "list":
|
|
255
|
+
await handleList(root, jsonFlag);
|
|
256
|
+
break;
|
|
257
|
+
case "clean":
|
|
258
|
+
await handleClean(subArgs, root, jsonFlag);
|
|
259
|
+
break;
|
|
260
|
+
default:
|
|
261
|
+
throw new ValidationError(
|
|
262
|
+
`Unknown worktree subcommand: ${subcommand ?? "(none)"}. Use: list, clean`,
|
|
263
|
+
{ field: "subcommand" },
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
}
|