@phnx-labs/agents-cli 1.20.62 → 1.20.64

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.
Files changed (114) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +19 -0
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/browser.js +13 -3
  5. package/dist/commands/exec.js +96 -28
  6. package/dist/commands/feed.d.ts +4 -0
  7. package/dist/commands/feed.js +27 -8
  8. package/dist/commands/funnel.d.ts +5 -0
  9. package/dist/commands/funnel.js +62 -0
  10. package/dist/commands/hosts.js +42 -0
  11. package/dist/commands/lease.d.ts +23 -0
  12. package/dist/commands/lease.js +201 -0
  13. package/dist/commands/mailboxes.d.ts +20 -0
  14. package/dist/commands/mailboxes.js +390 -0
  15. package/dist/commands/repo.d.ts +4 -4
  16. package/dist/commands/repo.js +30 -19
  17. package/dist/commands/routines.js +92 -29
  18. package/dist/commands/sessions-export.d.ts +2 -0
  19. package/dist/commands/sessions-export.js +279 -0
  20. package/dist/commands/sessions-import.d.ts +2 -0
  21. package/dist/commands/sessions-import.js +230 -0
  22. package/dist/commands/sessions-sync.d.ts +1 -0
  23. package/dist/commands/sessions-sync.js +16 -2
  24. package/dist/commands/sessions.js +12 -1
  25. package/dist/commands/setup.js +9 -0
  26. package/dist/commands/ssh.js +170 -5
  27. package/dist/commands/sync-provision.d.ts +23 -0
  28. package/dist/commands/sync-provision.js +107 -0
  29. package/dist/commands/usage.d.ts +2 -0
  30. package/dist/commands/usage.js +7 -2
  31. package/dist/commands/view.d.ts +1 -1
  32. package/dist/commands/webhook.d.ts +9 -0
  33. package/dist/commands/webhook.js +93 -0
  34. package/dist/index.js +7 -2
  35. package/dist/lib/agents.d.ts +44 -0
  36. package/dist/lib/agents.js +85 -35
  37. package/dist/lib/browser/drivers/ssh.js +19 -2
  38. package/dist/lib/browser/ipc.js +5 -4
  39. package/dist/lib/browser/profiles.d.ts +13 -0
  40. package/dist/lib/browser/profiles.js +17 -0
  41. package/dist/lib/browser/service.d.ts +12 -1
  42. package/dist/lib/browser/service.js +48 -13
  43. package/dist/lib/browser/sessions-list.d.ts +40 -0
  44. package/dist/lib/browser/sessions-list.js +190 -0
  45. package/dist/lib/comms-render.d.ts +37 -0
  46. package/dist/lib/comms-render.js +89 -0
  47. package/dist/lib/crabbox/cli.d.ts +72 -0
  48. package/dist/lib/crabbox/cli.js +158 -9
  49. package/dist/lib/crabbox/runtimes.d.ts +13 -0
  50. package/dist/lib/crabbox/runtimes.js +24 -0
  51. package/dist/lib/daemon.js +8 -1
  52. package/dist/lib/devices/fleet.d.ts +62 -0
  53. package/dist/lib/devices/fleet.js +128 -0
  54. package/dist/lib/devices/health.d.ts +77 -0
  55. package/dist/lib/devices/health.js +186 -0
  56. package/dist/lib/funnel.d.ts +5 -0
  57. package/dist/lib/funnel.js +23 -0
  58. package/dist/lib/git.d.ts +21 -5
  59. package/dist/lib/git.js +64 -14
  60. package/dist/lib/hosts/credentials.d.ts +28 -0
  61. package/dist/lib/hosts/credentials.js +48 -0
  62. package/dist/lib/hosts/dispatch.d.ts +25 -0
  63. package/dist/lib/hosts/dispatch.js +68 -2
  64. package/dist/lib/hosts/passthrough.d.ts +13 -10
  65. package/dist/lib/hosts/passthrough.js +119 -29
  66. package/dist/lib/mailbox-gc.js +4 -16
  67. package/dist/lib/mailbox.d.ts +39 -0
  68. package/dist/lib/mailbox.js +112 -0
  69. package/dist/lib/migrate.d.ts +12 -0
  70. package/dist/lib/migrate.js +55 -1
  71. package/dist/lib/paths.d.ts +13 -0
  72. package/dist/lib/paths.js +26 -4
  73. package/dist/lib/routines.d.ts +50 -12
  74. package/dist/lib/routines.js +82 -27
  75. package/dist/lib/runner.js +255 -13
  76. package/dist/lib/sandbox.d.ts +9 -1
  77. package/dist/lib/sandbox.js +11 -2
  78. package/dist/lib/session/bundle.d.ts +150 -0
  79. package/dist/lib/session/bundle.js +189 -0
  80. package/dist/lib/session/remote-bundle.d.ts +12 -0
  81. package/dist/lib/session/remote-bundle.js +61 -0
  82. package/dist/lib/session/sync/agents.d.ts +56 -6
  83. package/dist/lib/session/sync/agents.js +0 -0
  84. package/dist/lib/session/sync/config.d.ts +8 -0
  85. package/dist/lib/session/sync/config.js +6 -1
  86. package/dist/lib/session/sync/manifest.d.ts +14 -3
  87. package/dist/lib/session/sync/manifest.js +4 -0
  88. package/dist/lib/session/sync/provision.d.ts +49 -0
  89. package/dist/lib/session/sync/provision.js +91 -0
  90. package/dist/lib/session/sync/sync.d.ts +26 -2
  91. package/dist/lib/session/sync/sync.js +192 -69
  92. package/dist/lib/session/sync/transcript-crypto.d.ts +77 -0
  93. package/dist/lib/session/sync/transcript-crypto.js +147 -0
  94. package/dist/lib/ssh-tunnel.js +13 -1
  95. package/dist/lib/staleness/detectors/subagents.d.ts +5 -0
  96. package/dist/lib/staleness/detectors/subagents.js +5 -192
  97. package/dist/lib/staleness/writers/subagents.d.ts +10 -0
  98. package/dist/lib/staleness/writers/subagents.js +11 -102
  99. package/dist/lib/startup/command-registry.d.ts +4 -0
  100. package/dist/lib/startup/command-registry.js +16 -0
  101. package/dist/lib/state.d.ts +10 -2
  102. package/dist/lib/state.js +14 -2
  103. package/dist/lib/subagents-registry.d.ts +85 -0
  104. package/dist/lib/subagents-registry.js +393 -0
  105. package/dist/lib/subagents.d.ts +8 -8
  106. package/dist/lib/subagents.js +32 -663
  107. package/dist/lib/sync-umbrella.d.ts +1 -0
  108. package/dist/lib/sync-umbrella.js +14 -3
  109. package/dist/lib/triggers/webhook.d.ts +70 -27
  110. package/dist/lib/triggers/webhook.js +264 -43
  111. package/dist/lib/types.d.ts +9 -0
  112. package/dist/lib/usage.d.ts +42 -3
  113. package/dist/lib/usage.js +162 -22
  114. package/package.json +1 -1
@@ -0,0 +1,393 @@
1
+ /**
2
+ * Declarative subagent-target registry.
3
+ *
4
+ * Each subagents-capable agent gets ONE table entry (`SUBAGENT_TARGETS`)
5
+ * describing how a central subagent (`~/.agents/subagents/<name>/`) is
6
+ * materialized into that agent's home, how it is enumerated, and where it lives
7
+ * on disk. Generic install / list / detect / orphan / remove logic iterates the
8
+ * table instead of the near-identical `else if (agent === '...')` chains that
9
+ * used to be copy-pasted across `subagents.ts`, the staleness writer, and the
10
+ * staleness detector -- roughly O(agents x operations) arms.
11
+ *
12
+ * Adding a *standard* integration is now one line here (plus the `subagents`
13
+ * capability flag in `agents.ts`, the version gate). Three layout builders cover
14
+ * every current agent, so most entries are a single call:
15
+ *
16
+ * - `flatFile` one `<name><ext>` file, body from a `transform` fn.
17
+ * (claude, gemini, grok, droid, codex, opencode, copilot,
18
+ * cursor, forge, kiro, goose)
19
+ * - `dirFile` a `<name>/` directory holding one generated `<file>`.
20
+ * (antigravity: `<name>/agent.md`)
21
+ * - `dirCopy` copy the whole source directory to `<name>/`, applying
22
+ * renames, detected by a `marker` file. (openclaw)
23
+ *
24
+ * Genuinely-bespoke agents keep an explicit handler in the same table -- Kimi
25
+ * writes two files per subagent plus a managed parent index, so it is a hand
26
+ * -written `SubagentTarget` rather than a builder call.
27
+ *
28
+ * The per-agent `transform`/metadata parsers are the escape hatch: they live in
29
+ * `subagents.ts` and are referenced by the table, so the generic engine has zero
30
+ * per-agent branches. See the integration tiers in `docs/subagents.md`.
31
+ */
32
+ import * as fs from 'fs';
33
+ import * as path from 'path';
34
+ import * as yaml from 'yaml';
35
+ import { safeJoin } from './paths.js';
36
+ import { parseSubagentFrontmatter, transformSubagentForClaude, transformSubagentForCodex, transformSubagentForCopilot, transformSubagentForCursor, transformSubagentForDroid, transformSubagentForForge, transformSubagentForGoose, transformSubagentForKiro, transformSubagentForOpenCode, transformSubagentForAntigravity, writeKimiSubagentFiles, buildKimiSubagentsParentYaml, KIMI_SUBAGENTS_PARENT_FILE, } from './subagents.js';
37
+ // ── metadata readers (the per-format escape hatch) ───────────────────────────
38
+ /** Frontmatter, skipping files that lack a valid block (claude/gemini/grok/droid/codex). */
39
+ function metaFrontmatterSkip(filePath) {
40
+ return parseSubagentFrontmatter(filePath);
41
+ }
42
+ /** Frontmatter, falling back to an empty description (opencode/copilot/cursor/forge). */
43
+ function metaFrontmatterFallback(filePath, name) {
44
+ return parseSubagentFrontmatter(filePath) ?? { name, description: '' };
45
+ }
46
+ /** Kiro custom-agent JSON: read name/description/model; skip on parse error. */
47
+ function metaJson(filePath, name) {
48
+ try {
49
+ const cfg = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
50
+ return { name: cfg.name || name, description: cfg.description || '', model: cfg.model };
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ /** Goose recipe YAML: title -> name, description; skip on parse error. */
57
+ function metaGooseYaml(filePath, name) {
58
+ try {
59
+ const recipe = yaml.parse(fs.readFileSync(filePath, 'utf-8'));
60
+ return { name: recipe?.title || name, description: recipe?.description || '' };
61
+ }
62
+ catch {
63
+ return null;
64
+ }
65
+ }
66
+ // ── shared fs primitive ──────────────────────────────────────────────────────
67
+ /**
68
+ * Copy every file in `src` into `dest` (created if missing), applying
69
+ * `rename` (source filename -> target filename) on the way. Directories in
70
+ * `src` are skipped -- subagents are flat file sets. Throws on fs error.
71
+ */
72
+ export function copyDirWithRename(src, dest, rename) {
73
+ if (!fs.existsSync(dest)) {
74
+ fs.mkdirSync(dest, { recursive: true });
75
+ }
76
+ for (const file of fs.readdirSync(src)) {
77
+ const sourcePath = path.join(src, file);
78
+ if (!fs.statSync(sourcePath).isFile())
79
+ continue;
80
+ const targetName = rename?.[file] ?? file;
81
+ fs.copyFileSync(sourcePath, path.join(dest, targetName));
82
+ }
83
+ }
84
+ // ── layout builders ──────────────────────────────────────────────────────────
85
+ /** One flattened `<name><ext>` file per subagent under `subdir`. */
86
+ function flatFile(opts) {
87
+ const readMeta = opts.readMeta ?? metaFrontmatterSkip;
88
+ return {
89
+ dir: (home) => path.join(home, ...opts.subdir),
90
+ write(dir, sub) {
91
+ fs.mkdirSync(dir, { recursive: true });
92
+ fs.writeFileSync(safeJoin(dir, `${sub.name}${opts.ext}`), opts.transform(sub.path));
93
+ },
94
+ names(dir) {
95
+ if (!fs.existsSync(dir))
96
+ return [];
97
+ return fs
98
+ .readdirSync(dir)
99
+ .filter((f) => f.endsWith(opts.ext))
100
+ .map((f) => f.slice(0, -opts.ext.length));
101
+ },
102
+ occupied(dir, name) {
103
+ return [{ path: safeJoin(dir, `${name}${opts.ext}`), kind: 'file' }];
104
+ },
105
+ read(dir, name) {
106
+ const filePath = path.join(dir, `${name}${opts.ext}`);
107
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile())
108
+ return null;
109
+ const frontmatter = readMeta(filePath, name);
110
+ if (!frontmatter)
111
+ return null;
112
+ return { frontmatter, files: [`${name}${opts.ext}`], path: filePath };
113
+ },
114
+ };
115
+ }
116
+ /** A `<name>/` directory holding one generated `<file>` per subagent. */
117
+ function dirFile(opts) {
118
+ const readMeta = opts.readMeta ?? ((p, name) => metaFrontmatterFallback(p, name));
119
+ return {
120
+ dir: (home) => path.join(home, ...opts.subdir),
121
+ write(dir, sub) {
122
+ const target = safeJoin(dir, sub.name);
123
+ fs.mkdirSync(target, { recursive: true });
124
+ fs.writeFileSync(safeJoin(target, opts.file), opts.transform(sub.path));
125
+ },
126
+ names(dir) {
127
+ if (!fs.existsSync(dir))
128
+ return [];
129
+ return fs
130
+ .readdirSync(dir, { withFileTypes: true })
131
+ .filter((e) => e.isDirectory() && fs.existsSync(path.join(dir, e.name, opts.file)))
132
+ .map((e) => e.name);
133
+ },
134
+ occupied(dir, name) {
135
+ return [{ path: safeJoin(dir, name), kind: 'dir' }];
136
+ },
137
+ read(dir, name) {
138
+ const filePath = path.join(dir, name, opts.file);
139
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile())
140
+ return null;
141
+ const frontmatter = readMeta(filePath, name);
142
+ if (!frontmatter)
143
+ return null;
144
+ return { frontmatter, files: [opts.file], path: filePath };
145
+ },
146
+ };
147
+ }
148
+ /** Copy the whole source directory to `<name>/`, detected by `marker`. */
149
+ function dirCopy(opts) {
150
+ return {
151
+ dir: (home) => path.join(home, ...opts.subdir),
152
+ write(dir, sub) {
153
+ copyDirWithRename(sub.path, safeJoin(dir, sub.name), opts.rename);
154
+ },
155
+ names(dir) {
156
+ if (!fs.existsSync(dir))
157
+ return [];
158
+ return fs
159
+ .readdirSync(dir, { withFileTypes: true })
160
+ .filter((d) => d.isDirectory() && fs.existsSync(path.join(dir, d.name, opts.marker)))
161
+ .map((d) => d.name);
162
+ },
163
+ occupied(dir, name) {
164
+ return [{ path: safeJoin(dir, name), kind: 'dir' }];
165
+ },
166
+ read(dir, name) {
167
+ const markerPath = path.join(dir, name, opts.marker);
168
+ if (!fs.existsSync(markerPath))
169
+ return null;
170
+ // The marker may lack frontmatter; fall back to the first content line.
171
+ let frontmatter = { name, description: '' };
172
+ const parsed = parseSubagentFrontmatter(markerPath);
173
+ if (parsed) {
174
+ frontmatter = parsed;
175
+ }
176
+ else {
177
+ const content = fs.readFileSync(markerPath, 'utf-8');
178
+ const firstLine = content.split('\n').find((l) => l.trim() && !l.startsWith('#'));
179
+ frontmatter.description = firstLine?.slice(0, 80) || `${name}`;
180
+ }
181
+ const subagentDir = path.join(dir, name);
182
+ const files = fs
183
+ .readdirSync(subagentDir)
184
+ .filter((f) => f.endsWith('.md'))
185
+ .sort();
186
+ return { frontmatter, files, path: subagentDir };
187
+ },
188
+ };
189
+ }
190
+ /**
191
+ * Kimi (genuinely bespoke): each subagent is a `<name>.yaml` + sibling
192
+ * `<name>.system.md`, and a managed `_agents-cli.yaml` parent lists them all for
193
+ * `kimi --agent-file`. The parent index is a cross-item concern, so it is built
194
+ * in `finalize`, not per-item.
195
+ */
196
+ const kimiTarget = {
197
+ dir: (home) => path.join(home, '.kimi-code', 'agents'),
198
+ write(dir, sub) {
199
+ writeKimiSubagentFiles(dir, sub.path, sub.name);
200
+ },
201
+ names(dir) {
202
+ if (!fs.existsSync(dir))
203
+ return [];
204
+ // The parent is `_agents-cli.yaml` (underscore-prefixed, reserved).
205
+ return fs
206
+ .readdirSync(dir)
207
+ .filter((f) => f.endsWith('.yaml') && !f.startsWith('_'))
208
+ .map((f) => f.slice(0, -'.yaml'.length));
209
+ },
210
+ occupied(dir, name) {
211
+ return [
212
+ { path: safeJoin(dir, `${name}.yaml`), kind: 'file' },
213
+ { path: safeJoin(dir, `${name}.system.md`), kind: 'file' },
214
+ ];
215
+ },
216
+ read(dir, name) {
217
+ const yamlPath = path.join(dir, `${name}.yaml`);
218
+ if (!fs.existsSync(yamlPath) || !fs.statSync(yamlPath).isFile())
219
+ return null;
220
+ let description = '';
221
+ try {
222
+ const parsed = yaml.parse(fs.readFileSync(yamlPath, 'utf-8'));
223
+ description = parsed?.agent?.description ?? '';
224
+ }
225
+ catch {
226
+ /* leave description empty */
227
+ }
228
+ const files = [`${name}.yaml`];
229
+ const promptFile = `${name}.system.md`;
230
+ if (fs.existsSync(path.join(dir, promptFile)))
231
+ files.push(promptFile);
232
+ return { frontmatter: { name, description }, files, path: yamlPath };
233
+ },
234
+ finalize(dir, synced) {
235
+ const entries = synced.map((sub) => {
236
+ const fm = parseSubagentFrontmatter(path.join(sub.path, 'AGENT.md'));
237
+ return { name: sub.name, description: fm?.description ?? sub.name, relativePath: `./${sub.name}.yaml` };
238
+ });
239
+ fs.writeFileSync(safeJoin(dir, KIMI_SUBAGENTS_PARENT_FILE), buildKimiSubagentsParentYaml(entries));
240
+ },
241
+ };
242
+ // ── the registry ─────────────────────────────────────────────────────────────
243
+ /**
244
+ * Single source of truth for how each subagents-capable agent stores subagents.
245
+ * The keys MUST match `capableAgents('subagents')` (the `subagents` flag in
246
+ * `agents.ts`): the capability flag is the version gate, this table is the shape.
247
+ */
248
+ export const SUBAGENT_TARGETS = {
249
+ // Tier 1 -- flat markdown, Claude-compatible flatten.
250
+ claude: flatFile({ subdir: ['.claude', 'agents'], ext: '.md', transform: transformSubagentForClaude }),
251
+ gemini: flatFile({ subdir: ['.gemini', 'agents'], ext: '.md', transform: transformSubagentForClaude }),
252
+ grok: flatFile({ subdir: ['.grok', 'agents'], ext: '.md', transform: transformSubagentForClaude }),
253
+ droid: flatFile({ subdir: ['.factory', 'droids'], ext: '.md', transform: transformSubagentForDroid }),
254
+ // Bespoke frontmatter/format, still one flat file.
255
+ codex: flatFile({ subdir: ['.codex', 'agents'], ext: '.toml', transform: transformSubagentForCodex }),
256
+ opencode: flatFile({
257
+ subdir: ['.config', 'opencode', 'agents'],
258
+ ext: '.md',
259
+ transform: transformSubagentForOpenCode,
260
+ readMeta: metaFrontmatterFallback,
261
+ }),
262
+ copilot: flatFile({
263
+ subdir: ['.copilot', 'agents'],
264
+ ext: '.agent.md',
265
+ transform: transformSubagentForCopilot,
266
+ readMeta: metaFrontmatterFallback,
267
+ }),
268
+ cursor: flatFile({
269
+ subdir: ['.cursor', 'agents'],
270
+ ext: '.md',
271
+ transform: transformSubagentForCursor,
272
+ readMeta: metaFrontmatterFallback,
273
+ }),
274
+ forge: flatFile({
275
+ subdir: ['.forge', 'agents'],
276
+ ext: '.md',
277
+ transform: transformSubagentForForge,
278
+ readMeta: metaFrontmatterFallback,
279
+ }),
280
+ kiro: flatFile({
281
+ subdir: ['.kiro', 'agents'],
282
+ ext: '.json',
283
+ transform: transformSubagentForKiro,
284
+ readMeta: metaJson,
285
+ }),
286
+ goose: flatFile({
287
+ subdir: ['.config', 'goose', 'agents'],
288
+ ext: '.yaml',
289
+ transform: transformSubagentForGoose,
290
+ readMeta: metaGooseYaml,
291
+ }),
292
+ // Directory layouts.
293
+ antigravity: dirFile({
294
+ subdir: ['.gemini', 'config', 'agents'],
295
+ file: 'agent.md',
296
+ transform: transformSubagentForAntigravity,
297
+ }),
298
+ openclaw: dirCopy({ subdir: ['.openclaw'], marker: 'AGENTS.md', rename: { 'AGENT.md': 'AGENTS.md' } }),
299
+ // Bespoke multi-file + parent index.
300
+ kimi: kimiTarget,
301
+ };
302
+ /** The registry entry for `agent`, or undefined if it stores no subagents. */
303
+ export function subagentTarget(agent) {
304
+ return SUBAGENT_TARGETS[agent];
305
+ }
306
+ // ── generic engine (zero per-agent branches) ─────────────────────────────────
307
+ /**
308
+ * Materialize central subagent `sub` into `home` for `agent`. Returns whether a
309
+ * write happened (false when the agent has no registry entry). Throws only on
310
+ * unexpected fs errors -- bulk callers wrap per-item.
311
+ */
312
+ export function writeSubagentToHome(agent, home, sub) {
313
+ const target = SUBAGENT_TARGETS[agent];
314
+ if (!target)
315
+ return false;
316
+ target.write(target.dir(home), sub);
317
+ return true;
318
+ }
319
+ /** Installed subagent names for `agent` under `home` (detector + orphan diff). */
320
+ export function listInstalledSubagentNames(agent, home) {
321
+ const target = SUBAGENT_TARGETS[agent];
322
+ if (!target)
323
+ return [];
324
+ return target.names(target.dir(home));
325
+ }
326
+ /**
327
+ * Rich listing of subagents installed for `agent` under `home`, with parsed
328
+ * metadata. Enumerates names, then reads each -- entries whose metadata is
329
+ * unreadable (per the target's reader) are dropped.
330
+ */
331
+ export function listInstalledSubagentsRich(agent, home) {
332
+ const target = SUBAGENT_TARGETS[agent];
333
+ if (!target)
334
+ return [];
335
+ const dir = target.dir(home);
336
+ const out = [];
337
+ for (const name of target.names(dir)) {
338
+ const meta = target.read(dir, name);
339
+ if (!meta)
340
+ continue;
341
+ out.push({ name, path: meta.path, files: meta.files, frontmatter: meta.frontmatter });
342
+ }
343
+ return out;
344
+ }
345
+ /**
346
+ * Remove subagent `name` for `agent` from `home` (hard delete). No-op success
347
+ * when the agent has no registry entry or nothing is installed.
348
+ */
349
+ export function removeSubagentFromHome(agent, home, name) {
350
+ const target = SUBAGENT_TARGETS[agent];
351
+ if (!target)
352
+ return { success: true };
353
+ try {
354
+ for (const entry of target.occupied(target.dir(home), name)) {
355
+ if (!fs.existsSync(entry.path))
356
+ continue;
357
+ if (entry.kind === 'dir')
358
+ fs.rmSync(entry.path, { recursive: true, force: true });
359
+ else
360
+ fs.unlinkSync(entry.path);
361
+ }
362
+ return { success: true };
363
+ }
364
+ catch (err) {
365
+ return { success: false, error: String(err) };
366
+ }
367
+ }
368
+ /**
369
+ * Soft-delete subagent `name` for `agent` from `home` into `trashDir`, stamping
370
+ * each moved entry. Files land as `<basename>.<stamp>`, directories as
371
+ * `<stamp>/`. No-op success when nothing is installed.
372
+ */
373
+ export function trashSubagentFromHome(agent, home, name, trashDir, stamp) {
374
+ const target = SUBAGENT_TARGETS[agent];
375
+ if (!target)
376
+ return { success: true };
377
+ try {
378
+ const present = target.occupied(target.dir(home), name).filter((e) => fs.existsSync(e.path));
379
+ if (present.length > 0) {
380
+ fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
381
+ for (const entry of present) {
382
+ const dest = entry.kind === 'dir'
383
+ ? path.join(trashDir, stamp)
384
+ : path.join(trashDir, `${path.basename(entry.path)}.${stamp}`);
385
+ fs.renameSync(entry.path, dest);
386
+ }
387
+ }
388
+ return { success: true };
389
+ }
390
+ catch (err) {
391
+ return { success: false, error: err.message };
392
+ }
393
+ }
@@ -67,7 +67,7 @@ export declare function transformSubagentForDroid(subagentDir: string): string;
67
67
  * sections, `color` dropped), so this is an alias of transformSubagentForDroid.
68
68
  * See GitHub docs for custom agents.
69
69
  */
70
- export declare const transformSubagentForCopilot: typeof transformSubagentForDroid;
70
+ export declare function transformSubagentForCopilot(subagentDir: string): string;
71
71
  /**
72
72
  * Transform a subagent into a Cursor CLI custom subagent `.md` file.
73
73
  *
@@ -78,7 +78,7 @@ export declare const transformSubagentForCopilot: typeof transformSubagentForDro
78
78
  * an alias of transformSubagentForDroid, same as Copilot.
79
79
  * See https://cursor.com/docs/subagents.
80
80
  */
81
- export declare const transformSubagentForCursor: typeof transformSubagentForDroid;
81
+ export declare function transformSubagentForCursor(subagentDir: string): string;
82
82
  /**
83
83
  * Transform a subagent into a ForgeCode custom subagent `.md` file.
84
84
  *
@@ -88,7 +88,7 @@ export declare const transformSubagentForCursor: typeof transformSubagentForDroi
88
88
  * no `color` field, so this is an alias of transformSubagentForDroid, same as
89
89
  * Copilot/Cursor. See https://forgecode.dev/docs/agent-definition-guide/.
90
90
  */
91
- export declare const transformSubagentForForge: typeof transformSubagentForDroid;
91
+ export declare function transformSubagentForForge(subagentDir: string): string;
92
92
  /**
93
93
  * Transform a subagent into Antigravity's custom-agent markdown shape.
94
94
  *
@@ -197,11 +197,11 @@ export declare function removeSubagentFromAgent(subagentName: string, agent: Age
197
197
  */
198
198
  export declare function subagentContentMatches(installedDir: string, sourceDir: string): boolean;
199
199
  /**
200
- * List subagents installed to a specific agent's home
201
- * Claude/Gemini/Grok: scans ~/.{agent}/agents/{name}.md
202
- * Kimi: scans ~/.kimi-code/agents/{name}.yaml (+ sibling .system.md)
203
- * Kiro: scans ~/.kiro/agents/{name}.json
204
- * OpenClaw: scans ~/.openclaw/{name}/AGENTS.md
200
+ * List subagents installed to a specific agent's home.
201
+ *
202
+ * Layout (target dir, file/dir shape, metadata reader) is declared once per
203
+ * agent in the subagent registry; this is a thin wrapper over the generic
204
+ * enumerator so a new agent needs no arm here -- just a registry entry.
205
205
  */
206
206
  export declare function listSubagentsForAgent(agentId: AgentId, home: string): InstalledSubagent[];
207
207
  export interface VersionSubagentDiff {