@phnx-labs/agents-cli 1.20.52 → 1.20.53

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 (107) hide show
  1. package/CHANGELOG.md +57 -2
  2. package/README.md +12 -0
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/cloud.d.ts +3 -0
  5. package/dist/commands/cloud.js +2 -1
  6. package/dist/commands/exec.js +65 -1
  7. package/dist/commands/feed.d.ts +29 -0
  8. package/dist/commands/feed.js +237 -32
  9. package/dist/commands/memory.d.ts +9 -0
  10. package/dist/commands/memory.js +164 -0
  11. package/dist/commands/message.d.ts +11 -6
  12. package/dist/commands/message.js +140 -5
  13. package/dist/commands/routines.js +12 -0
  14. package/dist/commands/secrets-migrate.d.ts +2 -1
  15. package/dist/commands/secrets-migrate.js +88 -13
  16. package/dist/commands/secrets.js +4 -1
  17. package/dist/commands/sessions.js +10 -1
  18. package/dist/commands/worktree.js +4 -2
  19. package/dist/index.js +16 -21
  20. package/dist/lib/agents.js +249 -17
  21. package/dist/lib/answer-router.d.ts +75 -0
  22. package/dist/lib/answer-router.js +149 -0
  23. package/dist/lib/ask-classifier.d.ts +71 -0
  24. package/dist/lib/ask-classifier.js +197 -0
  25. package/dist/lib/cloud/antigravity.d.ts +0 -2
  26. package/dist/lib/cloud/antigravity.js +2 -17
  27. package/dist/lib/cloud/codex.js +3 -18
  28. package/dist/lib/cloud/rush.js +3 -15
  29. package/dist/lib/cloud/stream.js +2 -0
  30. package/dist/lib/cloud/types.d.ts +21 -0
  31. package/dist/lib/cloud/types.js +81 -0
  32. package/dist/lib/crabbox/cli.d.ts +1 -1
  33. package/dist/lib/crabbox/cli.js +12 -2
  34. package/dist/lib/crabbox/lease.d.ts +13 -0
  35. package/dist/lib/crabbox/lease.js +11 -2
  36. package/dist/lib/crabbox/progress.d.ts +62 -0
  37. package/dist/lib/crabbox/progress.js +129 -0
  38. package/dist/lib/events.js +4 -1
  39. package/dist/lib/exec.js +19 -1
  40. package/dist/lib/feed-outcome.d.ts +101 -0
  41. package/dist/lib/feed-outcome.js +244 -0
  42. package/dist/lib/feed-policy.d.ts +30 -0
  43. package/dist/lib/feed-policy.js +133 -0
  44. package/dist/lib/feed.d.ts +127 -3
  45. package/dist/lib/feed.js +416 -40
  46. package/dist/lib/git.d.ts +17 -1
  47. package/dist/lib/git.js +20 -1
  48. package/dist/lib/hooks.js +522 -12
  49. package/dist/lib/hosts/passthrough.d.ts +3 -3
  50. package/dist/lib/hosts/passthrough.js +3 -4
  51. package/dist/lib/mailbox-gc.d.ts +22 -0
  52. package/dist/lib/mailbox-gc.js +161 -0
  53. package/dist/lib/mailbox.d.ts +26 -2
  54. package/dist/lib/mailbox.js +80 -5
  55. package/dist/lib/mcp.js +82 -0
  56. package/dist/lib/memory.d.ts +55 -0
  57. package/dist/lib/memory.js +274 -0
  58. package/dist/lib/notify.d.ts +16 -0
  59. package/dist/lib/notify.js +61 -0
  60. package/dist/lib/operator.d.ts +26 -0
  61. package/dist/lib/operator.js +107 -0
  62. package/dist/lib/plugins.d.ts +35 -0
  63. package/dist/lib/plugins.js +217 -0
  64. package/dist/lib/remote-agents-json.d.ts +14 -0
  65. package/dist/lib/remote-agents-json.js +94 -0
  66. package/dist/lib/resources/mcp.js +44 -0
  67. package/dist/lib/resources/memory.d.ts +15 -0
  68. package/dist/lib/resources/memory.js +46 -0
  69. package/dist/lib/resources/types.d.ts +2 -2
  70. package/dist/lib/runner.d.ts +43 -0
  71. package/dist/lib/runner.js +323 -74
  72. package/dist/lib/sandbox.js +6 -0
  73. package/dist/lib/secrets/bundles.js +38 -13
  74. package/dist/lib/secrets/icloud-import.d.ts +12 -3
  75. package/dist/lib/secrets/icloud-import.js +37 -7
  76. package/dist/lib/secrets/index.d.ts +106 -2
  77. package/dist/lib/secrets/index.js +603 -28
  78. package/dist/lib/session/active.d.ts +18 -0
  79. package/dist/lib/session/active.js +47 -17
  80. package/dist/lib/session/db.d.ts +9 -1
  81. package/dist/lib/session/db.js +18 -3
  82. package/dist/lib/session/discover.d.ts +13 -0
  83. package/dist/lib/session/discover.js +31 -0
  84. package/dist/lib/session/parse.d.ts +8 -0
  85. package/dist/lib/session/parse.js +42 -21
  86. package/dist/lib/session/remote-active.js +8 -89
  87. package/dist/lib/session/state.d.ts +11 -0
  88. package/dist/lib/session/state.js +37 -0
  89. package/dist/lib/session/tail.d.ts +23 -4
  90. package/dist/lib/session/tail.js +34 -16
  91. package/dist/lib/session/throughput.d.ts +30 -0
  92. package/dist/lib/session/throughput.js +86 -0
  93. package/dist/lib/shim-heal.d.ts +12 -3
  94. package/dist/lib/shim-heal.js +12 -6
  95. package/dist/lib/staleness/detectors/subagents.js +57 -3
  96. package/dist/lib/staleness/writers/hooks.js +7 -3
  97. package/dist/lib/staleness/writers/subagents.js +37 -6
  98. package/dist/lib/startup/command-registry.d.ts +1 -0
  99. package/dist/lib/startup/command-registry.js +2 -0
  100. package/dist/lib/subagents.d.ts +52 -0
  101. package/dist/lib/subagents.js +315 -12
  102. package/dist/lib/teams/worktree.d.ts +8 -0
  103. package/dist/lib/teams/worktree.js +8 -0
  104. package/dist/lib/types.d.ts +10 -2
  105. package/dist/lib/versions.js +38 -48
  106. package/package.json +4 -3
  107. package/scripts/postinstall.js +61 -1
@@ -15,6 +15,7 @@ import * as fs from 'fs';
15
15
  import * as path from 'path';
16
16
  import * as os from 'os';
17
17
  import * as TOML from 'smol-toml';
18
+ import * as yaml from 'yaml';
18
19
  import chalk from 'chalk';
19
20
  import { needsWindowsShell } from './platform/index.js';
20
21
  import { latestFileMtimeMs } from './fs-walk.js';
@@ -213,7 +214,7 @@ export const AGENTS = {
213
214
  // Claude Code has no headless Anthropic-hosted dispatch CLI (only
214
215
  // --remote-control, which bridges a *local* session). Its cloud is Rush.
215
216
  cloudProvider: 'rush',
216
- capabilities: { hooks: true, mcp: true, mcpHttp: true, mcpHeaders: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'CLAUDE.md' }, workflows: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true },
217
+ capabilities: { hooks: true, mcp: true, mcpHttp: true, mcpHeaders: true, allowlist: true, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'CLAUDE.md' }, workflows: true, memory: true, modes: ['plan', 'edit', 'auto', 'skip'], rulesImports: true },
217
218
  },
218
219
  // codex hooks: gated to >= 0.116.0 (introduced [features] codex_hooks flag).
219
220
  codex: {
@@ -233,7 +234,9 @@ export const AGENTS = {
233
234
  variableSyntax: '$ARGUMENTS',
234
235
  supportsHooks: true,
235
236
  cloudProvider: 'codex',
236
- capabilities: { hooks: { since: '0.116.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: false, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'skip'] },
237
+ // Subagents: multi-agent plumbing since 0.117.0; custom agents as
238
+ // ~/.codex/agents/*.toml (name, description, developer_instructions).
239
+ capabilities: { hooks: { since: '0.116.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: false, skills: true, commands: { until: '0.117.0' }, plugins: { since: '0.128.0' }, subagents: { since: '0.117.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: true, modes: ['plan', 'edit', 'skip'] },
237
240
  },
238
241
  gemini: {
239
242
  id: 'gemini',
@@ -262,7 +265,7 @@ export const AGENTS = {
262
265
  url: 'https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/',
263
266
  },
264
267
  // gemini hooks: shipped in v0.26.0 (Jan 2026); older binaries silently ignore the `hooks` key.
265
- capabilities: { hooks: { since: '0.26.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'GEMINI.md' }, workflows: false, modes: ['plan', 'edit', 'skip'], rulesImports: true },
268
+ capabilities: { hooks: { since: '0.26.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'GEMINI.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'skip'], rulesImports: true },
266
269
  },
267
270
  cursor: {
268
271
  id: 'cursor',
@@ -275,12 +278,19 @@ export const AGENTS = {
275
278
  commandsDir: path.join(HOME, '.cursor', 'commands'),
276
279
  commandsSubdir: 'commands',
277
280
  skillsDir: path.join(HOME, '.cursor', 'skills'),
281
+ // Hooks: ~/.cursor/hooks.json (`{ "version": 1, "hooks": { event: [{ command }] } }`).
282
+ // CLI hooks since 2026-01-16. See registerHooksForCursor — only CLI-fired events.
278
283
  hooksDir: 'hooks',
284
+ // Plugins: `.cursor-plugin/plugin.json` (re-enabled in CLI 2026-05). Mirror the
285
+ // Claude marketplace layout into ~/.cursor/plugins/ and copy the manifest into
286
+ // pluginManifestDir so Cursor's native loader sees it (same pattern as droid/
287
+ // codex).
288
+ pluginManifestDir: '.cursor-plugin',
279
289
  instructionsFile: '.cursorrules',
280
290
  format: 'markdown',
281
291
  variableSyntax: '$ARGUMENTS',
282
- supportsHooks: false,
283
- capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: '.cursorrules' }, workflows: false, modes: ['edit', 'skip'] },
292
+ supportsHooks: true,
293
+ capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: true, subagents: false, rules: { file: '.cursorrules' }, workflows: false, memory: false, modes: ['edit', 'skip'] },
284
294
  },
285
295
  opencode: {
286
296
  id: 'opencode',
@@ -292,12 +302,15 @@ export const AGENTS = {
292
302
  commandsDir: path.join(HOME, '.opencode', 'commands'),
293
303
  commandsSubdir: 'commands',
294
304
  skillsDir: path.join(HOME, '.opencode', 'skills'),
305
+ // Plugins: TS/JS modules auto-loaded from ~/.config/opencode/plugins/ (global)
306
+ // and .opencode/plugins/ (project). Not Claude marketplace format — see
307
+ // installOpenCodePlugin in plugins.ts. No native shell hooks (plugins only).
295
308
  hooksDir: 'hooks',
296
309
  instructionsFile: 'AGENTS.md',
297
310
  format: 'markdown',
298
311
  variableSyntax: '$ARGUMENTS',
299
312
  supportsHooks: false,
300
- capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit'] },
313
+ capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '1.1.1' }, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'] }, // subagents: ~/.config/opencode/agents/*.md
301
314
  },
302
315
  openclaw: {
303
316
  id: 'openclaw',
@@ -315,7 +328,7 @@ export const AGENTS = {
315
328
  format: 'markdown',
316
329
  variableSyntax: '{{ARGUMENTS}}',
317
330
  supportsHooks: true,
318
- capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: false, plugins: true, subagents: true, rules: { file: 'workspace/AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'skip'] },
331
+ capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: false, plugins: true, subagents: true, rules: { file: 'workspace/AGENTS.md' }, workflows: false, memory: true, modes: ['plan', 'edit', 'skip'] },
319
332
  },
320
333
  copilot: {
321
334
  id: 'copilot',
@@ -327,6 +340,9 @@ export const AGENTS = {
327
340
  commandsDir: path.join(HOME, '.copilot', 'commands'),
328
341
  commandsSubdir: 'commands',
329
342
  skillsDir: path.join(HOME, '.copilot', 'skills'),
343
+ // Hooks: user-level `~/.copilot/hooks/*.json` (GA @github/copilot, every
344
+ // 1.x). Schema `{ "version": 1, "hooks": { event: [...] } }` with camelCase
345
+ // event names (sessionStart, preToolUse, …). See registerHooksForCopilot.
330
346
  hooksDir: 'hooks',
331
347
  // Copilot reads a plugin's manifest from the plugin ROOT (plugin.json),
332
348
  // not `.claude-plugin/plugin.json`. Mirror it there. Verified against the
@@ -336,8 +352,8 @@ export const AGENTS = {
336
352
  instructionsFile: 'AGENTS.md',
337
353
  format: 'markdown',
338
354
  variableSyntax: '$ARGUMENTS',
339
- supportsHooks: false,
340
- capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit', 'auto', 'skip'] },
355
+ supportsHooks: true,
356
+ capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'auto', 'skip'] },
341
357
  },
342
358
  amp: {
343
359
  id: 'amp',
@@ -354,7 +370,7 @@ export const AGENTS = {
354
370
  format: 'markdown',
355
371
  variableSyntax: '$ARGUMENTS',
356
372
  supportsHooks: false,
357
- capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['plan', 'edit'] },
373
+ capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'] },
358
374
  },
359
375
  kiro: {
360
376
  id: 'kiro',
@@ -367,12 +383,17 @@ export const AGENTS = {
367
383
  commandsDir: path.join(HOME, '.kiro', 'commands'),
368
384
  commandsSubdir: 'commands',
369
385
  skillsDir: path.join(HOME, '.kiro', 'skills'),
386
+ // Hooks: v3 standalone files under ~/.kiro/hooks/*.json
387
+ // (`{ "version": "v1", "hooks": [...] }`). Fixed PreToolUse/PostToolUse
388
+ // firing in kiro-cli 0.10; fully stable by 2.6.1. Launch always passes
389
+ // --v3 (see AGENT_COMMANDS.kiro) so the standalone files actually load.
390
+ // See registerHooksForKiro.
370
391
  hooksDir: 'hooks',
371
392
  instructionsFile: 'AGENTS.md',
372
393
  format: 'markdown',
373
394
  variableSyntax: '$ARGUMENTS',
374
- supportsHooks: false,
375
- capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit'] },
395
+ supportsHooks: true,
396
+ capabilities: { hooks: { since: '0.10.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['edit'] },
376
397
  },
377
398
  goose: {
378
399
  id: 'goose',
@@ -385,12 +406,17 @@ export const AGENTS = {
385
406
  commandsDir: path.join(HOME, '.config', 'goose', 'commands'),
386
407
  commandsSubdir: 'commands',
387
408
  skillsDir: path.join(HOME, '.config', 'goose', 'skills'),
409
+ // Hooks: Open Plugins format — auto-discovered from
410
+ // ~/.agents/plugins/<name>/hooks/hooks.json (shipped block-goose-cli
411
+ // ≥ 1.34.0). See registerHooksForGoose.
388
412
  hooksDir: 'hooks',
389
413
  instructionsFile: 'AGENTS.md',
390
414
  format: 'markdown',
391
415
  variableSyntax: '$ARGUMENTS',
392
- supportsHooks: false,
393
- capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: false, commands: false, plugins: false, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit'] },
416
+ supportsHooks: true,
417
+ // Plugins: Open Plugins under ~/.agents/plugins/<name>/ (same layout as
418
+ // agents-cli source). Version isolation copies into versionHome/.agents/plugins/.
419
+ capabilities: { hooks: { since: '1.34.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: false, commands: false, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['edit'] },
394
420
  },
395
421
  // Google Antigravity CLI (`agy`) — official replacement for Gemini CLI as of IO 2026.
396
422
  // configDir nests inside `~/.gemini/` since agy shares the parent dir with the Gemini
@@ -419,7 +445,7 @@ export const AGENTS = {
419
445
  variableSyntax: '{{args}}',
420
446
  supportsHooks: true,
421
447
  cloudProvider: 'antigravity',
422
- capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, modes: ['edit', 'skip'], rulesImports: false },
448
+ capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['edit', 'skip'], rulesImports: false },
423
449
  },
424
450
  // xAI Grok Build CLI (`grok`) — early beta, SuperGrok Heavy. Auth via OAuth on
425
451
  // first launch, or XAI_API_KEY env var for headless. MCP servers configured inline
@@ -452,9 +478,10 @@ export const AGENTS = {
452
478
  skills: true,
453
479
  commands: false, // covered by skills
454
480
  plugins: true,
455
- subagents: false,
481
+ subagents: true, // ~/.grok/agents/*.md (Claude-compatible agent defs)
456
482
  rules: { file: 'AGENTS.md' },
457
483
  workflows: false,
484
+ memory: true,
458
485
  modes: ['plan', 'edit', 'skip'],
459
486
  rulesImports: true,
460
487
  },
@@ -490,9 +517,10 @@ export const AGENTS = {
490
517
  skills: true,
491
518
  commands: false,
492
519
  plugins: true,
493
- subagents: false,
520
+ subagents: true, // YAML agent files under ~/.kimi-code/agents/ (see transformSubagentForKimi)
494
521
  rules: { file: 'AGENTS.md' },
495
522
  workflows: false,
523
+ memory: false,
496
524
  modes: ['plan', 'edit', 'auto', 'skip'],
497
525
  rulesImports: false,
498
526
  },
@@ -554,10 +582,82 @@ export const AGENTS = {
554
582
  subagents: true,
555
583
  rules: { file: 'AGENTS.md' },
556
584
  workflows: false,
585
+ memory: false,
557
586
  modes: ['plan', 'edit', 'auto', 'skip'],
558
587
  rulesImports: false,
559
588
  },
560
589
  },
590
+ // Nous Hermes Agent. Config lives under ~/.hermes/config.yaml; MCP servers
591
+ // are YAML `mcp_servers`, skills are local SKILL.md directories, and durable
592
+ // memory is file-backed.
593
+ hermes: {
594
+ id: 'hermes',
595
+ name: 'Hermes',
596
+ color: 'cyanBright',
597
+ cliCommand: 'hermes',
598
+ npmPackage: '',
599
+ installScript: 'curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash',
600
+ configDir: path.join(HOME, '.hermes'),
601
+ commandsDir: '',
602
+ commandsSubdir: '',
603
+ skillsDir: path.join(HOME, '.hermes', 'skills'),
604
+ hooksDir: 'hooks',
605
+ instructionsFile: 'MEMORY.md',
606
+ format: 'markdown',
607
+ variableSyntax: '$ARGUMENTS',
608
+ supportsHooks: false,
609
+ capabilities: {
610
+ hooks: false,
611
+ mcp: true,
612
+ mcpHttp: true,
613
+ mcpHeaders: false,
614
+ allowlist: false,
615
+ skills: true,
616
+ commands: false,
617
+ plugins: false,
618
+ subagents: false,
619
+ rules: { file: 'MEMORY.md' },
620
+ workflows: false,
621
+ memory: true,
622
+ modes: ['edit'],
623
+ rulesImports: false,
624
+ },
625
+ },
626
+ // ForgeCode (`forge`) from Tailcall. It reads AGENTS.md project rules,
627
+ // SKILL.md directories, and MCP servers from `.mcp.json` files.
628
+ forge: {
629
+ id: 'forge',
630
+ name: 'ForgeCode',
631
+ color: 'greenBright',
632
+ cliCommand: 'forge',
633
+ npmPackage: '',
634
+ installScript: 'curl -fsSL https://forgecode.dev/cli | sh',
635
+ configDir: path.join(HOME, '.forge'),
636
+ commandsDir: '',
637
+ commandsSubdir: '',
638
+ skillsDir: path.join(HOME, '.forge', 'skills'),
639
+ hooksDir: 'hooks',
640
+ instructionsFile: 'AGENTS.md',
641
+ format: 'markdown',
642
+ variableSyntax: '$ARGUMENTS',
643
+ supportsHooks: false,
644
+ capabilities: {
645
+ hooks: false,
646
+ mcp: true,
647
+ mcpHttp: true,
648
+ mcpHeaders: false,
649
+ allowlist: false,
650
+ skills: true,
651
+ commands: false,
652
+ plugins: false,
653
+ subagents: false,
654
+ rules: { file: 'AGENTS.md' },
655
+ workflows: false,
656
+ memory: false,
657
+ modes: ['edit'],
658
+ rulesImports: false,
659
+ },
660
+ },
561
661
  };
562
662
  /** All registered agent IDs derived from the AGENTS registry. */
563
663
  export const ALL_AGENT_IDS = Object.keys(AGENTS);
@@ -1324,6 +1424,15 @@ export async function registerMcp(agentId, name, command, scope = 'user', transp
1324
1424
  if (transport === 'http' && options?.headers && Object.keys(options.headers).length > 0 && !supports(agentId, 'mcpHeaders').ok) {
1325
1425
  return { success: false, error: 'skipped: HTTP MCP headers are only supported for Claude registration' };
1326
1426
  }
1427
+ if (agentId === 'hermes' || agentId === 'forge') {
1428
+ try {
1429
+ writeMcpToConfig(agentId, name, command, scope, transport, options?.home);
1430
+ return { success: true };
1431
+ }
1432
+ catch (err) {
1433
+ return { success: false, error: err.message };
1434
+ }
1435
+ }
1327
1436
  if (!options?.binary && !(await isCliInstalled(agentId))) {
1328
1437
  return { success: false, error: 'CLI not installed' };
1329
1438
  }
@@ -1366,6 +1475,15 @@ export async function unregisterMcp(agentId, name, options) {
1366
1475
  if (!agent.capabilities.mcp) {
1367
1476
  return { success: false, error: 'Agent does not support MCP' };
1368
1477
  }
1478
+ if (agentId === 'hermes' || agentId === 'forge') {
1479
+ try {
1480
+ removeMcpFromConfig(agentId, name, options?.home);
1481
+ return { success: true };
1482
+ }
1483
+ catch (err) {
1484
+ return { success: false, error: err.message };
1485
+ }
1486
+ }
1369
1487
  if (!options?.binary && !(await isCliInstalled(agentId))) {
1370
1488
  return { success: false, error: 'CLI not installed' };
1371
1489
  }
@@ -1422,6 +1540,86 @@ export async function unregisterMcpFromTargets(targets, name) {
1422
1540
  }
1423
1541
  return results;
1424
1542
  }
1543
+ function userMcpConfigPath(agentId, home) {
1544
+ if (home)
1545
+ return getMcpConfigPathForHome(agentId, home);
1546
+ return getUserMcpConfigPath(agentId);
1547
+ }
1548
+ function scopedMcpConfigPath(agentId, scope, home) {
1549
+ if (scope === 'project')
1550
+ return getProjectMcpConfigPath(agentId);
1551
+ return userMcpConfigPath(agentId, home);
1552
+ }
1553
+ function mcpEntryFromCommand(command, transport) {
1554
+ if (transport === 'http') {
1555
+ return { url: command };
1556
+ }
1557
+ const commandArgs = splitCommandLine(command);
1558
+ return {
1559
+ command: commandArgs[0],
1560
+ args: commandArgs.slice(1),
1561
+ };
1562
+ }
1563
+ function readYamlConfig(configPath) {
1564
+ if (!fs.existsSync(configPath))
1565
+ return {};
1566
+ const parsed = yaml.parse(fs.readFileSync(configPath, 'utf-8'));
1567
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
1568
+ ? parsed
1569
+ : {};
1570
+ }
1571
+ function readJsonConfig(configPath) {
1572
+ if (!fs.existsSync(configPath))
1573
+ return {};
1574
+ const content = configPath.endsWith('.jsonc')
1575
+ ? stripJsonComments(fs.readFileSync(configPath, 'utf-8'))
1576
+ : fs.readFileSync(configPath, 'utf-8');
1577
+ const parsed = JSON.parse(content);
1578
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
1579
+ ? parsed
1580
+ : {};
1581
+ }
1582
+ function writeMcpToConfig(agentId, name, command, scope, transport, home) {
1583
+ const configPath = scopedMcpConfigPath(agentId, scope, home);
1584
+ const entry = mcpEntryFromCommand(command, transport);
1585
+ if (agentId === 'hermes') {
1586
+ const config = readYamlConfig(configPath);
1587
+ if (!config.mcp_servers || typeof config.mcp_servers !== 'object' || Array.isArray(config.mcp_servers)) {
1588
+ config.mcp_servers = {};
1589
+ }
1590
+ config.mcp_servers[name] = entry;
1591
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
1592
+ fs.writeFileSync(configPath, yaml.stringify(config), 'utf-8');
1593
+ return;
1594
+ }
1595
+ const config = readJsonConfig(configPath);
1596
+ if (!config.mcpServers || typeof config.mcpServers !== 'object' || Array.isArray(config.mcpServers)) {
1597
+ config.mcpServers = {};
1598
+ }
1599
+ config.mcpServers[name] = entry;
1600
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
1601
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
1602
+ }
1603
+ function removeMcpFromConfig(agentId, name, home) {
1604
+ const configPath = userMcpConfigPath(agentId, home);
1605
+ if (!fs.existsSync(configPath))
1606
+ return;
1607
+ if (agentId === 'hermes') {
1608
+ const config = readYamlConfig(configPath);
1609
+ const servers = config.mcp_servers;
1610
+ if (servers && typeof servers === 'object' && !Array.isArray(servers)) {
1611
+ delete servers[name];
1612
+ fs.writeFileSync(configPath, yaml.stringify(config), 'utf-8');
1613
+ }
1614
+ return;
1615
+ }
1616
+ const config = readJsonConfig(configPath);
1617
+ const servers = config.mcpServers;
1618
+ if (servers && typeof servers === 'object' && !Array.isArray(servers)) {
1619
+ delete servers[name];
1620
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
1621
+ }
1622
+ }
1425
1623
  /**
1426
1624
  * Extract version from npm package specification.
1427
1625
  * Examples: @scope/package@latest -> latest
@@ -1539,6 +1737,20 @@ function parseMcpFromTomlConfig(configPath) {
1539
1737
  return {};
1540
1738
  }
1541
1739
  }
1740
+ function parseMcpFromYamlConfig(configPath) {
1741
+ if (!fs.existsSync(configPath)) {
1742
+ return {};
1743
+ }
1744
+ try {
1745
+ const config = readYamlConfig(configPath);
1746
+ const mcpServers = config.mcp_servers;
1747
+ return mcpServers || {};
1748
+ }
1749
+ catch {
1750
+ /* YAML config corrupt or unreadable */
1751
+ return {};
1752
+ }
1753
+ }
1542
1754
  /**
1543
1755
  * Parse MCP servers from OpenCode's JSONC config.
1544
1756
  * OpenCode stores MCPs in the "mcp" object with different structure.
@@ -1610,6 +1822,10 @@ export function getUserMcpConfigPath(agentId) {
1610
1822
  case 'droid':
1611
1823
  // Factory AI Droid stores MCPs in ~/.factory/mcp.json
1612
1824
  return path.join(agent.configDir, 'mcp.json');
1825
+ case 'hermes':
1826
+ return path.join(agent.configDir, 'config.yaml');
1827
+ case 'forge':
1828
+ return path.join(agent.configDir, '.mcp.json');
1613
1829
  default:
1614
1830
  // Gemini and others use settings.json
1615
1831
  return path.join(agent.configDir, 'settings.json');
@@ -1644,6 +1860,10 @@ export function getMcpConfigPathForHome(agentId, home) {
1644
1860
  return path.join(home, '.grok', 'config.toml');
1645
1861
  case 'droid':
1646
1862
  return path.join(home, '.factory', 'mcp.json');
1863
+ case 'hermes':
1864
+ return path.join(home, '.hermes', 'config.yaml');
1865
+ case 'forge':
1866
+ return path.join(home, '.forge', '.mcp.json');
1647
1867
  default:
1648
1868
  return path.join(home, agentConfigDirName(agentId), 'settings.json');
1649
1869
  }
@@ -1680,6 +1900,10 @@ function getProjectMcpConfigPath(agentId, cwd = process.cwd()) {
1680
1900
  return path.join(cwd, '.grok', 'config.toml');
1681
1901
  case 'droid':
1682
1902
  return path.join(cwd, '.factory', 'mcp.json');
1903
+ case 'hermes':
1904
+ return path.join(cwd, '.hermes', 'config.yaml');
1905
+ case 'forge':
1906
+ return path.join(cwd, '.mcp.json');
1683
1907
  default:
1684
1908
  return path.join(cwd, `.${agentId}`, 'settings.json');
1685
1909
  }
@@ -1733,6 +1957,8 @@ export function parseMcpConfig(agentId, configPath) {
1733
1957
  return parseMcpFromOpenCodeConfig(configPath);
1734
1958
  case 'openclaw':
1735
1959
  return parseMcpFromOpenClawConfig(configPath);
1960
+ case 'hermes':
1961
+ return parseMcpFromYamlConfig(configPath);
1736
1962
  default:
1737
1963
  return parseMcpFromJsonConfig(configPath);
1738
1964
  }
@@ -1818,6 +2044,12 @@ export const AGENT_NAME_ALIASES = {
1818
2044
  'kimi-code': 'kimi',
1819
2045
  factory: 'droid',
1820
2046
  'factory-ai': 'droid',
2047
+ droid: 'droid',
2048
+ hermes: 'hermes',
2049
+ 'hermes-agent': 'hermes',
2050
+ forge: 'forge',
2051
+ forgecode: 'forge',
2052
+ 'forge-code': 'forge',
1821
2053
  };
1822
2054
  /**
1823
2055
  * Resolve a user-provided agent name (alias, shorthand, or canonical) to its AgentId.
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Parked-agent answer router (RUSH-1474).
3
+ *
4
+ * `agents message` historically only enqueued into the mailbox (delivered at the
5
+ * next PreToolUse). That steers a *running* agent between tool calls but never
6
+ * unblocks a *parked* agent:
7
+ * - interactive TUI open on AskUserQuestion (needs keystrokes, not context)
8
+ * - headless run waiting on input (no next tool call; needs resume)
9
+ *
10
+ * This module picks the delivery mechanism from (open feed block × session
11
+ * liveness × runtime rail). Pure — unit-testable without a live PTY.
12
+ */
13
+ import type { OpenBlock, BlockOption } from './feed.js';
14
+ import type { ActiveSession } from './session/active.js';
15
+ import type { InjectTarget } from './terminal/inject.js';
16
+ export type AnswerRouteKind = 'mailbox' | 'pty' | 'tmux' | 'iterm' | 'resume' | 'refuse';
17
+ export interface AnswerRoute {
18
+ kind: AnswerRouteKind;
19
+ /** Human reason shown in CLI output / refused errors. */
20
+ reason: string;
21
+ /**
22
+ * Keystrokes / payload for the chosen path.
23
+ * - mailbox: unused (text is enqueued as-is)
24
+ * - pty/tmux/iterm: the digit or free-text to inject
25
+ * - resume: the free-text prompt to pass on re-entry
26
+ */
27
+ payload?: string;
28
+ /** Inject target when kind is pty/tmux/iterm. */
29
+ inject?: InjectTarget;
30
+ /** Session id + agent kind for resume. */
31
+ resume?: {
32
+ sessionId: string;
33
+ agent: string;
34
+ };
35
+ }
36
+ export interface AnswerRouterInput {
37
+ /** Resolved mailbox / session id. */
38
+ mailboxId: string;
39
+ /** Answer text (option label or free text). */
40
+ answer: string;
41
+ /** Open feed block for this agent, if any. */
42
+ block?: OpenBlock | null;
43
+ /** Live session row matching the mailbox, if any. */
44
+ session?: ActiveSession | null;
45
+ }
46
+ /**
47
+ * Match free-text answer against question options.
48
+ * Exact (case-insensitive) > startsWith > includes. Returns 0-based index or -1.
49
+ */
50
+ export declare function matchOptionIndex(answer: string, options: Array<Pick<BlockOption, 'label'>> | undefined): number;
51
+ /**
52
+ * Build the keystroke payload that closes an AskUserQuestion TUI.
53
+ * Numbered options are selected by digit (1-based) + Enter. Free text with an
54
+ * "Other" option selects Other then types; without Other, types the answer.
55
+ */
56
+ export declare function keystrokesForAnswer(answer: string, options?: Array<Pick<BlockOption, 'label'>>): {
57
+ payload: string;
58
+ matched: 'option' | 'free-text' | 'other';
59
+ };
60
+ /** True when the session is waiting on user input (parked on a question/plan). */
61
+ export declare function isParkedOnInput(session: ActiveSession | null | undefined): boolean;
62
+ /** True when an open feed block still needs an answer. */
63
+ export declare function isOpenQuestionBlock(block: OpenBlock | null | undefined): boolean;
64
+ /**
65
+ * Pick the delivery mechanism for one answer.
66
+ *
67
+ * Precedence:
68
+ * 1. Parked on open question + injectable rail (tmux/iterm/pty) → keystroke
69
+ * 2. Parked on open question + headless (no rail) → resume with answer
70
+ * 3. Parked on open question + no rail + interactive → refuse (don't mailbox-drop)
71
+ * 4. Otherwise → mailbox (running agent between tool calls)
72
+ */
73
+ export declare function resolveAnswerRoute(input: AnswerRouterInput): AnswerRoute;
74
+ /** Build argv for a headless resume that continues with the answer text. */
75
+ export declare function resumeArgv(route: AnswerRoute): string[];