@phnx-labs/agents-cli 1.20.58 → 1.20.60

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 (49) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.md +15 -7
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/exec.js +39 -2
  5. package/dist/commands/output.d.ts +19 -0
  6. package/dist/commands/output.js +333 -0
  7. package/dist/commands/secrets.js +6 -6
  8. package/dist/index.js +2 -1
  9. package/dist/lib/agents.js +19 -13
  10. package/dist/lib/hosts/dispatch.d.ts +36 -0
  11. package/dist/lib/hosts/dispatch.js +40 -2
  12. package/dist/lib/hosts/passthrough.js +1 -0
  13. package/dist/lib/mcp.js +1 -1
  14. package/dist/lib/output/git-output.d.ts +74 -0
  15. package/dist/lib/output/git-output.js +213 -0
  16. package/dist/lib/permissions.d.ts +26 -5
  17. package/dist/lib/permissions.js +212 -37
  18. package/dist/lib/plugins.d.ts +8 -0
  19. package/dist/lib/plugins.js +108 -0
  20. package/dist/lib/project-root.js +2 -1
  21. package/dist/lib/resources/mcp.js +1 -1
  22. package/dist/lib/resources/permissions.d.ts +1 -1
  23. package/dist/lib/resources/permissions.js +8 -2
  24. package/dist/lib/resources/skills.js +6 -1
  25. package/dist/lib/resources/types.d.ts +1 -1
  26. package/dist/lib/routines.d.ts +16 -0
  27. package/dist/lib/routines.js +46 -1
  28. package/dist/lib/secrets/remote.d.ts +7 -2
  29. package/dist/lib/secrets/remote.js +11 -10
  30. package/dist/lib/session/db.d.ts +3 -0
  31. package/dist/lib/session/db.js +20 -4
  32. package/dist/lib/session/discover.d.ts +2 -0
  33. package/dist/lib/session/discover.js +40 -4
  34. package/dist/lib/session/types.d.ts +2 -0
  35. package/dist/lib/shims.js +13 -3
  36. package/dist/lib/skills.js +14 -1
  37. package/dist/lib/staleness/detectors/permissions.js +50 -3
  38. package/dist/lib/staleness/detectors/subagents.js +31 -12
  39. package/dist/lib/staleness/detectors/workflows.js +33 -0
  40. package/dist/lib/staleness/writers/commands.js +3 -3
  41. package/dist/lib/staleness/writers/subagents.js +13 -5
  42. package/dist/lib/startup/command-registry.d.ts +1 -0
  43. package/dist/lib/startup/command-registry.js +2 -0
  44. package/dist/lib/subagents.d.ts +11 -1
  45. package/dist/lib/subagents.js +117 -26
  46. package/dist/lib/versions.js +12 -2
  47. package/dist/lib/workflows.d.ts +5 -3
  48. package/dist/lib/workflows.js +246 -9
  49. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Subagents detector. Claude: flat .md files under `<agentDir>/agents/`.
2
+ * Subagents detector. Claude/Gemini/Grok: flat .md files under `<agentDir>/agents/`.
3
3
  * Codex: flat .toml files under `<versionHome>/.codex/agents/`.
4
4
  * Droid: flat .md files under `<versionHome>/.factory/droids/`.
5
5
  * OpenClaw: subdirectories containing AGENTS.md under `<versionHome>/.openclaw/`.
@@ -29,6 +29,9 @@ function buildClaudeDetector() {
29
29
  function buildGrokDetector() {
30
30
  return buildFlatMdAgentsDetector('grok', '.grok');
31
31
  }
32
+ function buildGeminiDetector() {
33
+ return buildFlatMdAgentsDetector('gemini', '.gemini');
34
+ }
32
35
  function buildCodexDetector() {
33
36
  return {
34
37
  kind: 'subagents',
@@ -85,32 +88,32 @@ function buildOpenclawDetector() {
85
88
  },
86
89
  };
87
90
  }
88
- function buildKiroDetector() {
91
+ function buildKimiDetector() {
89
92
  return {
90
93
  kind: 'subagents',
91
- agent: 'kiro',
94
+ agent: 'kimi',
92
95
  list({ versionHome }) {
93
- const agentsDir = path.join(versionHome, '.kiro', 'agents');
96
+ const agentsDir = path.join(versionHome, '.kimi-code', 'agents');
94
97
  if (!fs.existsSync(agentsDir))
95
98
  return [];
99
+ // Parent is `_agents-cli.yaml` (underscore-prefixed reserved name).
96
100
  return fs.readdirSync(agentsDir)
97
- .filter(f => f.endsWith('.json'))
98
- .map(f => f.replace('.json', ''));
101
+ .filter(f => f.endsWith('.yaml') && !f.startsWith('_'))
102
+ .map(f => f.replace(/\.yaml$/, ''));
99
103
  },
100
104
  };
101
105
  }
102
- function buildKimiDetector() {
106
+ function buildKiroDetector() {
103
107
  return {
104
108
  kind: 'subagents',
105
- agent: 'kimi',
109
+ agent: 'kiro',
106
110
  list({ versionHome }) {
107
- const agentsDir = path.join(versionHome, '.kimi-code', 'agents');
111
+ const agentsDir = path.join(versionHome, '.kiro', 'agents');
108
112
  if (!fs.existsSync(agentsDir))
109
113
  return [];
110
- // Parent is `_agents-cli.yaml` (underscore-prefixed reserved name).
111
114
  return fs.readdirSync(agentsDir)
112
- .filter(f => f.endsWith('.yaml') && !f.startsWith('_'))
113
- .map(f => f.replace(/\.yaml$/, ''));
115
+ .filter(f => f.endsWith('.json'))
116
+ .map(f => f.replace(/\.json$/, ''));
114
117
  },
115
118
  };
116
119
  }
@@ -128,13 +131,29 @@ function buildOpenCodeDetector() {
128
131
  },
129
132
  };
130
133
  }
134
+ function buildAntigravityDetector() {
135
+ return {
136
+ kind: 'subagents',
137
+ agent: 'antigravity',
138
+ list({ versionHome }) {
139
+ const agentsDir = path.join(versionHome, '.gemini', 'config', 'agents');
140
+ if (!fs.existsSync(agentsDir))
141
+ return [];
142
+ return fs.readdirSync(agentsDir, { withFileTypes: true })
143
+ .filter(d => d.isDirectory() && fs.existsSync(path.join(agentsDir, d.name, 'agent.md')))
144
+ .map(d => d.name);
145
+ },
146
+ };
147
+ }
131
148
  const handlers = {
132
149
  claude: buildClaudeDetector,
133
150
  copilot: buildCopilotDetector,
151
+ gemini: buildGeminiDetector,
134
152
  grok: buildGrokDetector,
135
153
  codex: buildCodexDetector,
136
154
  kimi: buildKimiDetector,
137
155
  opencode: buildOpenCodeDetector,
156
+ antigravity: buildAntigravityDetector,
138
157
  droid: buildDroidDetector,
139
158
  openclaw: buildOpenclawDetector,
140
159
  kiro: buildKiroDetector,
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import * as fs from 'fs';
6
6
  import * as path from 'path';
7
+ import * as yaml from 'yaml';
7
8
  import { capableAgents } from '../../capabilities.js';
8
9
  import { lazyAgentMap } from '../writers/lazy-map.js';
9
10
  function buildWorkflowsDetector(agent) {
@@ -11,6 +12,38 @@ function buildWorkflowsDetector(agent) {
11
12
  kind: 'workflows',
12
13
  agent,
13
14
  list({ versionHome }) {
15
+ if (agent === 'kimi') {
16
+ const skillsDir = path.join(versionHome, '.kimi-code', 'skills');
17
+ if (!fs.existsSync(skillsDir))
18
+ return [];
19
+ return fs.readdirSync(skillsDir, { withFileTypes: true })
20
+ .filter(d => d.isDirectory() && fs.existsSync(path.join(skillsDir, d.name, 'SKILL.md')))
21
+ .filter(d => {
22
+ try {
23
+ const skill = fs.readFileSync(path.join(skillsDir, d.name, 'SKILL.md'), 'utf-8');
24
+ const lines = skill.split('\n');
25
+ if (lines[0] !== '---')
26
+ return false;
27
+ const endIndex = lines.slice(1).findIndex(l => l === '---');
28
+ if (endIndex < 0)
29
+ return false;
30
+ const parsed = yaml.parse(lines.slice(1, endIndex + 1).join('\n'));
31
+ return parsed?.type === 'flow' && parsed.agents_workflow === d.name;
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ })
37
+ .map(d => d.name);
38
+ }
39
+ if (agent === 'goose') {
40
+ const recipesDir = path.join(versionHome, '.config', 'goose', 'recipes');
41
+ if (!fs.existsSync(recipesDir))
42
+ return [];
43
+ return fs.readdirSync(recipesDir, { withFileTypes: true })
44
+ .filter(d => d.isFile() && d.name.endsWith('.yaml') && !d.name.startsWith('.'))
45
+ .map(d => d.name.slice(0, -'.yaml'.length));
46
+ }
14
47
  const workflowsDir = path.join(versionHome, 'workflows');
15
48
  if (!fs.existsSync(workflowsDir))
16
49
  return [];
@@ -36,10 +36,10 @@ function buildCommandsWriter(agent) {
36
36
  const agentDir = path.join(versionHome, agentConfigDirName(agent));
37
37
  const commandsAsSkills = shouldInstallCommandAsSkill(agent, version);
38
38
  const supportsCommands = supports(agent, 'commands', version).ok;
39
- // Writers fire only after supports() OR commands-as-skills says yes
40
- // both paths produce a usable result here.
39
+ // Version-gated agents (e.g. goose skills >= 1.25.0) are registered but
40
+ // may be called at a version too old for both paths — skip gracefully.
41
41
  if (!commandsAsSkills && !supportsCommands) {
42
- throw new Error(`commands writer reached for ${agent}@${version} with no path (cmd=false, asSkill=false)`);
42
+ return { synced: [] };
43
43
  }
44
44
  const skillRoots = trustedSkillRoots();
45
45
  const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
@@ -1,6 +1,7 @@
1
1
  /**
2
- * Subagents writer. Claude flattens each subagent into a single .md file
3
- * under `<agentDir>/agents/`. Codex writes TOML under `.codex/agents/`.
2
+ * Subagents writer. Claude/Gemini/Grok flatten each subagent into a single
3
+ * .md file under their native agents directory. Codex writes TOML under
4
+ * `.codex/agents/`.
4
5
  * Droid (Factory AI) flattens each into a custom droid .md under
5
6
  * `<versionHome>/.factory/droids/`. OpenClaw copies the full subagent
6
7
  * directory (with AGENT.md renamed to AGENTS.md) into
@@ -13,7 +14,7 @@
13
14
  import * as fs from 'fs';
14
15
  import * as path from 'path';
15
16
  import { capableAgents } from '../../capabilities.js';
16
- import { listInstalledSubagents, transformSubagentForClaude, transformSubagentForCodex, transformSubagentForCopilot, writeKimiSubagentFiles, buildKimiSubagentsParentYaml, KIMI_SUBAGENTS_PARENT_FILE, transformSubagentForOpenCode, transformSubagentForDroid, transformSubagentForKiro, syncSubagentToOpenclaw, parseSubagentFrontmatter, } from '../../subagents.js';
17
+ import { listInstalledSubagents, transformSubagentForClaude, transformSubagentForCodex, transformSubagentForCopilot, writeKimiSubagentFiles, buildKimiSubagentsParentYaml, KIMI_SUBAGENTS_PARENT_FILE, transformSubagentForOpenCode, transformSubagentForAntigravity, transformSubagentForDroid, transformSubagentForKiro, syncSubagentToOpenclaw, parseSubagentFrontmatter, } from '../../subagents.js';
17
18
  import { safeJoin } from '../../paths.js';
18
19
  import { lazyAgentMap } from './lazy-map.js';
19
20
  function buildSubagentsWriter(agent) {
@@ -29,8 +30,9 @@ function buildSubagentsWriter(agent) {
29
30
  if (!sub)
30
31
  continue;
31
32
  try {
32
- if (agent === 'claude' || agent === 'grok') {
33
- const agentsDir = path.join(versionHome, agent === 'grok' ? '.grok' : '.claude', 'agents');
33
+ if (agent === 'claude' || agent === 'gemini' || agent === 'grok') {
34
+ const agentsRoot = agent === 'grok' ? '.grok' : agent === 'gemini' ? '.gemini' : '.claude';
35
+ const agentsDir = path.join(versionHome, agentsRoot, 'agents');
34
36
  fs.mkdirSync(agentsDir, { recursive: true });
35
37
  fs.writeFileSync(safeJoin(agentsDir, `${sub.name}.md`), transformSubagentForClaude(sub.path));
36
38
  synced.push(sub.name);
@@ -51,6 +53,12 @@ function buildSubagentsWriter(agent) {
51
53
  fs.writeFileSync(safeJoin(agentsDir, `${sub.name}.md`), transformSubagentForOpenCode(sub.path));
52
54
  synced.push(sub.name);
53
55
  }
56
+ else if (agent === 'antigravity') {
57
+ const agentDir = safeJoin(path.join(versionHome, '.gemini', 'config', 'agents'), sub.name);
58
+ fs.mkdirSync(agentDir, { recursive: true });
59
+ fs.writeFileSync(safeJoin(agentDir, 'agent.md'), transformSubagentForAntigravity(sub.path));
60
+ synced.push(sub.name);
61
+ }
54
62
  else if (agent === 'droid') {
55
63
  const droidsDir = path.join(versionHome, '.factory', 'droids');
56
64
  fs.mkdirSync(droidsDir, { recursive: true });
@@ -65,6 +65,7 @@ export declare const loadDrive: ModuleLoader;
65
65
  export declare const loadFactory: ModuleLoader;
66
66
  export declare const loadUsage: ModuleLoader;
67
67
  export declare const loadCost: ModuleLoader;
68
+ export declare const loadOutput: ModuleLoader;
68
69
  export declare const loadBudget: ModuleLoader;
69
70
  export declare const loadAlias: ModuleLoader;
70
71
  export declare const loadPty: ModuleLoader;
@@ -43,6 +43,7 @@ export const loadDrive = async () => (await import('../../commands/drive.js')).r
43
43
  export const loadFactory = async () => (await import('../../commands/factory.js')).registerFactoryCommands;
44
44
  export const loadUsage = async () => (await import('../../commands/usage.js')).registerUsageCommand;
45
45
  export const loadCost = async () => (await import('../../commands/cost.js')).registerCostCommand;
46
+ export const loadOutput = async () => (await import('../../commands/output.js')).registerOutputCommand;
46
47
  export const loadBudget = async () => (await import('../../commands/budget.js')).registerBudgetCommand;
47
48
  export const loadAlias = async () => (await import('../../commands/alias.js')).registerAliasCommand;
48
49
  export const loadPty = async () => (await import('../../commands/pty.js')).registerPtyCommands;
@@ -137,6 +138,7 @@ export const COMMAND_LOADERS = {
137
138
  factory: [loadFactory],
138
139
  usage: [loadUsage],
139
140
  cost: [loadCost],
141
+ output: [loadOutput],
140
142
  budget: [loadBudget],
141
143
  alias: [loadAlias],
142
144
  pty: [loadPty],
@@ -68,6 +68,15 @@ export declare function transformSubagentForDroid(subagentDir: string): string;
68
68
  * See GitHub docs for custom agents.
69
69
  */
70
70
  export declare const transformSubagentForCopilot: typeof transformSubagentForDroid;
71
+ /**
72
+ * Transform a subagent into Antigravity's custom-agent markdown shape.
73
+ *
74
+ * Antigravity exposes custom agents as Markdown files with YAML frontmatter,
75
+ * close to Gemini CLI subagents. Keep portable frontmatter fields and flatten
76
+ * sibling markdown files into the prompt body like the other markdown-backed
77
+ * agents.
78
+ */
79
+ export declare function transformSubagentForAntigravity(subagentDir: string): string;
71
80
  /**
72
81
  * Transform a subagent into an OpenCode agent markdown file.
73
82
  *
@@ -157,8 +166,9 @@ export declare function removeSubagentFromAgent(subagentName: string, agent: Age
157
166
  export declare function subagentContentMatches(installedDir: string, sourceDir: string): boolean;
158
167
  /**
159
168
  * List subagents installed to a specific agent's home
160
- * Claude: scans ~/.claude/agents/{name}.md
169
+ * Claude/Gemini/Grok: scans ~/.{agent}/agents/{name}.md
161
170
  * Kimi: scans ~/.kimi-code/agents/{name}.yaml (+ sibling .system.md)
171
+ * Kiro: scans ~/.kiro/agents/{name}.json
162
172
  * OpenClaw: scans ~/.openclaw/{name}/AGENTS.md
163
173
  */
164
174
  export declare function listSubagentsForAgent(agentId: AgentId, home: string): InstalledSubagent[];
@@ -274,6 +274,39 @@ export function transformSubagentForDroid(subagentDir) {
274
274
  * See GitHub docs for custom agents.
275
275
  */
276
276
  export const transformSubagentForCopilot = transformSubagentForDroid;
277
+ /**
278
+ * Transform a subagent into Antigravity's custom-agent markdown shape.
279
+ *
280
+ * Antigravity exposes custom agents as Markdown files with YAML frontmatter,
281
+ * close to Gemini CLI subagents. Keep portable frontmatter fields and flatten
282
+ * sibling markdown files into the prompt body like the other markdown-backed
283
+ * agents.
284
+ */
285
+ export function transformSubagentForAntigravity(subagentDir) {
286
+ const agentMd = path.join(subagentDir, 'AGENT.md');
287
+ const frontmatter = parseSubagentFrontmatter(agentMd);
288
+ const body = getSubagentBody(agentMd);
289
+ if (!frontmatter) {
290
+ throw new Error(`Invalid AGENT.md in ${subagentDir}`);
291
+ }
292
+ const frontmatterYaml = yaml.stringify({
293
+ name: frontmatter.name,
294
+ description: frontmatter.description,
295
+ kind: 'local',
296
+ ...(frontmatter.model && { model: frontmatter.model }),
297
+ }).trim();
298
+ let result = `---\n${frontmatterYaml}\n---\n\n${body}`;
299
+ const files = fs.readdirSync(subagentDir)
300
+ .filter(f => f.endsWith('.md') && f !== 'AGENT.md')
301
+ .sort();
302
+ for (const file of files) {
303
+ const content = fs.readFileSync(path.join(subagentDir, file), 'utf-8').trim();
304
+ const sectionName = file.replace('.md', '');
305
+ const title = sectionName.charAt(0).toUpperCase() + sectionName.slice(1).toLowerCase();
306
+ result += `\n\n## ${title}\n\n${content}`;
307
+ }
308
+ return `${result.trim()}\n`;
309
+ }
277
310
  /**
278
311
  * Transform a subagent into an OpenCode agent markdown file.
279
312
  *
@@ -393,21 +426,10 @@ export function writeKimiSubagentFiles(agentsDir, subagentDir, name) {
393
426
  export function transformSubagentForCodex(subagentDir) {
394
427
  const agentMd = path.join(subagentDir, 'AGENT.md');
395
428
  const frontmatter = parseSubagentFrontmatter(agentMd);
396
- const body = getSubagentBody(agentMd);
397
429
  if (!frontmatter) {
398
430
  throw new Error(`Invalid AGENT.md in ${subagentDir}`);
399
431
  }
400
- // Append other .md files into the developer_instructions body.
401
- let instructions = body.trim();
402
- const files = fs.readdirSync(subagentDir)
403
- .filter(f => f.endsWith('.md') && f !== 'AGENT.md')
404
- .sort();
405
- for (const file of files) {
406
- const content = fs.readFileSync(path.join(subagentDir, file), 'utf-8').trim();
407
- const sectionName = file.replace('.md', '');
408
- const title = sectionName.charAt(0).toUpperCase() + sectionName.slice(1).toLowerCase();
409
- instructions += `\n\n## ${title}\n\n${content}`;
410
- }
432
+ const instructions = flattenSubagentInstructions(subagentDir);
411
433
  // Escape TOML multi-line string (""") content — only """ needs escaping.
412
434
  const safeInstructions = instructions.replace(/"""/g, '\\"""');
413
435
  const safeName = frontmatter.name.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
@@ -421,6 +443,19 @@ export function transformSubagentForCodex(subagentDir) {
421
443
  toml += `developer_instructions = """\n${safeInstructions}\n"""\n`;
422
444
  return toml;
423
445
  }
446
+ function flattenSubagentInstructions(subagentDir) {
447
+ let instructions = getSubagentBody(path.join(subagentDir, 'AGENT.md')).trim();
448
+ const files = fs.readdirSync(subagentDir)
449
+ .filter(f => f.endsWith('.md') && f !== 'AGENT.md')
450
+ .sort();
451
+ for (const file of files) {
452
+ const content = fs.readFileSync(path.join(subagentDir, file), 'utf-8').trim();
453
+ const sectionName = file.replace('.md', '');
454
+ const title = sectionName.charAt(0).toUpperCase() + sectionName.slice(1).toLowerCase();
455
+ instructions += `\n\n## ${title}\n\n${content}`;
456
+ }
457
+ return instructions;
458
+ }
424
459
  /**
425
460
  * Transform a subagent into a Kiro CLI custom-agent JSON file.
426
461
  *
@@ -487,10 +522,10 @@ export function syncSubagentToOpenclaw(subagentDir, targetDir) {
487
522
  * Install a subagent to a specific agent's home
488
523
  */
489
524
  export function installSubagentToAgent(subagentDir, subagentName, agent, agentHome) {
490
- if (agent === 'claude' || agent === 'grok') {
491
- // Claude / Grok: flatten to single .md under ~/.claude/agents or ~/.grok/agents
492
- // Grok discovers user agent defs from ~/.grok/agents/*.md (same shape as Claude).
493
- const agentsDir = path.join(agentHome, agent === 'grok' ? '.grok' : '.claude', 'agents');
525
+ if (agent === 'claude' || agent === 'gemini' || agent === 'grok') {
526
+ // Claude / Gemini / Grok: flatten to single .md under the native agents dir.
527
+ const agentsRoot = agent === 'grok' ? '.grok' : agent === 'gemini' ? '.gemini' : '.claude';
528
+ const agentsDir = path.join(agentHome, agentsRoot, 'agents');
494
529
  if (!fs.existsSync(agentsDir)) {
495
530
  fs.mkdirSync(agentsDir, { recursive: true });
496
531
  }
@@ -541,6 +576,19 @@ export function installSubagentToAgent(subagentDir, subagentName, agent, agentHo
541
576
  return { success: false, error: String(err) };
542
577
  }
543
578
  }
579
+ else if (agent === 'antigravity') {
580
+ // Antigravity: custom-agent markdown under ~/.gemini/config/agents/<name>/agent.md.
581
+ const agentDir = safeJoin(path.join(agentHome, '.gemini', 'config', 'agents'), subagentName);
582
+ if (!fs.existsSync(agentDir))
583
+ fs.mkdirSync(agentDir, { recursive: true });
584
+ try {
585
+ fs.writeFileSync(safeJoin(agentDir, 'agent.md'), transformSubagentForAntigravity(subagentDir));
586
+ return { success: true };
587
+ }
588
+ catch (err) {
589
+ return { success: false, error: String(err) };
590
+ }
591
+ }
544
592
  else if (agent === 'openclaw') {
545
593
  // OpenClaw: copy full directory
546
594
  const targetDir = safeJoin(path.join(agentHome, '.openclaw'), subagentName);
@@ -571,8 +619,8 @@ export function installSubagentToAgent(subagentDir, subagentName, agent, agentHo
571
619
  */
572
620
  export function removeSubagentFromAgent(subagentName, agent, agentHome) {
573
621
  try {
574
- if (agent === 'claude' || agent === 'grok') {
575
- const agentsRoot = agent === 'grok' ? '.grok' : '.claude';
622
+ if (agent === 'claude' || agent === 'gemini' || agent === 'grok') {
623
+ const agentsRoot = agent === 'grok' ? '.grok' : agent === 'gemini' ? '.gemini' : '.claude';
576
624
  const targetPath = safeJoin(path.join(agentHome, agentsRoot, 'agents'), `${subagentName}.md`);
577
625
  if (fs.existsSync(targetPath)) {
578
626
  fs.unlinkSync(targetPath);
@@ -602,6 +650,12 @@ export function removeSubagentFromAgent(subagentName, agent, agentHome) {
602
650
  fs.unlinkSync(targetPath);
603
651
  return { success: true };
604
652
  }
653
+ else if (agent === 'antigravity') {
654
+ const targetDir = safeJoin(path.join(agentHome, '.gemini', 'config', 'agents'), subagentName);
655
+ if (fs.existsSync(targetDir))
656
+ fs.rmSync(targetDir, { recursive: true, force: true });
657
+ return { success: true };
658
+ }
605
659
  else if (agent === 'openclaw') {
606
660
  const targetDir = safeJoin(path.join(agentHome, '.openclaw'), subagentName);
607
661
  if (fs.existsSync(targetDir)) {
@@ -653,15 +707,17 @@ export function subagentContentMatches(installedDir, sourceDir) {
653
707
  // source of truth.
654
708
  /**
655
709
  * List subagents installed to a specific agent's home
656
- * Claude: scans ~/.claude/agents/{name}.md
710
+ * Claude/Gemini/Grok: scans ~/.{agent}/agents/{name}.md
657
711
  * Kimi: scans ~/.kimi-code/agents/{name}.yaml (+ sibling .system.md)
712
+ * Kiro: scans ~/.kiro/agents/{name}.json
658
713
  * OpenClaw: scans ~/.openclaw/{name}/AGENTS.md
659
714
  */
660
715
  export function listSubagentsForAgent(agentId, home) {
661
716
  const subagents = [];
662
- if (agentId === 'claude' || agentId === 'grok') {
663
- // Claude / Grok: flat .md files in agents/
664
- const agentsDir = path.join(home, agentId === 'grok' ? '.grok' : '.claude', 'agents');
717
+ if (agentId === 'claude' || agentId === 'gemini' || agentId === 'grok') {
718
+ // Claude / Gemini / Grok: flat .md files in agents/
719
+ const agentsRoot = agentId === 'grok' ? '.grok' : agentId === 'gemini' ? '.gemini' : '.claude';
720
+ const agentsDir = path.join(home, agentsRoot, 'agents');
665
721
  if (!fs.existsSync(agentsDir))
666
722
  return subagents;
667
723
  for (const file of fs.readdirSync(agentsDir)) {
@@ -727,6 +783,20 @@ export function listSubagentsForAgent(agentId, home) {
727
783
  subagents.push({ name, path: filePath, files: [file], frontmatter });
728
784
  }
729
785
  }
786
+ else if (agentId === 'antigravity') {
787
+ const agentsDir = path.join(home, '.gemini', 'config', 'agents');
788
+ if (!fs.existsSync(agentsDir))
789
+ return subagents;
790
+ for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
791
+ if (!entry.isDirectory())
792
+ continue;
793
+ const filePath = path.join(agentsDir, entry.name, 'agent.md');
794
+ if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile())
795
+ continue;
796
+ const frontmatter = parseSubagentFrontmatter(filePath) ?? { name: entry.name, description: '' };
797
+ subagents.push({ name: entry.name, path: filePath, files: ['agent.md'], frontmatter });
798
+ }
799
+ }
730
800
  else if (agentId === 'copilot') {
731
801
  // Copilot: flat `<name>.agent.md` files under ~/.copilot/agents/
732
802
  const agentsDir = path.join(home, '.copilot', 'agents');
@@ -837,8 +907,9 @@ export function diffVersionSubagents(agent, version) {
837
907
  }
838
908
  }
839
909
  // Check what's installed
840
- if (agent === 'claude' || agent === 'grok') {
841
- const agentsDir = path.join(versionHome, agent === 'grok' ? '.grok' : '.claude', 'agents');
910
+ if (agent === 'claude' || agent === 'gemini' || agent === 'grok') {
911
+ const agentsRoot = agent === 'grok' ? '.grok' : agent === 'gemini' ? '.gemini' : '.claude';
912
+ const agentsDir = path.join(versionHome, agentsRoot, 'agents');
842
913
  if (fs.existsSync(agentsDir)) {
843
914
  for (const file of fs.readdirSync(agentsDir)) {
844
915
  if (!file.endsWith('.md'))
@@ -875,6 +946,19 @@ export function diffVersionSubagents(agent, version) {
875
946
  }
876
947
  }
877
948
  }
949
+ else if (agent === 'antigravity') {
950
+ const agentsDir = path.join(versionHome, '.gemini', 'config', 'agents');
951
+ if (fs.existsSync(agentsDir)) {
952
+ for (const entry of fs.readdirSync(agentsDir, { withFileTypes: true })) {
953
+ if (!entry.isDirectory())
954
+ continue;
955
+ if (!fs.existsSync(path.join(agentsDir, entry.name, 'agent.md')))
956
+ continue;
957
+ if (!discovered.has(entry.name))
958
+ orphans.push(entry.name);
959
+ }
960
+ }
961
+ }
878
962
  else if (agent === 'openclaw') {
879
963
  const openclawDir = path.join(versionHome, '.openclaw');
880
964
  if (fs.existsSync(openclawDir)) {
@@ -929,8 +1013,8 @@ export function removeSubagentFromVersion(agent, version, subagentName) {
929
1013
  const stamp = new Date().toISOString().replace(/[:.]/g, '-');
930
1014
  const trashDir = path.join(getTrashSubagentsDir(), agent, version, subagentName);
931
1015
  try {
932
- if (agent === 'claude' || agent === 'grok') {
933
- const agentsRoot = agent === 'grok' ? '.grok' : '.claude';
1016
+ if (agent === 'claude' || agent === 'gemini' || agent === 'grok') {
1017
+ const agentsRoot = agent === 'grok' ? '.grok' : agent === 'gemini' ? '.gemini' : '.claude';
934
1018
  const targetPath = path.join(versionHome, agentsRoot, 'agents', `${subagentName}.md`);
935
1019
  if (fs.existsSync(targetPath)) {
936
1020
  fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
@@ -958,6 +1042,13 @@ export function removeSubagentFromVersion(agent, version, subagentName) {
958
1042
  fs.renameSync(targetPath, path.join(trashDir, `${subagentName}.md.${stamp}`));
959
1043
  }
960
1044
  }
1045
+ else if (agent === 'antigravity') {
1046
+ const targetDir = path.join(versionHome, '.gemini', 'config', 'agents', subagentName);
1047
+ if (fs.existsSync(targetDir)) {
1048
+ fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
1049
+ fs.renameSync(targetDir, path.join(trashDir, stamp));
1050
+ }
1051
+ }
961
1052
  else if (agent === 'copilot') {
962
1053
  const targetPath = path.join(versionHome, '.copilot', 'agents', `${subagentName}.agent.md`);
963
1054
  if (fs.existsSync(targetPath)) {
@@ -2462,9 +2462,14 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
2462
2462
  // reads only user + system layers (project excluded for the same defense
2463
2463
  // as commands/skills/hooks).
2464
2464
  const subagentsWriter = getWriter('subagents', agent);
2465
- const subagentsToSync = selection
2465
+ const subagentsGate = supports(agent, 'subagents', version);
2466
+ const subagentsRequested = selection
2466
2467
  ? resolveSelection(selection.subagents, available.subagents)
2467
2468
  : (subagentsWriter ? available.subagents : []);
2469
+ const subagentsToSync = subagentsGate.ok ? subagentsRequested : [];
2470
+ if (subagentsRequested.length > 0 && !subagentsGate.ok) {
2471
+ console.warn(explainSkip(agent, 'subagents', subagentsGate, version) + ' -- skipped');
2472
+ }
2468
2473
  if (subagentsToSync.length > 0 && subagentsWriter) {
2469
2474
  const r = subagentsWriter.write({ version, versionHome, selection: subagentsToSync, cwd });
2470
2475
  result.subagents.push(...r.synced);
@@ -2499,9 +2504,14 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
2499
2504
  }
2500
2505
  // Sync workflows — dispatch through WRITERS.workflows.
2501
2506
  const workflowsWriter = getWriter('workflows', agent);
2502
- const workflowsToSync = selection
2507
+ const workflowsGate = supports(agent, 'workflows', version);
2508
+ const workflowsRequested = selection
2503
2509
  ? resolveSelection(selection.workflows, available.workflows)
2504
2510
  : (workflowsWriter ? available.workflows : []);
2511
+ const workflowsToSync = workflowsGate.ok ? workflowsRequested : [];
2512
+ if (workflowsRequested.length > 0 && !workflowsGate.ok) {
2513
+ console.warn(explainSkip(agent, 'workflows', workflowsGate, version) + ' -- skipped');
2514
+ }
2505
2515
  if (workflowsToSync.length > 0 && workflowsWriter) {
2506
2516
  const r = workflowsWriter.write({ version, versionHome, selection: workflowsToSync, cwd });
2507
2517
  result.workflows.push(...r.synced);
@@ -263,6 +263,8 @@ export declare function resolveAllowedSubagents(available: string[], allowedAgen
263
263
  export declare function pruneStaleWorkflowSubagents(sharedAgentsDir: string, workflowSubagentFiles: string[], allowedStems: string[]): string[];
264
264
  /** Count subagent .md files in a workflow's subagents/ directory. */
265
265
  export declare function countWorkflowSubagents(workflowDir: string): number;
266
+ /** Convert a canonical agents-cli workflow bundle into a Kimi flow skill. */
267
+ export declare function transformWorkflowForKimi(workflowPath: string, name: string): string;
266
268
  /**
267
269
  * Resolve an `agents run <workflow>` reference.
268
270
  *
@@ -291,10 +293,10 @@ export declare function removeWorkflow(name: string): {
291
293
  success: boolean;
292
294
  error?: string;
293
295
  };
294
- /** List workflow names synced into a specific agent version home (at {versionHome}/workflows/). */
295
- export declare function listWorkflowsForAgent(_agent: AgentId, versionHome: string): string[];
296
+ /** List workflow names synced into a specific agent version home. */
297
+ export declare function listWorkflowsForAgent(agent: AgentId, versionHome: string): string[];
296
298
  /** Copy a workflow directory into a version home at {versionHome}/workflows/<name>/. */
297
- export declare function syncWorkflowToVersion(workflowPath: string, name: string, _agent: AgentId, versionHome: string): {
299
+ export declare function syncWorkflowToVersion(workflowPath: string, name: string, agent: AgentId, versionHome: string): {
298
300
  success: boolean;
299
301
  error?: string;
300
302
  };