@phnx-labs/agents-cli 1.20.63 → 1.20.65
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 +56 -3
- package/README.md +46 -2
- package/dist/bin/agents +0 -0
- package/dist/commands/apply.d.ts +12 -0
- package/dist/commands/apply.js +274 -0
- package/dist/commands/browser.js +2 -2
- package/dist/commands/cloud.js +32 -2
- package/dist/commands/doctor.js +4 -1
- package/dist/commands/exec.js +149 -77
- package/dist/commands/feed.d.ts +4 -0
- package/dist/commands/feed.js +52 -19
- package/dist/commands/hosts.js +44 -6
- package/dist/commands/lease.d.ts +23 -0
- package/dist/commands/lease.js +201 -0
- package/dist/commands/mailboxes.d.ts +20 -0
- package/dist/commands/mailboxes.js +390 -0
- package/dist/commands/mcp.js +55 -5
- package/dist/commands/monitors.d.ts +12 -0
- package/dist/commands/monitors.js +740 -0
- package/dist/commands/output.js +2 -2
- package/dist/commands/routines.js +43 -16
- package/dist/commands/secrets.d.ts +16 -0
- package/dist/commands/secrets.js +215 -64
- package/dist/commands/serve.js +31 -0
- package/dist/commands/sessions-export.d.ts +2 -0
- package/dist/commands/sessions-export.js +284 -0
- package/dist/commands/sessions-import.d.ts +2 -0
- package/dist/commands/sessions-import.js +230 -0
- package/dist/commands/sessions.d.ts +16 -0
- package/dist/commands/sessions.js +40 -6
- package/dist/commands/ssh.js +143 -5
- package/dist/commands/usage.d.ts +2 -0
- package/dist/commands/usage.js +7 -2
- package/dist/commands/versions.js +7 -3
- package/dist/commands/view.d.ts +27 -1
- package/dist/commands/view.js +32 -9
- package/dist/commands/webhook.js +10 -2
- package/dist/index.js +35 -14
- package/dist/lib/agents.d.ts +36 -0
- package/dist/lib/agents.js +80 -20
- package/dist/lib/auto-dispatch-provider.js +7 -2
- package/dist/lib/auto-dispatch.d.ts +3 -0
- package/dist/lib/auto-dispatch.js +3 -0
- package/dist/lib/browser/chrome.js +2 -2
- package/dist/lib/browser/drivers/ssh.js +19 -2
- package/dist/lib/cloud/antigravity.js +2 -2
- package/dist/lib/cloud/host.d.ts +59 -0
- package/dist/lib/cloud/host.js +224 -0
- package/dist/lib/cloud/registry.js +4 -0
- package/dist/lib/cloud/types.d.ts +6 -4
- package/dist/lib/comms-render.d.ts +37 -0
- package/dist/lib/comms-render.js +89 -0
- package/dist/lib/computer-rpc.js +3 -1
- package/dist/lib/crabbox/cli.d.ts +72 -0
- package/dist/lib/crabbox/cli.js +162 -9
- package/dist/lib/crabbox/runtimes.d.ts +13 -0
- package/dist/lib/crabbox/runtimes.js +35 -2
- package/dist/lib/daemon.d.ts +20 -4
- package/dist/lib/daemon.js +68 -20
- package/dist/lib/devices/fleet.d.ts +3 -2
- package/dist/lib/devices/fleet.js +9 -0
- package/dist/lib/devices/health.d.ts +77 -0
- package/dist/lib/devices/health.js +186 -0
- package/dist/lib/devices/registry.d.ts +15 -0
- package/dist/lib/devices/registry.js +9 -0
- package/dist/lib/exec.d.ts +19 -2
- package/dist/lib/exec.js +41 -13
- package/dist/lib/fleet/apply.d.ts +63 -0
- package/dist/lib/fleet/apply.js +214 -0
- package/dist/lib/fleet/auth-sync.d.ts +67 -0
- package/dist/lib/fleet/auth-sync.js +142 -0
- package/dist/lib/fleet/manifest.d.ts +29 -0
- package/dist/lib/fleet/manifest.js +127 -0
- package/dist/lib/fleet/types.d.ts +129 -0
- package/dist/lib/fleet/types.js +13 -0
- package/dist/lib/git.d.ts +27 -0
- package/dist/lib/git.js +34 -2
- package/dist/lib/hosts/dispatch.d.ts +29 -8
- package/dist/lib/hosts/dispatch.js +46 -18
- package/dist/lib/hosts/passthrough.js +2 -0
- package/dist/lib/hosts/providers/devices.d.ts +27 -0
- package/dist/lib/hosts/providers/devices.js +98 -0
- package/dist/lib/hosts/registry.d.ts +10 -16
- package/dist/lib/hosts/registry.js +17 -50
- package/dist/lib/hosts/remote-cmd.d.ts +23 -0
- package/dist/lib/hosts/remote-cmd.js +71 -0
- package/dist/lib/hosts/run-target.d.ts +84 -0
- package/dist/lib/hosts/run-target.js +99 -0
- package/dist/lib/hosts/types.d.ts +23 -5
- package/dist/lib/hosts/types.js +22 -4
- package/dist/lib/linear-autoclose.d.ts +30 -0
- package/dist/lib/linear-autoclose.js +22 -0
- package/dist/lib/mailbox.d.ts +39 -0
- package/dist/lib/mailbox.js +112 -0
- package/dist/lib/mcp.d.ts +27 -1
- package/dist/lib/mcp.js +126 -12
- package/dist/lib/monitors/config.d.ts +161 -0
- package/dist/lib/monitors/config.js +372 -0
- package/dist/lib/monitors/dispatch.d.ts +28 -0
- package/dist/lib/monitors/dispatch.js +91 -0
- package/dist/lib/monitors/engine.d.ts +61 -0
- package/dist/lib/monitors/engine.js +201 -0
- package/dist/lib/monitors/sources/command.d.ts +11 -0
- package/dist/lib/monitors/sources/command.js +31 -0
- package/dist/lib/monitors/sources/device.d.ts +13 -0
- package/dist/lib/monitors/sources/device.js +35 -0
- package/dist/lib/monitors/sources/file.d.ts +14 -0
- package/dist/lib/monitors/sources/file.js +57 -0
- package/dist/lib/monitors/sources/http.d.ts +10 -0
- package/dist/lib/monitors/sources/http.js +34 -0
- package/dist/lib/monitors/sources/index.d.ts +14 -0
- package/dist/lib/monitors/sources/index.js +31 -0
- package/dist/lib/monitors/sources/poll.d.ts +9 -0
- package/dist/lib/monitors/sources/poll.js +9 -0
- package/dist/lib/monitors/sources/types.d.ts +18 -0
- package/dist/lib/monitors/sources/types.js +9 -0
- package/dist/lib/monitors/sources/webhook.d.ts +23 -0
- package/dist/lib/monitors/sources/webhook.js +47 -0
- package/dist/lib/monitors/sources/ws.d.ts +14 -0
- package/dist/lib/monitors/sources/ws.js +45 -0
- package/dist/lib/monitors/state.d.ts +69 -0
- package/dist/lib/monitors/state.js +144 -0
- package/dist/lib/paths.d.ts +13 -0
- package/dist/lib/paths.js +26 -4
- package/dist/lib/platform/exec.d.ts +16 -0
- package/dist/lib/platform/exec.js +17 -0
- package/dist/lib/plugins.js +101 -2
- package/dist/lib/redact.d.ts +14 -1
- package/dist/lib/redact.js +47 -1
- package/dist/lib/remote-agents-json.js +7 -1
- package/dist/lib/rotate.d.ts +6 -3
- package/dist/lib/rotate.js +0 -1
- package/dist/lib/routines.d.ts +37 -2
- package/dist/lib/routines.js +54 -12
- package/dist/lib/runner.d.ts +1 -0
- package/dist/lib/runner.js +348 -13
- package/dist/lib/sandbox.d.ts +9 -1
- package/dist/lib/sandbox.js +11 -2
- package/dist/lib/secrets/agent.d.ts +48 -10
- package/dist/lib/secrets/agent.js +123 -15
- package/dist/lib/secrets/bundles.d.ts +26 -0
- package/dist/lib/secrets/bundles.js +59 -8
- package/dist/lib/secrets/mcp.js +4 -2
- package/dist/lib/secrets/remote.d.ts +17 -0
- package/dist/lib/secrets/remote.js +40 -0
- package/dist/lib/self-update.d.ts +20 -0
- package/dist/lib/self-update.js +54 -1
- package/dist/lib/serve/control.d.ts +95 -0
- package/dist/lib/serve/control.js +260 -0
- package/dist/lib/serve/server.d.ts +35 -1
- package/dist/lib/serve/server.js +106 -76
- package/dist/lib/serve/stream.d.ts +43 -0
- package/dist/lib/serve/stream.js +116 -0
- package/dist/lib/serve/token.d.ts +35 -0
- package/dist/lib/serve/token.js +85 -0
- package/dist/lib/session/bundle.d.ts +164 -0
- package/dist/lib/session/bundle.js +200 -0
- package/dist/lib/session/remote-bundle.d.ts +12 -0
- package/dist/lib/session/remote-bundle.js +61 -0
- package/dist/lib/session/remote-list.js +5 -1
- package/dist/lib/session/state.d.ts +7 -25
- package/dist/lib/session/state.js +16 -6
- package/dist/lib/session/sync/agents.d.ts +54 -6
- package/dist/lib/session/sync/agents.js +0 -0
- package/dist/lib/session/sync/config.js +8 -2
- package/dist/lib/session/sync/manifest.d.ts +14 -3
- package/dist/lib/session/sync/manifest.js +4 -0
- package/dist/lib/session/sync/sync.d.ts +23 -2
- package/dist/lib/session/sync/sync.js +177 -74
- package/dist/lib/session/types.d.ts +30 -0
- package/dist/lib/ssh-tunnel.d.ts +19 -1
- package/dist/lib/ssh-tunnel.js +99 -8
- package/dist/lib/staleness/detectors/subagents.d.ts +5 -0
- package/dist/lib/staleness/detectors/subagents.js +5 -192
- package/dist/lib/staleness/writers/subagents.d.ts +10 -0
- package/dist/lib/staleness/writers/subagents.js +11 -102
- package/dist/lib/startup/command-registry.d.ts +4 -0
- package/dist/lib/startup/command-registry.js +9 -0
- package/dist/lib/state.d.ts +5 -0
- package/dist/lib/state.js +12 -0
- package/dist/lib/subagents-registry.d.ts +85 -0
- package/dist/lib/subagents-registry.js +393 -0
- package/dist/lib/subagents.d.ts +8 -8
- package/dist/lib/subagents.js +32 -663
- package/dist/lib/sync-umbrella.d.ts +1 -0
- package/dist/lib/sync-umbrella.js +14 -3
- package/dist/lib/tmux/session.d.ts +7 -0
- package/dist/lib/tmux/session.js +3 -1
- package/dist/lib/triggers/webhook.d.ts +18 -0
- package/dist/lib/triggers/webhook.js +105 -0
- package/dist/lib/types.d.ts +35 -1
- package/dist/lib/usage.d.ts +42 -3
- package/dist/lib/usage.js +163 -21
- package/dist/lib/versions.js +14 -11
- package/dist/lib/workflows.d.ts +20 -0
- package/dist/lib/workflows.js +24 -0
- package/package.json +2 -1
package/dist/lib/subagents.js
CHANGED
|
@@ -13,6 +13,7 @@ import { capableAgents } from './capabilities.js';
|
|
|
13
13
|
import { getSubagentsDir, getUserSubagentsDir, getTrashSubagentsDir } from './state.js';
|
|
14
14
|
import { listInstalledVersions, getVersionHomePath } from './versions.js';
|
|
15
15
|
import { safeJoin } from './paths.js';
|
|
16
|
+
import { subagentTarget, writeSubagentToHome, listInstalledSubagentNames, listInstalledSubagentsRich, removeSubagentFromHome, trashSubagentFromHome, copyDirWithRename, } from './subagents-registry.js';
|
|
16
17
|
/**
|
|
17
18
|
* Parse AGENT.md frontmatter to extract subagent metadata
|
|
18
19
|
*/
|
|
@@ -273,7 +274,9 @@ export function transformSubagentForDroid(subagentDir) {
|
|
|
273
274
|
* sections, `color` dropped), so this is an alias of transformSubagentForDroid.
|
|
274
275
|
* See GitHub docs for custom agents.
|
|
275
276
|
*/
|
|
276
|
-
export
|
|
277
|
+
export function transformSubagentForCopilot(subagentDir) {
|
|
278
|
+
return transformSubagentForDroid(subagentDir);
|
|
279
|
+
}
|
|
277
280
|
/**
|
|
278
281
|
* Transform a subagent into a Cursor CLI custom subagent `.md` file.
|
|
279
282
|
*
|
|
@@ -284,7 +287,9 @@ export const transformSubagentForCopilot = transformSubagentForDroid;
|
|
|
284
287
|
* an alias of transformSubagentForDroid, same as Copilot.
|
|
285
288
|
* See https://cursor.com/docs/subagents.
|
|
286
289
|
*/
|
|
287
|
-
export
|
|
290
|
+
export function transformSubagentForCursor(subagentDir) {
|
|
291
|
+
return transformSubagentForDroid(subagentDir);
|
|
292
|
+
}
|
|
288
293
|
/**
|
|
289
294
|
* Transform a subagent into a ForgeCode custom subagent `.md` file.
|
|
290
295
|
*
|
|
@@ -294,7 +299,9 @@ export const transformSubagentForCursor = transformSubagentForDroid;
|
|
|
294
299
|
* no `color` field, so this is an alias of transformSubagentForDroid, same as
|
|
295
300
|
* Copilot/Cursor. See https://forgecode.dev/docs/agent-definition-guide/.
|
|
296
301
|
*/
|
|
297
|
-
export
|
|
302
|
+
export function transformSubagentForForge(subagentDir) {
|
|
303
|
+
return transformSubagentForDroid(subagentDir);
|
|
304
|
+
}
|
|
298
305
|
/**
|
|
299
306
|
* Transform a subagent into Antigravity's custom-agent markdown shape.
|
|
300
307
|
*
|
|
@@ -559,19 +566,7 @@ export function transformSubagentForGoose(subagentDir) {
|
|
|
559
566
|
*/
|
|
560
567
|
export function syncSubagentToOpenclaw(subagentDir, targetDir) {
|
|
561
568
|
try {
|
|
562
|
-
|
|
563
|
-
if (!fs.existsSync(targetDir)) {
|
|
564
|
-
fs.mkdirSync(targetDir, { recursive: true });
|
|
565
|
-
}
|
|
566
|
-
// Copy all files
|
|
567
|
-
for (const file of fs.readdirSync(subagentDir)) {
|
|
568
|
-
const sourcePath = path.join(subagentDir, file);
|
|
569
|
-
const targetFile = file === 'AGENT.md' ? 'AGENTS.md' : file;
|
|
570
|
-
const targetPath = path.join(targetDir, targetFile);
|
|
571
|
-
if (fs.statSync(sourcePath).isFile()) {
|
|
572
|
-
fs.copyFileSync(sourcePath, targetPath);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
569
|
+
copyDirWithRename(subagentDir, targetDir, { 'AGENT.md': 'AGENTS.md' });
|
|
575
570
|
return { success: true };
|
|
576
571
|
}
|
|
577
572
|
catch (err) {
|
|
@@ -582,227 +577,23 @@ export function syncSubagentToOpenclaw(subagentDir, targetDir) {
|
|
|
582
577
|
* Install a subagent to a specific agent's home
|
|
583
578
|
*/
|
|
584
579
|
export function installSubagentToAgent(subagentDir, subagentName, agent, agentHome) {
|
|
585
|
-
if (agent
|
|
586
|
-
|
|
587
|
-
const agentsRoot = agent === 'grok' ? '.grok' : agent === 'gemini' ? '.gemini' : '.claude';
|
|
588
|
-
const agentsDir = path.join(agentHome, agentsRoot, 'agents');
|
|
589
|
-
if (!fs.existsSync(agentsDir)) {
|
|
590
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
591
|
-
}
|
|
592
|
-
try {
|
|
593
|
-
const transformed = transformSubagentForClaude(subagentDir);
|
|
594
|
-
fs.writeFileSync(safeJoin(agentsDir, `${subagentName}.md`), transformed);
|
|
595
|
-
return { success: true };
|
|
596
|
-
}
|
|
597
|
-
catch (err) {
|
|
598
|
-
return { success: false, error: String(err) };
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
else if (agent === 'codex') {
|
|
602
|
-
// Codex: standalone TOML under ~/.codex/agents/<name>.toml
|
|
603
|
-
const agentsDir = path.join(agentHome, '.codex', 'agents');
|
|
604
|
-
if (!fs.existsSync(agentsDir)) {
|
|
605
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
606
|
-
}
|
|
607
|
-
try {
|
|
608
|
-
const toml = transformSubagentForCodex(subagentDir);
|
|
609
|
-
fs.writeFileSync(safeJoin(agentsDir, `${subagentName}.toml`), toml);
|
|
610
|
-
return { success: true };
|
|
611
|
-
}
|
|
612
|
-
catch (err) {
|
|
613
|
-
return { success: false, error: String(err) };
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
else if (agent === 'kimi') {
|
|
617
|
-
// Kimi: YAML + sibling system-prompt md under ~/.kimi-code/agents/
|
|
618
|
-
try {
|
|
619
|
-
writeKimiSubagentFiles(path.join(agentHome, '.kimi-code', 'agents'), subagentDir, subagentName);
|
|
620
|
-
return { success: true };
|
|
621
|
-
}
|
|
622
|
-
catch (err) {
|
|
623
|
-
return { success: false, error: String(err) };
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
else if (agent === 'opencode') {
|
|
627
|
-
// OpenCode: markdown agents under ~/.config/opencode/agents/
|
|
628
|
-
const agentsDir = path.join(agentHome, '.config', 'opencode', 'agents');
|
|
629
|
-
if (!fs.existsSync(agentsDir))
|
|
630
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
631
|
-
try {
|
|
632
|
-
fs.writeFileSync(safeJoin(agentsDir, `${subagentName}.md`), transformSubagentForOpenCode(subagentDir));
|
|
633
|
-
return { success: true };
|
|
634
|
-
}
|
|
635
|
-
catch (err) {
|
|
636
|
-
return { success: false, error: String(err) };
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
else if (agent === 'antigravity') {
|
|
640
|
-
// Antigravity: custom-agent markdown under ~/.gemini/config/agents/<name>/agent.md.
|
|
641
|
-
const agentDir = safeJoin(path.join(agentHome, '.gemini', 'config', 'agents'), subagentName);
|
|
642
|
-
if (!fs.existsSync(agentDir))
|
|
643
|
-
fs.mkdirSync(agentDir, { recursive: true });
|
|
644
|
-
try {
|
|
645
|
-
fs.writeFileSync(safeJoin(agentDir, 'agent.md'), transformSubagentForAntigravity(subagentDir));
|
|
646
|
-
return { success: true };
|
|
647
|
-
}
|
|
648
|
-
catch (err) {
|
|
649
|
-
return { success: false, error: String(err) };
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
else if (agent === 'openclaw') {
|
|
653
|
-
// OpenClaw: copy full directory
|
|
654
|
-
const targetDir = safeJoin(path.join(agentHome, '.openclaw'), subagentName);
|
|
655
|
-
return syncSubagentToOpenclaw(subagentDir, targetDir);
|
|
656
|
-
}
|
|
657
|
-
else if (agent === 'kiro') {
|
|
658
|
-
// Kiro: JSON custom-agent file under ~/.kiro/agents/
|
|
659
|
-
const agentsDir = path.join(agentHome, '.kiro', 'agents');
|
|
660
|
-
if (!fs.existsSync(agentsDir)) {
|
|
661
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
662
|
-
}
|
|
663
|
-
try {
|
|
664
|
-
const transformed = transformSubagentForKiro(subagentDir);
|
|
665
|
-
fs.writeFileSync(safeJoin(agentsDir, `${subagentName}.json`), transformed);
|
|
666
|
-
return { success: true };
|
|
667
|
-
}
|
|
668
|
-
catch (err) {
|
|
669
|
-
return { success: false, error: String(err) };
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
else if (agent === 'cursor') {
|
|
673
|
-
// Cursor: flattened .md custom subagent under ~/.cursor/agents/
|
|
674
|
-
const agentsDir = path.join(agentHome, '.cursor', 'agents');
|
|
675
|
-
if (!fs.existsSync(agentsDir)) {
|
|
676
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
677
|
-
}
|
|
678
|
-
try {
|
|
679
|
-
const transformed = transformSubagentForCursor(subagentDir);
|
|
680
|
-
fs.writeFileSync(safeJoin(agentsDir, `${subagentName}.md`), transformed);
|
|
681
|
-
return { success: true };
|
|
682
|
-
}
|
|
683
|
-
catch (err) {
|
|
684
|
-
return { success: false, error: String(err) };
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
else if (agent === 'forge') {
|
|
688
|
-
// ForgeCode: flattened .md custom subagent under ~/.forge/agents/
|
|
689
|
-
const agentsDir = path.join(agentHome, '.forge', 'agents');
|
|
690
|
-
if (!fs.existsSync(agentsDir)) {
|
|
691
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
692
|
-
}
|
|
693
|
-
try {
|
|
694
|
-
const transformed = transformSubagentForForge(subagentDir);
|
|
695
|
-
fs.writeFileSync(safeJoin(agentsDir, `${subagentName}.md`), transformed);
|
|
696
|
-
return { success: true };
|
|
697
|
-
}
|
|
698
|
-
catch (err) {
|
|
699
|
-
return { success: false, error: String(err) };
|
|
700
|
-
}
|
|
580
|
+
if (!subagentTarget(agent)) {
|
|
581
|
+
return { success: false, error: `Agent '${agent}' does not support subagents` };
|
|
701
582
|
}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
if (!fs.existsSync(agentsDir)) {
|
|
706
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
707
|
-
}
|
|
708
|
-
try {
|
|
709
|
-
const transformed = transformSubagentForGoose(subagentDir);
|
|
710
|
-
fs.writeFileSync(safeJoin(agentsDir, `${subagentName}.yaml`), transformed);
|
|
711
|
-
return { success: true };
|
|
712
|
-
}
|
|
713
|
-
catch (err) {
|
|
714
|
-
return { success: false, error: String(err) };
|
|
715
|
-
}
|
|
583
|
+
try {
|
|
584
|
+
writeSubagentToHome(agent, agentHome, { name: subagentName, path: subagentDir });
|
|
585
|
+
return { success: true };
|
|
716
586
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
return { success: false, error: `Agent '${agent}' does not support subagents` };
|
|
587
|
+
catch (err) {
|
|
588
|
+
return { success: false, error: String(err) };
|
|
720
589
|
}
|
|
721
590
|
}
|
|
722
591
|
/**
|
|
723
592
|
* Remove a subagent from a specific agent's home
|
|
724
593
|
*/
|
|
725
594
|
export function removeSubagentFromAgent(subagentName, agent, agentHome) {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
const agentsRoot = agent === 'grok' ? '.grok' : agent === 'gemini' ? '.gemini' : '.claude';
|
|
729
|
-
const targetPath = safeJoin(path.join(agentHome, agentsRoot, 'agents'), `${subagentName}.md`);
|
|
730
|
-
if (fs.existsSync(targetPath)) {
|
|
731
|
-
fs.unlinkSync(targetPath);
|
|
732
|
-
}
|
|
733
|
-
return { success: true };
|
|
734
|
-
}
|
|
735
|
-
else if (agent === 'codex') {
|
|
736
|
-
const targetPath = safeJoin(path.join(agentHome, '.codex', 'agents'), `${subagentName}.toml`);
|
|
737
|
-
if (fs.existsSync(targetPath)) {
|
|
738
|
-
fs.unlinkSync(targetPath);
|
|
739
|
-
}
|
|
740
|
-
return { success: true };
|
|
741
|
-
}
|
|
742
|
-
else if (agent === 'kimi') {
|
|
743
|
-
const agentsDir = path.join(agentHome, '.kimi-code', 'agents');
|
|
744
|
-
const targetPath = safeJoin(agentsDir, `${subagentName}.yaml`);
|
|
745
|
-
const promptPath = safeJoin(agentsDir, `${subagentName}.system.md`);
|
|
746
|
-
if (fs.existsSync(targetPath))
|
|
747
|
-
fs.unlinkSync(targetPath);
|
|
748
|
-
if (fs.existsSync(promptPath))
|
|
749
|
-
fs.unlinkSync(promptPath);
|
|
750
|
-
return { success: true };
|
|
751
|
-
}
|
|
752
|
-
else if (agent === 'opencode') {
|
|
753
|
-
const targetPath = safeJoin(path.join(agentHome, '.config', 'opencode', 'agents'), `${subagentName}.md`);
|
|
754
|
-
if (fs.existsSync(targetPath))
|
|
755
|
-
fs.unlinkSync(targetPath);
|
|
756
|
-
return { success: true };
|
|
757
|
-
}
|
|
758
|
-
else if (agent === 'antigravity') {
|
|
759
|
-
const targetDir = safeJoin(path.join(agentHome, '.gemini', 'config', 'agents'), subagentName);
|
|
760
|
-
if (fs.existsSync(targetDir))
|
|
761
|
-
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
762
|
-
return { success: true };
|
|
763
|
-
}
|
|
764
|
-
else if (agent === 'openclaw') {
|
|
765
|
-
const targetDir = safeJoin(path.join(agentHome, '.openclaw'), subagentName);
|
|
766
|
-
if (fs.existsSync(targetDir)) {
|
|
767
|
-
fs.rmSync(targetDir, { recursive: true });
|
|
768
|
-
}
|
|
769
|
-
return { success: true };
|
|
770
|
-
}
|
|
771
|
-
else if (agent === 'kiro') {
|
|
772
|
-
const targetPath = safeJoin(path.join(agentHome, '.kiro', 'agents'), `${subagentName}.json`);
|
|
773
|
-
if (fs.existsSync(targetPath)) {
|
|
774
|
-
fs.unlinkSync(targetPath);
|
|
775
|
-
}
|
|
776
|
-
return { success: true };
|
|
777
|
-
}
|
|
778
|
-
else if (agent === 'cursor') {
|
|
779
|
-
const targetPath = safeJoin(path.join(agentHome, '.cursor', 'agents'), `${subagentName}.md`);
|
|
780
|
-
if (fs.existsSync(targetPath)) {
|
|
781
|
-
fs.unlinkSync(targetPath);
|
|
782
|
-
}
|
|
783
|
-
return { success: true };
|
|
784
|
-
}
|
|
785
|
-
else if (agent === 'forge') {
|
|
786
|
-
const targetPath = safeJoin(path.join(agentHome, '.forge', 'agents'), `${subagentName}.md`);
|
|
787
|
-
if (fs.existsSync(targetPath)) {
|
|
788
|
-
fs.unlinkSync(targetPath);
|
|
789
|
-
}
|
|
790
|
-
return { success: true };
|
|
791
|
-
}
|
|
792
|
-
else if (agent === 'goose') {
|
|
793
|
-
const targetPath = safeJoin(path.join(agentHome, '.config', 'goose', 'agents'), `${subagentName}.yaml`);
|
|
794
|
-
if (fs.existsSync(targetPath)) {
|
|
795
|
-
fs.unlinkSync(targetPath);
|
|
796
|
-
}
|
|
797
|
-
return { success: true };
|
|
798
|
-
}
|
|
799
|
-
else {
|
|
800
|
-
return { success: true }; // No-op for unsupported agents
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
catch (err) {
|
|
804
|
-
return { success: false, error: String(err) };
|
|
805
|
-
}
|
|
595
|
+
// No-op success for agents without a registry entry, matching prior behavior.
|
|
596
|
+
return removeSubagentFromHome(agent, agentHome, subagentName);
|
|
806
597
|
}
|
|
807
598
|
/**
|
|
808
599
|
* Check if subagent content matches between source and installed
|
|
@@ -832,242 +623,14 @@ export function subagentContentMatches(installedDir, sourceDir) {
|
|
|
832
623
|
// lib/capabilities.ts. The capability matrix on AgentConfig is the single
|
|
833
624
|
// source of truth.
|
|
834
625
|
/**
|
|
835
|
-
* List subagents installed to a specific agent's home
|
|
836
|
-
*
|
|
837
|
-
*
|
|
838
|
-
*
|
|
839
|
-
*
|
|
626
|
+
* List subagents installed to a specific agent's home.
|
|
627
|
+
*
|
|
628
|
+
* Layout (target dir, file/dir shape, metadata reader) is declared once per
|
|
629
|
+
* agent in the subagent registry; this is a thin wrapper over the generic
|
|
630
|
+
* enumerator so a new agent needs no arm here -- just a registry entry.
|
|
840
631
|
*/
|
|
841
632
|
export function listSubagentsForAgent(agentId, home) {
|
|
842
|
-
|
|
843
|
-
if (agentId === 'claude' || agentId === 'gemini' || agentId === 'grok') {
|
|
844
|
-
// Claude / Gemini / Grok: flat .md files in agents/
|
|
845
|
-
const agentsRoot = agentId === 'grok' ? '.grok' : agentId === 'gemini' ? '.gemini' : '.claude';
|
|
846
|
-
const agentsDir = path.join(home, agentsRoot, 'agents');
|
|
847
|
-
if (!fs.existsSync(agentsDir))
|
|
848
|
-
return subagents;
|
|
849
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
850
|
-
if (!file.endsWith('.md'))
|
|
851
|
-
continue;
|
|
852
|
-
const filePath = path.join(agentsDir, file);
|
|
853
|
-
if (!fs.statSync(filePath).isFile())
|
|
854
|
-
continue;
|
|
855
|
-
const frontmatter = parseSubagentFrontmatter(filePath);
|
|
856
|
-
if (!frontmatter)
|
|
857
|
-
continue;
|
|
858
|
-
const name = file.replace('.md', '');
|
|
859
|
-
subagents.push({
|
|
860
|
-
name,
|
|
861
|
-
path: filePath,
|
|
862
|
-
files: [file],
|
|
863
|
-
frontmatter,
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
else if (agentId === 'kimi') {
|
|
868
|
-
// Kimi: flat YAML under .kimi-code/agents/ (exclude managed parent _*.yaml)
|
|
869
|
-
const agentsDir = path.join(home, '.kimi-code', 'agents');
|
|
870
|
-
if (!fs.existsSync(agentsDir))
|
|
871
|
-
return subagents;
|
|
872
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
873
|
-
if (!file.endsWith('.yaml') || file.startsWith('_'))
|
|
874
|
-
continue;
|
|
875
|
-
const filePath = path.join(agentsDir, file);
|
|
876
|
-
if (!fs.statSync(filePath).isFile())
|
|
877
|
-
continue;
|
|
878
|
-
const name = file.replace(/\.yaml$/, '');
|
|
879
|
-
let description = '';
|
|
880
|
-
try {
|
|
881
|
-
const parsed = yaml.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
882
|
-
description = parsed?.agent?.description ?? '';
|
|
883
|
-
}
|
|
884
|
-
catch { /* leave description empty */ }
|
|
885
|
-
const files = [file];
|
|
886
|
-
const promptFile = `${name}.system.md`;
|
|
887
|
-
if (fs.existsSync(path.join(agentsDir, promptFile)))
|
|
888
|
-
files.push(promptFile);
|
|
889
|
-
subagents.push({
|
|
890
|
-
name,
|
|
891
|
-
path: filePath,
|
|
892
|
-
files,
|
|
893
|
-
frontmatter: { name, description },
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
else if (agentId === 'opencode') {
|
|
898
|
-
const agentsDir = path.join(home, '.config', 'opencode', 'agents');
|
|
899
|
-
if (!fs.existsSync(agentsDir))
|
|
900
|
-
return subagents;
|
|
901
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
902
|
-
if (!file.endsWith('.md'))
|
|
903
|
-
continue;
|
|
904
|
-
const filePath = path.join(agentsDir, file);
|
|
905
|
-
if (!fs.statSync(filePath).isFile())
|
|
906
|
-
continue;
|
|
907
|
-
const name = file.replace(/\.md$/, '');
|
|
908
|
-
const frontmatter = parseSubagentFrontmatter(filePath) ?? { name, description: '' };
|
|
909
|
-
subagents.push({ name, path: filePath, files: [file], frontmatter });
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
else if (agentId === 'antigravity') {
|
|
913
|
-
const agentsDir = path.join(home, '.gemini', 'config', 'agents');
|
|
914
|
-
if (!fs.existsSync(agentsDir))
|
|
915
|
-
return subagents;
|
|
916
|
-
for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
|
|
917
|
-
if (!entry.isDirectory())
|
|
918
|
-
continue;
|
|
919
|
-
const filePath = path.join(agentsDir, entry.name, 'agent.md');
|
|
920
|
-
if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile())
|
|
921
|
-
continue;
|
|
922
|
-
const frontmatter = parseSubagentFrontmatter(filePath) ?? { name: entry.name, description: '' };
|
|
923
|
-
subagents.push({ name: entry.name, path: filePath, files: ['agent.md'], frontmatter });
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
else if (agentId === 'copilot') {
|
|
927
|
-
// Copilot: flat `<name>.agent.md` files under ~/.copilot/agents/
|
|
928
|
-
const agentsDir = path.join(home, '.copilot', 'agents');
|
|
929
|
-
if (!fs.existsSync(agentsDir))
|
|
930
|
-
return subagents;
|
|
931
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
932
|
-
if (!file.endsWith('.agent.md'))
|
|
933
|
-
continue;
|
|
934
|
-
const filePath = path.join(agentsDir, file);
|
|
935
|
-
if (!fs.statSync(filePath).isFile())
|
|
936
|
-
continue;
|
|
937
|
-
const name = file.replace(/\.agent\.md$/, '');
|
|
938
|
-
const frontmatter = parseSubagentFrontmatter(filePath) ?? { name, description: '' };
|
|
939
|
-
subagents.push({ name, path: filePath, files: [file], frontmatter });
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
else if (agentId === 'openclaw') {
|
|
943
|
-
// OpenClaw: directories with AGENTS.md
|
|
944
|
-
const openclawDir = path.join(home, '.openclaw');
|
|
945
|
-
if (!fs.existsSync(openclawDir))
|
|
946
|
-
return subagents;
|
|
947
|
-
for (const dir of fs.readdirSync(openclawDir)) {
|
|
948
|
-
const dirPath = path.join(openclawDir, dir);
|
|
949
|
-
if (!fs.statSync(dirPath).isDirectory())
|
|
950
|
-
continue;
|
|
951
|
-
// OpenClaw uses AGENTS.md (not AGENT.md)
|
|
952
|
-
const agentsMd = path.join(dirPath, 'AGENTS.md');
|
|
953
|
-
if (!fs.existsSync(agentsMd))
|
|
954
|
-
continue;
|
|
955
|
-
// Parse frontmatter - OpenClaw may not have standard frontmatter
|
|
956
|
-
// Try to extract name/description from content
|
|
957
|
-
const content = fs.readFileSync(agentsMd, 'utf-8');
|
|
958
|
-
let frontmatter = {
|
|
959
|
-
name: dir,
|
|
960
|
-
description: '',
|
|
961
|
-
};
|
|
962
|
-
// Try to parse frontmatter if present
|
|
963
|
-
const parsed = parseSubagentFrontmatter(agentsMd);
|
|
964
|
-
if (parsed) {
|
|
965
|
-
frontmatter = parsed;
|
|
966
|
-
}
|
|
967
|
-
else {
|
|
968
|
-
// Extract first non-empty line as description
|
|
969
|
-
const firstLine = content.split('\n').find(l => l.trim() && !l.startsWith('#'));
|
|
970
|
-
frontmatter.description = firstLine?.slice(0, 80) || `OpenClaw agent: ${dir}`;
|
|
971
|
-
}
|
|
972
|
-
const files = fs.readdirSync(dirPath)
|
|
973
|
-
.filter(f => f.endsWith('.md'))
|
|
974
|
-
.sort();
|
|
975
|
-
subagents.push({
|
|
976
|
-
name: dir,
|
|
977
|
-
path: dirPath,
|
|
978
|
-
files,
|
|
979
|
-
frontmatter,
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
else if (agentId === 'kiro') {
|
|
984
|
-
// Kiro: JSON files under ~/.kiro/agents/
|
|
985
|
-
const agentsDir = path.join(home, '.kiro', 'agents');
|
|
986
|
-
if (!fs.existsSync(agentsDir))
|
|
987
|
-
return subagents;
|
|
988
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
989
|
-
if (!file.endsWith('.json'))
|
|
990
|
-
continue;
|
|
991
|
-
const filePath = path.join(agentsDir, file);
|
|
992
|
-
if (!fs.statSync(filePath).isFile())
|
|
993
|
-
continue;
|
|
994
|
-
let frontmatter;
|
|
995
|
-
try {
|
|
996
|
-
const config = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
997
|
-
frontmatter = {
|
|
998
|
-
name: config.name || file.replace('.json', ''),
|
|
999
|
-
description: config.description || '',
|
|
1000
|
-
model: config.model,
|
|
1001
|
-
};
|
|
1002
|
-
}
|
|
1003
|
-
catch {
|
|
1004
|
-
continue;
|
|
1005
|
-
}
|
|
1006
|
-
const name = file.replace('.json', '');
|
|
1007
|
-
subagents.push({
|
|
1008
|
-
name,
|
|
1009
|
-
path: filePath,
|
|
1010
|
-
files: [file],
|
|
1011
|
-
frontmatter,
|
|
1012
|
-
});
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
else if (agentId === 'cursor') {
|
|
1016
|
-
// Cursor: flat `<name>.md` files under ~/.cursor/agents/
|
|
1017
|
-
const agentsDir = path.join(home, '.cursor', 'agents');
|
|
1018
|
-
if (!fs.existsSync(agentsDir))
|
|
1019
|
-
return subagents;
|
|
1020
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1021
|
-
if (!file.endsWith('.md'))
|
|
1022
|
-
continue;
|
|
1023
|
-
const filePath = path.join(agentsDir, file);
|
|
1024
|
-
if (!fs.statSync(filePath).isFile())
|
|
1025
|
-
continue;
|
|
1026
|
-
const name = file.replace(/\.md$/, '');
|
|
1027
|
-
const frontmatter = parseSubagentFrontmatter(filePath) ?? { name, description: '' };
|
|
1028
|
-
subagents.push({ name, path: filePath, files: [file], frontmatter });
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
else if (agentId === 'forge') {
|
|
1032
|
-
// ForgeCode: flat `<name>.md` files under ~/.forge/agents/
|
|
1033
|
-
const agentsDir = path.join(home, '.forge', 'agents');
|
|
1034
|
-
if (!fs.existsSync(agentsDir))
|
|
1035
|
-
return subagents;
|
|
1036
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1037
|
-
if (!file.endsWith('.md'))
|
|
1038
|
-
continue;
|
|
1039
|
-
const filePath = path.join(agentsDir, file);
|
|
1040
|
-
if (!fs.statSync(filePath).isFile())
|
|
1041
|
-
continue;
|
|
1042
|
-
const name = file.replace(/\.md$/, '');
|
|
1043
|
-
const frontmatter = parseSubagentFrontmatter(filePath) ?? { name, description: '' };
|
|
1044
|
-
subagents.push({ name, path: filePath, files: [file], frontmatter });
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
else if (agentId === 'goose') {
|
|
1048
|
-
// Goose: recipe YAML files under ~/.config/goose/agents/
|
|
1049
|
-
const agentsDir = path.join(home, '.config', 'goose', 'agents');
|
|
1050
|
-
if (!fs.existsSync(agentsDir))
|
|
1051
|
-
return subagents;
|
|
1052
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1053
|
-
if (!file.endsWith('.yaml'))
|
|
1054
|
-
continue;
|
|
1055
|
-
const filePath = path.join(agentsDir, file);
|
|
1056
|
-
if (!fs.statSync(filePath).isFile())
|
|
1057
|
-
continue;
|
|
1058
|
-
const name = file.replace(/\.yaml$/, '');
|
|
1059
|
-
let frontmatter;
|
|
1060
|
-
try {
|
|
1061
|
-
const recipe = yaml.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
1062
|
-
frontmatter = { name: recipe?.title || name, description: recipe?.description || '' };
|
|
1063
|
-
}
|
|
1064
|
-
catch {
|
|
1065
|
-
continue;
|
|
1066
|
-
}
|
|
1067
|
-
subagents.push({ name, path: filePath, files: [file], frontmatter });
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
return subagents;
|
|
633
|
+
return listInstalledSubagentsRich(agentId, home);
|
|
1071
634
|
}
|
|
1072
635
|
/**
|
|
1073
636
|
* Compare a version home's subagents against discovered subagents.
|
|
@@ -1075,7 +638,6 @@ export function listSubagentsForAgent(agentId, home) {
|
|
|
1075
638
|
*/
|
|
1076
639
|
export function diffVersionSubagents(agent, version) {
|
|
1077
640
|
const versionHome = getVersionHomePath(agent, version);
|
|
1078
|
-
const orphans = [];
|
|
1079
641
|
// Get all discovered subagent names
|
|
1080
642
|
const discovered = new Set();
|
|
1081
643
|
for (const dir of [getSubagentsDir(), getUserSubagentsDir()]) {
|
|
@@ -1087,120 +649,9 @@ export function diffVersionSubagents(agent, version) {
|
|
|
1087
649
|
}
|
|
1088
650
|
}
|
|
1089
651
|
}
|
|
1090
|
-
//
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
const agentsDir = path.join(versionHome, agentsRoot, 'agents');
|
|
1094
|
-
if (fs.existsSync(agentsDir)) {
|
|
1095
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1096
|
-
if (!file.endsWith('.md'))
|
|
1097
|
-
continue;
|
|
1098
|
-
const name = path.basename(file, '.md');
|
|
1099
|
-
if (!discovered.has(name)) {
|
|
1100
|
-
orphans.push(name);
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
else if (agent === 'kimi') {
|
|
1106
|
-
const agentsDir = path.join(versionHome, '.kimi-code', 'agents');
|
|
1107
|
-
if (fs.existsSync(agentsDir)) {
|
|
1108
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1109
|
-
if (!file.endsWith('.yaml') || file.startsWith('_'))
|
|
1110
|
-
continue;
|
|
1111
|
-
const name = path.basename(file, '.yaml');
|
|
1112
|
-
if (!discovered.has(name)) {
|
|
1113
|
-
orphans.push(name);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
else if (agent === 'opencode') {
|
|
1119
|
-
const agentsDir = path.join(versionHome, '.config', 'opencode', 'agents');
|
|
1120
|
-
if (fs.existsSync(agentsDir)) {
|
|
1121
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1122
|
-
if (!file.endsWith('.md'))
|
|
1123
|
-
continue;
|
|
1124
|
-
const name = path.basename(file, '.md');
|
|
1125
|
-
if (!discovered.has(name))
|
|
1126
|
-
orphans.push(name);
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
else if (agent === 'antigravity') {
|
|
1131
|
-
const agentsDir = path.join(versionHome, '.gemini', 'config', 'agents');
|
|
1132
|
-
if (fs.existsSync(agentsDir)) {
|
|
1133
|
-
for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
|
|
1134
|
-
if (!entry.isDirectory())
|
|
1135
|
-
continue;
|
|
1136
|
-
if (!fs.existsSync(path.join(agentsDir, entry.name, 'agent.md')))
|
|
1137
|
-
continue;
|
|
1138
|
-
if (!discovered.has(entry.name))
|
|
1139
|
-
orphans.push(entry.name);
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1143
|
-
else if (agent === 'openclaw') {
|
|
1144
|
-
const openclawDir = path.join(versionHome, '.openclaw');
|
|
1145
|
-
if (fs.existsSync(openclawDir)) {
|
|
1146
|
-
for (const entry of fs.readdirSync(openclawDir, { withFileTypes: true })) {
|
|
1147
|
-
if (!entry.isDirectory())
|
|
1148
|
-
continue;
|
|
1149
|
-
if (!discovered.has(entry.name)) {
|
|
1150
|
-
orphans.push(entry.name);
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
else if (agent === 'kiro') {
|
|
1156
|
-
const agentsDir = path.join(versionHome, '.kiro', 'agents');
|
|
1157
|
-
if (fs.existsSync(agentsDir)) {
|
|
1158
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1159
|
-
if (!file.endsWith('.json'))
|
|
1160
|
-
continue;
|
|
1161
|
-
const name = path.basename(file, '.json');
|
|
1162
|
-
if (!discovered.has(name)) {
|
|
1163
|
-
orphans.push(name);
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
else if (agent === 'cursor') {
|
|
1169
|
-
const agentsDir = path.join(versionHome, '.cursor', 'agents');
|
|
1170
|
-
if (fs.existsSync(agentsDir)) {
|
|
1171
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1172
|
-
if (!file.endsWith('.md'))
|
|
1173
|
-
continue;
|
|
1174
|
-
const name = path.basename(file, '.md');
|
|
1175
|
-
if (!discovered.has(name))
|
|
1176
|
-
orphans.push(name);
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
else if (agent === 'forge') {
|
|
1181
|
-
const agentsDir = path.join(versionHome, '.forge', 'agents');
|
|
1182
|
-
if (fs.existsSync(agentsDir)) {
|
|
1183
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1184
|
-
if (!file.endsWith('.md'))
|
|
1185
|
-
continue;
|
|
1186
|
-
const name = path.basename(file, '.md');
|
|
1187
|
-
if (!discovered.has(name))
|
|
1188
|
-
orphans.push(name);
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
else if (agent === 'goose') {
|
|
1193
|
-
const agentsDir = path.join(versionHome, '.config', 'goose', 'agents');
|
|
1194
|
-
if (fs.existsSync(agentsDir)) {
|
|
1195
|
-
for (const file of fs.readdirSync(agentsDir)) {
|
|
1196
|
-
if (!file.endsWith('.yaml'))
|
|
1197
|
-
continue;
|
|
1198
|
-
const name = path.basename(file, '.yaml');
|
|
1199
|
-
if (!discovered.has(name))
|
|
1200
|
-
orphans.push(name);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
652
|
+
// Anything installed in the version home that isn't a discovered subagent is
|
|
653
|
+
// an orphan. Installed enumeration is registry-driven (per-agent layout).
|
|
654
|
+
const orphans = listInstalledSubagentNames(agent, versionHome).filter((name) => !discovered.has(name));
|
|
1204
655
|
return { agent, version, orphans: orphans.sort() };
|
|
1205
656
|
}
|
|
1206
657
|
/**
|
|
@@ -1229,89 +680,7 @@ export function removeSubagentFromVersion(agent, version, subagentName) {
|
|
|
1229
680
|
const versionHome = getVersionHomePath(agent, version);
|
|
1230
681
|
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1231
682
|
const trashDir = path.join(getTrashSubagentsDir(), agent, version, subagentName);
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
const targetPath = path.join(versionHome, agentsRoot, 'agents', `${subagentName}.md`);
|
|
1236
|
-
if (fs.existsSync(targetPath)) {
|
|
1237
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1238
|
-
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.md.${stamp}`));
|
|
1239
|
-
}
|
|
1240
|
-
}
|
|
1241
|
-
else if (agent === 'kimi') {
|
|
1242
|
-
const agentsDir = path.join(versionHome, '.kimi-code', 'agents');
|
|
1243
|
-
const yamlPath = path.join(agentsDir, `${subagentName}.yaml`);
|
|
1244
|
-
const promptPath = path.join(agentsDir, `${subagentName}.system.md`);
|
|
1245
|
-
if (fs.existsSync(yamlPath) || fs.existsSync(promptPath)) {
|
|
1246
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1247
|
-
if (fs.existsSync(yamlPath)) {
|
|
1248
|
-
fs.renameSync(yamlPath, path.join(trashDir, `${subagentName}.yaml.${stamp}`));
|
|
1249
|
-
}
|
|
1250
|
-
if (fs.existsSync(promptPath)) {
|
|
1251
|
-
fs.renameSync(promptPath, path.join(trashDir, `${subagentName}.system.md.${stamp}`));
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
else if (agent === 'opencode') {
|
|
1256
|
-
const targetPath = path.join(versionHome, '.config', 'opencode', 'agents', `${subagentName}.md`);
|
|
1257
|
-
if (fs.existsSync(targetPath)) {
|
|
1258
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1259
|
-
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.md.${stamp}`));
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
else if (agent === 'antigravity') {
|
|
1263
|
-
const targetDir = path.join(versionHome, '.gemini', 'config', 'agents', subagentName);
|
|
1264
|
-
if (fs.existsSync(targetDir)) {
|
|
1265
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1266
|
-
fs.renameSync(targetDir, path.join(trashDir, stamp));
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
else if (agent === 'copilot') {
|
|
1270
|
-
const targetPath = path.join(versionHome, '.copilot', 'agents', `${subagentName}.agent.md`);
|
|
1271
|
-
if (fs.existsSync(targetPath)) {
|
|
1272
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1273
|
-
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.agent.md.${stamp}`));
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
else if (agent === 'openclaw') {
|
|
1277
|
-
const targetDir = path.join(versionHome, '.openclaw', subagentName);
|
|
1278
|
-
if (fs.existsSync(targetDir)) {
|
|
1279
|
-
const trashDest = path.join(trashDir, stamp);
|
|
1280
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1281
|
-
fs.renameSync(targetDir, trashDest);
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
else if (agent === 'kiro') {
|
|
1285
|
-
const targetPath = path.join(versionHome, '.kiro', 'agents', `${subagentName}.json`);
|
|
1286
|
-
if (fs.existsSync(targetPath)) {
|
|
1287
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1288
|
-
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.json.${stamp}`));
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
else if (agent === 'cursor') {
|
|
1292
|
-
const targetPath = path.join(versionHome, '.cursor', 'agents', `${subagentName}.md`);
|
|
1293
|
-
if (fs.existsSync(targetPath)) {
|
|
1294
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1295
|
-
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.md.${stamp}`));
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1298
|
-
else if (agent === 'forge') {
|
|
1299
|
-
const targetPath = path.join(versionHome, '.forge', 'agents', `${subagentName}.md`);
|
|
1300
|
-
if (fs.existsSync(targetPath)) {
|
|
1301
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1302
|
-
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.md.${stamp}`));
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
else if (agent === 'goose') {
|
|
1306
|
-
const targetPath = path.join(versionHome, '.config', 'goose', 'agents', `${subagentName}.yaml`);
|
|
1307
|
-
if (fs.existsSync(targetPath)) {
|
|
1308
|
-
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
1309
|
-
fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.yaml.${stamp}`));
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
return { success: true };
|
|
1313
|
-
}
|
|
1314
|
-
catch (err) {
|
|
1315
|
-
return { success: false, error: err.message };
|
|
1316
|
-
}
|
|
683
|
+
// Registry-driven soft-delete: files land as `<basename>.<stamp>`, dirs as
|
|
684
|
+
// `<stamp>/`, matching the prior per-agent behavior for every layout.
|
|
685
|
+
return trashSubagentFromHome(agent, versionHome, subagentName, trashDir, stamp);
|
|
1317
686
|
}
|