@swarmify/agents-cli 1.13.4 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -319
- package/bin/agents.js +2 -0
- package/package.json +13 -79
- package/scripts/postinstall.js +4 -71
- package/CHANGELOG.md +0 -316
- package/LICENSE +0 -21
- package/dist/commands/__tests__/sessions-tail.test.d.ts +0 -2
- package/dist/commands/__tests__/sessions-tail.test.d.ts.map +0 -1
- package/dist/commands/__tests__/sessions-tail.test.js +0 -108
- package/dist/commands/__tests__/sessions-tail.test.js.map +0 -1
- package/dist/commands/__tests__/sessions.test.d.ts +0 -2
- package/dist/commands/__tests__/sessions.test.d.ts.map +0 -1
- package/dist/commands/__tests__/sessions.test.js +0 -636
- package/dist/commands/__tests__/sessions.test.js.map +0 -1
- package/dist/commands/cloud.d.ts +0 -11
- package/dist/commands/cloud.d.ts.map +0 -1
- package/dist/commands/cloud.js +0 -363
- package/dist/commands/cloud.js.map +0 -1
- package/dist/commands/commands.d.ts +0 -12
- package/dist/commands/commands.d.ts.map +0 -1
- package/dist/commands/commands.js +0 -629
- package/dist/commands/commands.js.map +0 -1
- package/dist/commands/daemon.d.ts +0 -11
- package/dist/commands/daemon.d.ts.map +0 -1
- package/dist/commands/daemon.js +0 -119
- package/dist/commands/daemon.js.map +0 -1
- package/dist/commands/drive.d.ts +0 -11
- package/dist/commands/drive.d.ts.map +0 -1
- package/dist/commands/drive.js +0 -175
- package/dist/commands/drive.js.map +0 -1
- package/dist/commands/exec.d.ts +0 -11
- package/dist/commands/exec.d.ts.map +0 -1
- package/dist/commands/exec.js +0 -251
- package/dist/commands/exec.js.map +0 -1
- package/dist/commands/factory.d.ts +0 -11
- package/dist/commands/factory.d.ts.map +0 -1
- package/dist/commands/factory.js +0 -445
- package/dist/commands/factory.js.map +0 -1
- package/dist/commands/fork.d.ts +0 -11
- package/dist/commands/fork.d.ts.map +0 -1
- package/dist/commands/fork.js +0 -147
- package/dist/commands/fork.js.map +0 -1
- package/dist/commands/hooks.d.ts +0 -12
- package/dist/commands/hooks.d.ts.map +0 -1
- package/dist/commands/hooks.js +0 -690
- package/dist/commands/hooks.js.map +0 -1
- package/dist/commands/init.d.ts +0 -15
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -137
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/mcp.d.ts +0 -12
- package/dist/commands/mcp.d.ts.map +0 -1
- package/dist/commands/mcp.js +0 -583
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/models.d.ts +0 -11
- package/dist/commands/models.d.ts.map +0 -1
- package/dist/commands/models.js +0 -170
- package/dist/commands/models.js.map +0 -1
- package/dist/commands/packages.d.ts +0 -11
- package/dist/commands/packages.d.ts.map +0 -1
- package/dist/commands/packages.js +0 -551
- package/dist/commands/packages.js.map +0 -1
- package/dist/commands/permissions.d.ts +0 -12
- package/dist/commands/permissions.d.ts.map +0 -1
- package/dist/commands/permissions.js +0 -724
- package/dist/commands/permissions.js.map +0 -1
- package/dist/commands/plugins.d.ts +0 -11
- package/dist/commands/plugins.d.ts.map +0 -1
- package/dist/commands/plugins.js +0 -393
- package/dist/commands/plugins.js.map +0 -1
- package/dist/commands/profiles.d.ts +0 -12
- package/dist/commands/profiles.d.ts.map +0 -1
- package/dist/commands/profiles.js +0 -255
- package/dist/commands/profiles.js.map +0 -1
- package/dist/commands/pty.d.ts +0 -21
- package/dist/commands/pty.d.ts.map +0 -1
- package/dist/commands/pty.js +0 -391
- package/dist/commands/pty.js.map +0 -1
- package/dist/commands/pull.d.ts +0 -11
- package/dist/commands/pull.d.ts.map +0 -1
- package/dist/commands/pull.js +0 -456
- package/dist/commands/pull.js.map +0 -1
- package/dist/commands/push.d.ts +0 -11
- package/dist/commands/push.d.ts.map +0 -1
- package/dist/commands/push.js +0 -188
- package/dist/commands/push.js.map +0 -1
- package/dist/commands/refresh-memory.d.ts +0 -16
- package/dist/commands/refresh-memory.d.ts.map +0 -1
- package/dist/commands/refresh-memory.js +0 -52
- package/dist/commands/refresh-memory.js.map +0 -1
- package/dist/commands/resource-view.d.ts +0 -39
- package/dist/commands/resource-view.d.ts.map +0 -1
- package/dist/commands/resource-view.js +0 -197
- package/dist/commands/resource-view.js.map +0 -1
- package/dist/commands/routines.d.ts +0 -11
- package/dist/commands/routines.d.ts.map +0 -1
- package/dist/commands/routines.js +0 -590
- package/dist/commands/routines.js.map +0 -1
- package/dist/commands/rules.d.ts +0 -12
- package/dist/commands/rules.d.ts.map +0 -1
- package/dist/commands/rules.js +0 -489
- package/dist/commands/rules.js.map +0 -1
- package/dist/commands/secrets.d.ts +0 -11
- package/dist/commands/secrets.d.ts.map +0 -1
- package/dist/commands/secrets.js +0 -352
- package/dist/commands/secrets.js.map +0 -1
- package/dist/commands/sessions-picker.d.ts +0 -18
- package/dist/commands/sessions-picker.d.ts.map +0 -1
- package/dist/commands/sessions-picker.js +0 -265
- package/dist/commands/sessions-picker.js.map +0 -1
- package/dist/commands/sessions-tail.d.ts +0 -20
- package/dist/commands/sessions-tail.d.ts.map +0 -1
- package/dist/commands/sessions-tail.js +0 -236
- package/dist/commands/sessions-tail.js.map +0 -1
- package/dist/commands/sessions.d.ts +0 -18
- package/dist/commands/sessions.d.ts.map +0 -1
- package/dist/commands/sessions.js +0 -1173
- package/dist/commands/sessions.js.map +0 -1
- package/dist/commands/skills.d.ts +0 -12
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js +0 -717
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/status.d.ts +0 -10
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -26
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/subagents.d.ts +0 -11
- package/dist/commands/subagents.d.ts.map +0 -1
- package/dist/commands/subagents.js +0 -361
- package/dist/commands/subagents.js.map +0 -1
- package/dist/commands/sync.d.ts +0 -11
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js +0 -70
- package/dist/commands/sync.js.map +0 -1
- package/dist/commands/teams-picker.d.ts +0 -18
- package/dist/commands/teams-picker.d.ts.map +0 -1
- package/dist/commands/teams-picker.js +0 -290
- package/dist/commands/teams-picker.js.map +0 -1
- package/dist/commands/teams.d.ts +0 -18
- package/dist/commands/teams.d.ts.map +0 -1
- package/dist/commands/teams.js +0 -1144
- package/dist/commands/teams.js.map +0 -1
- package/dist/commands/utils.d.ts +0 -45
- package/dist/commands/utils.d.ts.map +0 -1
- package/dist/commands/utils.js +0 -106
- package/dist/commands/utils.js.map +0 -1
- package/dist/commands/versions.d.ts +0 -11
- package/dist/commands/versions.d.ts.map +0 -1
- package/dist/commands/versions.js +0 -701
- package/dist/commands/versions.js.map +0 -1
- package/dist/commands/view.d.ts +0 -43
- package/dist/commands/view.d.ts.map +0 -1
- package/dist/commands/view.js +0 -894
- package/dist/commands/view.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -496
- package/dist/index.js.map +0 -1
- package/dist/lib/__tests__/bugfixes.test.d.ts +0 -2
- package/dist/lib/__tests__/bugfixes.test.d.ts.map +0 -1
- package/dist/lib/__tests__/bugfixes.test.js +0 -192
- package/dist/lib/__tests__/bugfixes.test.js.map +0 -1
- package/dist/lib/__tests__/exec.test.d.ts +0 -2
- package/dist/lib/__tests__/exec.test.d.ts.map +0 -1
- package/dist/lib/__tests__/exec.test.js +0 -446
- package/dist/lib/__tests__/exec.test.js.map +0 -1
- package/dist/lib/__tests__/git-sync.test.d.ts +0 -2
- package/dist/lib/__tests__/git-sync.test.d.ts.map +0 -1
- package/dist/lib/__tests__/git-sync.test.js +0 -138
- package/dist/lib/__tests__/git-sync.test.js.map +0 -1
- package/dist/lib/__tests__/hooks.test.d.ts +0 -2
- package/dist/lib/__tests__/hooks.test.d.ts.map +0 -1
- package/dist/lib/__tests__/hooks.test.js +0 -203
- package/dist/lib/__tests__/hooks.test.js.map +0 -1
- package/dist/lib/__tests__/memory-compile.test.d.ts +0 -2
- package/dist/lib/__tests__/memory-compile.test.d.ts.map +0 -1
- package/dist/lib/__tests__/memory-compile.test.js +0 -95
- package/dist/lib/__tests__/memory-compile.test.js.map +0 -1
- package/dist/lib/__tests__/models.test.d.ts +0 -2
- package/dist/lib/__tests__/models.test.d.ts.map +0 -1
- package/dist/lib/__tests__/models.test.js +0 -239
- package/dist/lib/__tests__/models.test.js.map +0 -1
- package/dist/lib/__tests__/rotate.test.d.ts +0 -2
- package/dist/lib/__tests__/rotate.test.d.ts.map +0 -1
- package/dist/lib/__tests__/rotate.test.js +0 -80
- package/dist/lib/__tests__/rotate.test.js.map +0 -1
- package/dist/lib/__tests__/secrets-bundles.test.d.ts +0 -2
- package/dist/lib/__tests__/secrets-bundles.test.d.ts.map +0 -1
- package/dist/lib/__tests__/secrets-bundles.test.js +0 -104
- package/dist/lib/__tests__/secrets-bundles.test.js.map +0 -1
- package/dist/lib/__tests__/secrets.test.d.ts +0 -2
- package/dist/lib/__tests__/secrets.test.d.ts.map +0 -1
- package/dist/lib/__tests__/secrets.test.js +0 -90
- package/dist/lib/__tests__/secrets.test.js.map +0 -1
- package/dist/lib/__tests__/shims.test.d.ts +0 -2
- package/dist/lib/__tests__/shims.test.d.ts.map +0 -1
- package/dist/lib/__tests__/shims.test.js +0 -57
- package/dist/lib/__tests__/shims.test.js.map +0 -1
- package/dist/lib/__tests__/usage.test.d.ts +0 -2
- package/dist/lib/__tests__/usage.test.d.ts.map +0 -1
- package/dist/lib/__tests__/usage.test.js +0 -220
- package/dist/lib/__tests__/usage.test.js.map +0 -1
- package/dist/lib/__tests__/versions.test.d.ts +0 -2
- package/dist/lib/__tests__/versions.test.d.ts.map +0 -1
- package/dist/lib/__tests__/versions.test.js +0 -63
- package/dist/lib/__tests__/versions.test.js.map +0 -1
- package/dist/lib/agents.d.ts +0 -158
- package/dist/lib/agents.d.ts.map +0 -1
- package/dist/lib/agents.js +0 -1159
- package/dist/lib/agents.js.map +0 -1
- package/dist/lib/artifact-actions.d.ts +0 -27
- package/dist/lib/artifact-actions.d.ts.map +0 -1
- package/dist/lib/artifact-actions.js +0 -58
- package/dist/lib/artifact-actions.js.map +0 -1
- package/dist/lib/cloud/codex.d.ts +0 -26
- package/dist/lib/cloud/codex.d.ts.map +0 -1
- package/dist/lib/cloud/codex.js +0 -237
- package/dist/lib/cloud/codex.js.map +0 -1
- package/dist/lib/cloud/factory.d.ts +0 -32
- package/dist/lib/cloud/factory.d.ts.map +0 -1
- package/dist/lib/cloud/factory.js +0 -43
- package/dist/lib/cloud/factory.js.map +0 -1
- package/dist/lib/cloud/registry.d.ts +0 -16
- package/dist/lib/cloud/registry.d.ts.map +0 -1
- package/dist/lib/cloud/registry.js +0 -68
- package/dist/lib/cloud/registry.js.map +0 -1
- package/dist/lib/cloud/rush.d.ts +0 -37
- package/dist/lib/cloud/rush.d.ts.map +0 -1
- package/dist/lib/cloud/rush.js +0 -230
- package/dist/lib/cloud/rush.js.map +0 -1
- package/dist/lib/cloud/rush.test.d.ts +0 -2
- package/dist/lib/cloud/rush.test.d.ts.map +0 -1
- package/dist/lib/cloud/rush.test.js +0 -63
- package/dist/lib/cloud/rush.test.js.map +0 -1
- package/dist/lib/cloud/store.d.ts +0 -23
- package/dist/lib/cloud/store.d.ts.map +0 -1
- package/dist/lib/cloud/store.js +0 -116
- package/dist/lib/cloud/store.js.map +0 -1
- package/dist/lib/cloud/stream.d.ts +0 -24
- package/dist/lib/cloud/stream.d.ts.map +0 -1
- package/dist/lib/cloud/stream.js +0 -145
- package/dist/lib/cloud/stream.js.map +0 -1
- package/dist/lib/cloud/types.d.ts +0 -109
- package/dist/lib/cloud/types.d.ts.map +0 -1
- package/dist/lib/cloud/types.js +0 -33
- package/dist/lib/cloud/types.js.map +0 -1
- package/dist/lib/cloud/types.test.d.ts +0 -2
- package/dist/lib/cloud/types.test.d.ts.map +0 -1
- package/dist/lib/cloud/types.test.js +0 -41
- package/dist/lib/cloud/types.test.js.map +0 -1
- package/dist/lib/commands.d.ts +0 -141
- package/dist/lib/commands.d.ts.map +0 -1
- package/dist/lib/commands.js +0 -514
- package/dist/lib/commands.js.map +0 -1
- package/dist/lib/convert.d.ts +0 -21
- package/dist/lib/convert.d.ts.map +0 -1
- package/dist/lib/convert.js +0 -54
- package/dist/lib/convert.js.map +0 -1
- package/dist/lib/daemon.d.ts +0 -43
- package/dist/lib/daemon.d.ts.map +0 -1
- package/dist/lib/daemon.js +0 -332
- package/dist/lib/daemon.js.map +0 -1
- package/dist/lib/drive-sync.d.ts +0 -46
- package/dist/lib/drive-sync.d.ts.map +0 -1
- package/dist/lib/drive-sync.js +0 -209
- package/dist/lib/drive-sync.js.map +0 -1
- package/dist/lib/exec.d.ts +0 -101
- package/dist/lib/exec.d.ts.map +0 -1
- package/dist/lib/exec.js +0 -450
- package/dist/lib/exec.js.map +0 -1
- package/dist/lib/factory/__tests__/config.test.d.ts +0 -2
- package/dist/lib/factory/__tests__/config.test.d.ts.map +0 -1
- package/dist/lib/factory/__tests__/config.test.js +0 -128
- package/dist/lib/factory/__tests__/config.test.js.map +0 -1
- package/dist/lib/factory/config.d.ts +0 -49
- package/dist/lib/factory/config.d.ts.map +0 -1
- package/dist/lib/factory/config.js +0 -127
- package/dist/lib/factory/config.js.map +0 -1
- package/dist/lib/factory.d.ts +0 -57
- package/dist/lib/factory.d.ts.map +0 -1
- package/dist/lib/factory.js +0 -107
- package/dist/lib/factory.js.map +0 -1
- package/dist/lib/git.d.ts +0 -157
- package/dist/lib/git.d.ts.map +0 -1
- package/dist/lib/git.js +0 -647
- package/dist/lib/git.js.map +0 -1
- package/dist/lib/help.d.ts +0 -10
- package/dist/lib/help.d.ts.map +0 -1
- package/dist/lib/help.js +0 -66
- package/dist/lib/help.js.map +0 -1
- package/dist/lib/hooks.d.ts +0 -125
- package/dist/lib/hooks.d.ts.map +0 -1
- package/dist/lib/hooks.js +0 -846
- package/dist/lib/hooks.js.map +0 -1
- package/dist/lib/ledger/__tests__/local.test.d.ts +0 -2
- package/dist/lib/ledger/__tests__/local.test.d.ts.map +0 -1
- package/dist/lib/ledger/__tests__/local.test.js +0 -177
- package/dist/lib/ledger/__tests__/local.test.js.map +0 -1
- package/dist/lib/ledger/__tests__/sync.test.d.ts +0 -2
- package/dist/lib/ledger/__tests__/sync.test.d.ts.map +0 -1
- package/dist/lib/ledger/__tests__/sync.test.js +0 -117
- package/dist/lib/ledger/__tests__/sync.test.js.map +0 -1
- package/dist/lib/ledger/index.d.ts +0 -18
- package/dist/lib/ledger/index.d.ts.map +0 -1
- package/dist/lib/ledger/index.js +0 -32
- package/dist/lib/ledger/index.js.map +0 -1
- package/dist/lib/ledger/local.d.ts +0 -22
- package/dist/lib/ledger/local.d.ts.map +0 -1
- package/dist/lib/ledger/local.js +0 -333
- package/dist/lib/ledger/local.js.map +0 -1
- package/dist/lib/ledger/r2.d.ts +0 -41
- package/dist/lib/ledger/r2.d.ts.map +0 -1
- package/dist/lib/ledger/r2.js +0 -335
- package/dist/lib/ledger/r2.js.map +0 -1
- package/dist/lib/ledger/sync.d.ts +0 -33
- package/dist/lib/ledger/sync.d.ts.map +0 -1
- package/dist/lib/ledger/sync.js +0 -106
- package/dist/lib/ledger/sync.js.map +0 -1
- package/dist/lib/ledger/types.d.ts +0 -100
- package/dist/lib/ledger/types.d.ts.map +0 -1
- package/dist/lib/ledger/types.js +0 -21
- package/dist/lib/ledger/types.js.map +0 -1
- package/dist/lib/manifest.d.ts +0 -14
- package/dist/lib/manifest.d.ts.map +0 -1
- package/dist/lib/manifest.js +0 -48
- package/dist/lib/manifest.js.map +0 -1
- package/dist/lib/markdown.d.ts +0 -5
- package/dist/lib/markdown.d.ts.map +0 -1
- package/dist/lib/markdown.js +0 -17
- package/dist/lib/markdown.js.map +0 -1
- package/dist/lib/mcp.d.ts +0 -64
- package/dist/lib/mcp.d.ts.map +0 -1
- package/dist/lib/mcp.js +0 -327
- package/dist/lib/mcp.js.map +0 -1
- package/dist/lib/memory-compile.d.ts +0 -65
- package/dist/lib/memory-compile.d.ts.map +0 -1
- package/dist/lib/memory-compile.js +0 -174
- package/dist/lib/memory-compile.js.map +0 -1
- package/dist/lib/memory.d.ts +0 -64
- package/dist/lib/memory.d.ts.map +0 -1
- package/dist/lib/memory.js +0 -275
- package/dist/lib/memory.js.map +0 -1
- package/dist/lib/models.d.ts +0 -98
- package/dist/lib/models.d.ts.map +0 -1
- package/dist/lib/models.js +0 -728
- package/dist/lib/models.js.map +0 -1
- package/dist/lib/permissions.d.ts +0 -227
- package/dist/lib/permissions.d.ts.map +0 -1
- package/dist/lib/permissions.js +0 -1085
- package/dist/lib/permissions.js.map +0 -1
- package/dist/lib/picker.d.ts +0 -27
- package/dist/lib/picker.d.ts.map +0 -1
- package/dist/lib/picker.js +0 -110
- package/dist/lib/picker.js.map +0 -1
- package/dist/lib/plugins.d.ts +0 -80
- package/dist/lib/plugins.d.ts.map +0 -1
- package/dist/lib/plugins.js +0 -556
- package/dist/lib/plugins.js.map +0 -1
- package/dist/lib/profiles-keychain.d.ts +0 -11
- package/dist/lib/profiles-keychain.d.ts.map +0 -1
- package/dist/lib/profiles-keychain.js +0 -14
- package/dist/lib/profiles-keychain.js.map +0 -1
- package/dist/lib/profiles-presets.d.ts +0 -25
- package/dist/lib/profiles-presets.d.ts.map +0 -1
- package/dist/lib/profiles-presets.js +0 -104
- package/dist/lib/profiles-presets.js.map +0 -1
- package/dist/lib/profiles.d.ts +0 -70
- package/dist/lib/profiles.d.ts.map +0 -1
- package/dist/lib/profiles.js +0 -145
- package/dist/lib/profiles.js.map +0 -1
- package/dist/lib/pty-client.d.ts +0 -23
- package/dist/lib/pty-client.d.ts.map +0 -1
- package/dist/lib/pty-client.js +0 -181
- package/dist/lib/pty-client.js.map +0 -1
- package/dist/lib/pty-server.d.ts +0 -21
- package/dist/lib/pty-server.d.ts.map +0 -1
- package/dist/lib/pty-server.js +0 -427
- package/dist/lib/pty-server.js.map +0 -1
- package/dist/lib/registry.d.ts +0 -45
- package/dist/lib/registry.d.ts.map +0 -1
- package/dist/lib/registry.js +0 -220
- package/dist/lib/registry.js.map +0 -1
- package/dist/lib/resources.d.ts +0 -55
- package/dist/lib/resources.d.ts.map +0 -1
- package/dist/lib/resources.js +0 -103
- package/dist/lib/resources.js.map +0 -1
- package/dist/lib/rotate.d.ts +0 -58
- package/dist/lib/rotate.d.ts.map +0 -1
- package/dist/lib/rotate.js +0 -93
- package/dist/lib/rotate.js.map +0 -1
- package/dist/lib/routines.d.ts +0 -99
- package/dist/lib/routines.d.ts.map +0 -1
- package/dist/lib/routines.js +0 -352
- package/dist/lib/routines.js.map +0 -1
- package/dist/lib/runner.d.ts +0 -26
- package/dist/lib/runner.d.ts.map +0 -1
- package/dist/lib/runner.js +0 -325
- package/dist/lib/runner.js.map +0 -1
- package/dist/lib/sandbox.d.ts +0 -26
- package/dist/lib/sandbox.d.ts.map +0 -1
- package/dist/lib/sandbox.js +0 -218
- package/dist/lib/sandbox.js.map +0 -1
- package/dist/lib/scheduler.d.ts +0 -26
- package/dist/lib/scheduler.d.ts.map +0 -1
- package/dist/lib/scheduler.js +0 -77
- package/dist/lib/scheduler.js.map +0 -1
- package/dist/lib/secrets-bundles.d.ts +0 -38
- package/dist/lib/secrets-bundles.d.ts.map +0 -1
- package/dist/lib/secrets-bundles.js +0 -176
- package/dist/lib/secrets-bundles.js.map +0 -1
- package/dist/lib/secrets.d.ts +0 -53
- package/dist/lib/secrets.d.ts.map +0 -1
- package/dist/lib/secrets.js +0 -140
- package/dist/lib/secrets.js.map +0 -1
- package/dist/lib/session/__tests__/db.test.d.ts +0 -2
- package/dist/lib/session/__tests__/db.test.d.ts.map +0 -1
- package/dist/lib/session/__tests__/db.test.js +0 -54
- package/dist/lib/session/__tests__/db.test.js.map +0 -1
- package/dist/lib/session/__tests__/discover.test.d.ts +0 -2
- package/dist/lib/session/__tests__/discover.test.d.ts.map +0 -1
- package/dist/lib/session/__tests__/discover.test.js +0 -63
- package/dist/lib/session/__tests__/discover.test.js.map +0 -1
- package/dist/lib/session/__tests__/prompt.test.d.ts +0 -2
- package/dist/lib/session/__tests__/prompt.test.d.ts.map +0 -1
- package/dist/lib/session/__tests__/prompt.test.js +0 -44
- package/dist/lib/session/__tests__/prompt.test.js.map +0 -1
- package/dist/lib/session/__tests__/render.test.d.ts +0 -2
- package/dist/lib/session/__tests__/render.test.d.ts.map +0 -1
- package/dist/lib/session/__tests__/render.test.js +0 -602
- package/dist/lib/session/__tests__/render.test.js.map +0 -1
- package/dist/lib/session/active.d.ts +0 -44
- package/dist/lib/session/active.d.ts.map +0 -1
- package/dist/lib/session/active.js +0 -379
- package/dist/lib/session/active.js.map +0 -1
- package/dist/lib/session/artifacts.d.ts +0 -15
- package/dist/lib/session/artifacts.d.ts.map +0 -1
- package/dist/lib/session/artifacts.js +0 -86
- package/dist/lib/session/artifacts.js.map +0 -1
- package/dist/lib/session/db.d.ts +0 -140
- package/dist/lib/session/db.d.ts.map +0 -1
- package/dist/lib/session/db.js +0 -599
- package/dist/lib/session/db.js.map +0 -1
- package/dist/lib/session/discover.d.ts +0 -72
- package/dist/lib/session/discover.d.ts.map +0 -1
- package/dist/lib/session/discover.js +0 -1315
- package/dist/lib/session/discover.js.map +0 -1
- package/dist/lib/session/parse.d.ts +0 -34
- package/dist/lib/session/parse.d.ts.map +0 -1
- package/dist/lib/session/parse.js +0 -663
- package/dist/lib/session/parse.js.map +0 -1
- package/dist/lib/session/prompt.d.ts +0 -13
- package/dist/lib/session/prompt.d.ts.map +0 -1
- package/dist/lib/session/prompt.js +0 -79
- package/dist/lib/session/prompt.js.map +0 -1
- package/dist/lib/session/prompt.test.d.ts +0 -2
- package/dist/lib/session/prompt.test.d.ts.map +0 -1
- package/dist/lib/session/prompt.test.js +0 -57
- package/dist/lib/session/prompt.test.js.map +0 -1
- package/dist/lib/session/render.d.ts +0 -103
- package/dist/lib/session/render.d.ts.map +0 -1
- package/dist/lib/session/render.js +0 -798
- package/dist/lib/session/render.js.map +0 -1
- package/dist/lib/session/team-filter.d.ts +0 -35
- package/dist/lib/session/team-filter.d.ts.map +0 -1
- package/dist/lib/session/team-filter.js +0 -75
- package/dist/lib/session/team-filter.js.map +0 -1
- package/dist/lib/session/team-filter.test.d.ts +0 -2
- package/dist/lib/session/team-filter.test.d.ts.map +0 -1
- package/dist/lib/session/team-filter.test.js +0 -157
- package/dist/lib/session/team-filter.test.js.map +0 -1
- package/dist/lib/session/types.d.ts +0 -84
- package/dist/lib/session/types.d.ts.map +0 -1
- package/dist/lib/session/types.js +0 -11
- package/dist/lib/session/types.js.map +0 -1
- package/dist/lib/shims.d.ts +0 -272
- package/dist/lib/shims.d.ts.map +0 -1
- package/dist/lib/shims.js +0 -1322
- package/dist/lib/shims.js.map +0 -1
- package/dist/lib/skills.d.ts +0 -142
- package/dist/lib/skills.d.ts.map +0 -1
- package/dist/lib/skills.js +0 -791
- package/dist/lib/skills.js.map +0 -1
- package/dist/lib/state.d.ts +0 -87
- package/dist/lib/state.d.ts.map +0 -1
- package/dist/lib/state.js +0 -333
- package/dist/lib/state.js.map +0 -1
- package/dist/lib/subagents.d.ts +0 -84
- package/dist/lib/subagents.d.ts.map +0 -1
- package/dist/lib/subagents.js +0 -410
- package/dist/lib/subagents.js.map +0 -1
- package/dist/lib/teams/__tests__/oracle.test.d.ts +0 -2
- package/dist/lib/teams/__tests__/oracle.test.d.ts.map +0 -1
- package/dist/lib/teams/__tests__/oracle.test.js +0 -89
- package/dist/lib/teams/__tests__/oracle.test.js.map +0 -1
- package/dist/lib/teams/__tests__/supervisor.test.d.ts +0 -2
- package/dist/lib/teams/__tests__/supervisor.test.d.ts.map +0 -1
- package/dist/lib/teams/__tests__/supervisor.test.js +0 -179
- package/dist/lib/teams/__tests__/supervisor.test.js.map +0 -1
- package/dist/lib/teams/agents.d.ts +0 -247
- package/dist/lib/teams/agents.d.ts.map +0 -1
- package/dist/lib/teams/agents.js +0 -1244
- package/dist/lib/teams/agents.js.map +0 -1
- package/dist/lib/teams/api.d.ts +0 -91
- package/dist/lib/teams/api.d.ts.map +0 -1
- package/dist/lib/teams/api.js +0 -239
- package/dist/lib/teams/api.js.map +0 -1
- package/dist/lib/teams/cloud.d.ts +0 -11
- package/dist/lib/teams/cloud.d.ts.map +0 -1
- package/dist/lib/teams/cloud.js +0 -169
- package/dist/lib/teams/cloud.js.map +0 -1
- package/dist/lib/teams/debug.d.ts +0 -8
- package/dist/lib/teams/debug.d.ts.map +0 -1
- package/dist/lib/teams/debug.js +0 -12
- package/dist/lib/teams/debug.js.map +0 -1
- package/dist/lib/teams/file_ops.d.ts +0 -13
- package/dist/lib/teams/file_ops.d.ts.map +0 -1
- package/dist/lib/teams/file_ops.js +0 -66
- package/dist/lib/teams/file_ops.js.map +0 -1
- package/dist/lib/teams/index.d.ts +0 -16
- package/dist/lib/teams/index.d.ts.map +0 -1
- package/dist/lib/teams/index.js +0 -15
- package/dist/lib/teams/index.js.map +0 -1
- package/dist/lib/teams/oracle.d.ts +0 -20
- package/dist/lib/teams/oracle.d.ts.map +0 -1
- package/dist/lib/teams/oracle.js +0 -59
- package/dist/lib/teams/oracle.js.map +0 -1
- package/dist/lib/teams/parsers.d.ts +0 -9
- package/dist/lib/teams/parsers.d.ts.map +0 -1
- package/dist/lib/teams/parsers.js +0 -837
- package/dist/lib/teams/parsers.js.map +0 -1
- package/dist/lib/teams/persistence.d.ts +0 -43
- package/dist/lib/teams/persistence.d.ts.map +0 -1
- package/dist/lib/teams/persistence.js +0 -299
- package/dist/lib/teams/persistence.js.map +0 -1
- package/dist/lib/teams/ralph.d.ts +0 -8
- package/dist/lib/teams/ralph.d.ts.map +0 -1
- package/dist/lib/teams/ralph.js +0 -59
- package/dist/lib/teams/ralph.js.map +0 -1
- package/dist/lib/teams/registry.d.ts +0 -18
- package/dist/lib/teams/registry.d.ts.map +0 -1
- package/dist/lib/teams/registry.js +0 -68
- package/dist/lib/teams/registry.js.map +0 -1
- package/dist/lib/teams/summarizer.d.ts +0 -73
- package/dist/lib/teams/summarizer.d.ts.map +0 -1
- package/dist/lib/teams/summarizer.js +0 -780
- package/dist/lib/teams/summarizer.js.map +0 -1
- package/dist/lib/teams/supervisor.d.ts +0 -49
- package/dist/lib/teams/supervisor.d.ts.map +0 -1
- package/dist/lib/teams/supervisor.js +0 -74
- package/dist/lib/teams/supervisor.js.map +0 -1
- package/dist/lib/template.d.ts +0 -27
- package/dist/lib/template.d.ts.map +0 -1
- package/dist/lib/template.js +0 -60
- package/dist/lib/template.js.map +0 -1
- package/dist/lib/types.d.ts +0 -331
- package/dist/lib/types.d.ts.map +0 -1
- package/dist/lib/types.js +0 -29
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/usage.d.ts +0 -105
- package/dist/lib/usage.d.ts.map +0 -1
- package/dist/lib/usage.js +0 -686
- package/dist/lib/usage.js.map +0 -1
- package/dist/lib/versions.d.ts +0 -253
- package/dist/lib/versions.d.ts.map +0 -1
- package/dist/lib/versions.js +0 -1796
- package/dist/lib/versions.js.map +0 -1
- package/scripts/rebuild-sqlite.sh +0 -46
package/dist/lib/versions.js
DELETED
|
@@ -1,1796 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Version management module for agents-cli.
|
|
3
|
-
*
|
|
4
|
-
* Handles installing, removing, listing, and switching between agent CLI versions.
|
|
5
|
-
* Each version is installed into an isolated directory under ~/.agents/versions/{agent}/{version}/
|
|
6
|
-
* with its own HOME directory for config isolation. Resources (commands, skills, hooks, memory,
|
|
7
|
-
* MCP servers, permissions, subagents, plugins) from ~/.agents/ are synced into version homes
|
|
8
|
-
* via copies or conversions (not symlinks).
|
|
9
|
-
*
|
|
10
|
-
* Key responsibilities:
|
|
11
|
-
* - Version lifecycle: install, remove, list, resolve (project-level or global default)
|
|
12
|
-
* - Resource discovery: scan ~/.agents/ for available resources across all types
|
|
13
|
-
* - Resource sync: copy/convert resources into a version's isolated config directory
|
|
14
|
-
* - Diff and reconciliation: detect new/unsynced resources and prompt users to sync them
|
|
15
|
-
* - Agent/version target resolution: parse agent@version specs from CLI flags
|
|
16
|
-
*/
|
|
17
|
-
import * as fs from 'fs';
|
|
18
|
-
import * as path from 'path';
|
|
19
|
-
import * as os from 'os';
|
|
20
|
-
import * as yaml from 'yaml';
|
|
21
|
-
import { exec } from 'child_process';
|
|
22
|
-
import { promisify } from 'util';
|
|
23
|
-
import chalk from 'chalk';
|
|
24
|
-
import * as TOML from 'smol-toml';
|
|
25
|
-
import { checkbox, select } from '@inquirer/prompts';
|
|
26
|
-
import { getVersionsDir, ensureAgentsDir, readMeta, writeMeta, getCommandsDir, getSkillsDir, getHooksDir, getMemoryDir, clearVersionResources, recordVersionResources, getProjectAgentsDir, getPromptcutsPath } from './state.js';
|
|
27
|
-
import { AGENTS, getAccountEmail, MCP_CAPABLE_AGENTS, COMMANDS_CAPABLE_AGENTS, getMcpConfigPathForHome, parseMcpConfig, resolveAgentName, formatAgentError, CODEX_HOOKS_MIN_VERSION } from './agents.js';
|
|
28
|
-
import { applyPermissionsToVersion as applyPermsToVersion, PERMISSIONS_CAPABLE_AGENTS, discoverPermissionGroups, buildPermissionsFromGroups, CODEX_RULES_FILENAME, getActivePermissionSetName, readPermissionSetRecipe, PERMISSION_SET_ENV_VAR } from './permissions.js';
|
|
29
|
-
import { installMcpServers } from './mcp.js';
|
|
30
|
-
import { markdownToToml } from './convert.js';
|
|
31
|
-
import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion, ensureClaudeInsideSymlink } from './shims.js';
|
|
32
|
-
import { listInstalledSubagents, transformSubagentForClaude, syncSubagentToOpenclaw, SUBAGENT_CAPABLE_AGENTS } from './subagents.js';
|
|
33
|
-
import { registerHooksToSettings } from './hooks.js';
|
|
34
|
-
import { discoverPlugins, syncPluginToVersion, isPluginSynced, pluginSupportsAgent, cleanOrphanedPluginSkills } from './plugins.js';
|
|
35
|
-
import { compileMemoryForAgent } from './memory-compile.js';
|
|
36
|
-
import { PLUGINS_CAPABLE_AGENTS } from './agents.js';
|
|
37
|
-
/** Promisified exec for running shell commands. */
|
|
38
|
-
const execAsync = promisify(exec);
|
|
39
|
-
/**
|
|
40
|
-
* Get all available resources from ~/.agents/.
|
|
41
|
-
*/
|
|
42
|
-
export function getAvailableResources(cwd = process.cwd()) {
|
|
43
|
-
const result = {
|
|
44
|
-
commands: [],
|
|
45
|
-
skills: [],
|
|
46
|
-
hooks: [],
|
|
47
|
-
memory: [],
|
|
48
|
-
mcp: [],
|
|
49
|
-
permissions: [],
|
|
50
|
-
subagents: [],
|
|
51
|
-
plugins: [],
|
|
52
|
-
promptcuts: false,
|
|
53
|
-
};
|
|
54
|
-
const projectAgentsDir = getProjectAgentsDir(cwd);
|
|
55
|
-
const userBase = path.dirname(getCommandsDir());
|
|
56
|
-
const resourceBases = [];
|
|
57
|
-
if (projectAgentsDir) {
|
|
58
|
-
resourceBases.push({ scope: 'project', base: projectAgentsDir });
|
|
59
|
-
}
|
|
60
|
-
resourceBases.push({ scope: 'user', base: userBase });
|
|
61
|
-
// Commands (*.md files)
|
|
62
|
-
const commandNames = new Set();
|
|
63
|
-
for (const { base } of resourceBases) {
|
|
64
|
-
const commandsDir = path.join(base, 'commands');
|
|
65
|
-
if (!fs.existsSync(commandsDir))
|
|
66
|
-
continue;
|
|
67
|
-
const names = fs.readdirSync(commandsDir)
|
|
68
|
-
.filter(f => f.endsWith('.md'))
|
|
69
|
-
.map(f => f.replace(/\.md$/, ''));
|
|
70
|
-
for (const name of names) {
|
|
71
|
-
commandNames.add(name);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
result.commands = Array.from(commandNames);
|
|
75
|
-
// Skills (directories, excluding hidden)
|
|
76
|
-
const skillNames = new Set();
|
|
77
|
-
for (const { base } of resourceBases) {
|
|
78
|
-
const skillsDir = path.join(base, 'skills');
|
|
79
|
-
if (!fs.existsSync(skillsDir))
|
|
80
|
-
continue;
|
|
81
|
-
const names = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
82
|
-
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
83
|
-
.map(d => d.name);
|
|
84
|
-
for (const name of names) {
|
|
85
|
-
skillNames.add(name);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
result.skills = Array.from(skillNames);
|
|
89
|
-
// Hooks (files)
|
|
90
|
-
const hookNames = new Set();
|
|
91
|
-
for (const { base } of resourceBases) {
|
|
92
|
-
const hooksDir = path.join(base, 'hooks');
|
|
93
|
-
if (!fs.existsSync(hooksDir))
|
|
94
|
-
continue;
|
|
95
|
-
const names = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
|
|
96
|
-
for (const name of names) {
|
|
97
|
-
hookNames.add(name);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
result.hooks = Array.from(hookNames);
|
|
101
|
-
// Memory (*.md files, excluding symlinks)
|
|
102
|
-
const memoryNames = new Set();
|
|
103
|
-
for (const { base } of resourceBases) {
|
|
104
|
-
const memoryDir = path.join(base, 'memory');
|
|
105
|
-
if (!fs.existsSync(memoryDir))
|
|
106
|
-
continue;
|
|
107
|
-
const names = fs.readdirSync(memoryDir)
|
|
108
|
-
.filter(f => {
|
|
109
|
-
if (!f.endsWith('.md'))
|
|
110
|
-
return false;
|
|
111
|
-
const stat = fs.lstatSync(path.join(memoryDir, f));
|
|
112
|
-
return !stat.isSymbolicLink();
|
|
113
|
-
})
|
|
114
|
-
.map(f => f.replace(/\.md$/, ''));
|
|
115
|
-
for (const name of names) {
|
|
116
|
-
memoryNames.add(name);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
result.memory = Array.from(memoryNames);
|
|
120
|
-
// MCP servers (*.yaml files)
|
|
121
|
-
const mcpNames = new Set();
|
|
122
|
-
for (const { base } of resourceBases) {
|
|
123
|
-
const mcpDir = path.join(base, 'mcp');
|
|
124
|
-
if (!fs.existsSync(mcpDir))
|
|
125
|
-
continue;
|
|
126
|
-
const names = fs.readdirSync(mcpDir)
|
|
127
|
-
.filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
|
|
128
|
-
.map(f => f.replace(/\.(yaml|yml)$/, ''));
|
|
129
|
-
for (const name of names) {
|
|
130
|
-
mcpNames.add(name);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
result.mcp = Array.from(mcpNames);
|
|
134
|
-
// Permission groups (from permissions/groups/*.yaml)
|
|
135
|
-
const permissionNames = new Set();
|
|
136
|
-
for (const { base } of resourceBases) {
|
|
137
|
-
const permsGroupsDir = path.join(base, 'permissions', 'groups');
|
|
138
|
-
if (!fs.existsSync(permsGroupsDir))
|
|
139
|
-
continue;
|
|
140
|
-
const names = fs.readdirSync(permsGroupsDir)
|
|
141
|
-
.filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
|
|
142
|
-
.map(f => f.replace(/\.(yaml|yml)$/, ''));
|
|
143
|
-
for (const name of names) {
|
|
144
|
-
permissionNames.add(name);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
result.permissions = Array.from(permissionNames);
|
|
148
|
-
// Subagents (directories with AGENT.md)
|
|
149
|
-
const subagentNames = new Set();
|
|
150
|
-
for (const { base } of resourceBases) {
|
|
151
|
-
const subagentsDir = path.join(base, 'subagents');
|
|
152
|
-
if (!fs.existsSync(subagentsDir))
|
|
153
|
-
continue;
|
|
154
|
-
const names = fs.readdirSync(subagentsDir, { withFileTypes: true })
|
|
155
|
-
.filter(d => d.isDirectory() && fs.existsSync(path.join(subagentsDir, d.name, 'AGENT.md')))
|
|
156
|
-
.map(d => d.name);
|
|
157
|
-
for (const name of names) {
|
|
158
|
-
subagentNames.add(name);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
result.subagents = Array.from(subagentNames);
|
|
162
|
-
// Plugins (directories with .claude-plugin/plugin.json)
|
|
163
|
-
const allPlugins = discoverPlugins();
|
|
164
|
-
result.plugins = allPlugins.map(p => p.name);
|
|
165
|
-
// Promptcuts — single file at ~/.agents/promptcuts.yaml, not per-agent.
|
|
166
|
-
// Project-scoped .agents/promptcuts.yaml is intentionally not supported
|
|
167
|
-
// (user-global shortcuts only — they follow the user, not the repo).
|
|
168
|
-
result.promptcuts = fs.existsSync(getPromptcutsPath());
|
|
169
|
-
return result;
|
|
170
|
-
}
|
|
171
|
-
/**
|
|
172
|
-
* Recursively compare two directories: every file in src must exist in dest with identical content.
|
|
173
|
-
*/
|
|
174
|
-
function skillDirsMatch(src, dest) {
|
|
175
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
176
|
-
for (const entry of entries) {
|
|
177
|
-
const srcPath = path.join(src, entry.name);
|
|
178
|
-
const destPath = path.join(dest, entry.name);
|
|
179
|
-
if (entry.isDirectory()) {
|
|
180
|
-
if (!fs.existsSync(destPath))
|
|
181
|
-
return false;
|
|
182
|
-
if (!skillDirsMatch(srcPath, destPath))
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
else {
|
|
186
|
-
if (!fs.existsSync(destPath))
|
|
187
|
-
return false;
|
|
188
|
-
const srcContent = fs.readFileSync(srcPath, 'utf-8');
|
|
189
|
-
const destContent = fs.readFileSync(destPath, 'utf-8');
|
|
190
|
-
if (srcContent !== destContent)
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return true;
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Get what's ACTUALLY synced to a version by inspecting the version home.
|
|
198
|
-
* This is the source of truth - not the tracking in agents.yaml.
|
|
199
|
-
*/
|
|
200
|
-
export function getActuallySyncedResources(agent, version, options = {}) {
|
|
201
|
-
const agentConfig = AGENTS[agent];
|
|
202
|
-
const versionHome = path.join(getVersionsDir(), agent, version, 'home');
|
|
203
|
-
const configDir = path.join(versionHome, `.${agent}`);
|
|
204
|
-
const projectAgentsDir = getProjectAgentsDir(options.cwd || process.cwd());
|
|
205
|
-
const result = {
|
|
206
|
-
commands: [],
|
|
207
|
-
skills: [],
|
|
208
|
-
hooks: [],
|
|
209
|
-
memory: [],
|
|
210
|
-
mcp: [],
|
|
211
|
-
permissions: [],
|
|
212
|
-
subagents: [],
|
|
213
|
-
plugins: [],
|
|
214
|
-
promptcuts: false,
|
|
215
|
-
};
|
|
216
|
-
// Commands - check what files exist in version home
|
|
217
|
-
const commandsDir = path.join(configDir, agentConfig.commandsSubdir);
|
|
218
|
-
if (fs.existsSync(commandsDir)) {
|
|
219
|
-
const ext = agentConfig.format === 'toml' ? '.toml' : '.md';
|
|
220
|
-
result.commands = fs.readdirSync(commandsDir)
|
|
221
|
-
.filter(f => f.endsWith(ext))
|
|
222
|
-
.map(f => f.replace(new RegExp(`\\${ext}$`), ''));
|
|
223
|
-
}
|
|
224
|
-
// Skills - check what directories exist AND content matches central source
|
|
225
|
-
const skillsDir = path.join(configDir, 'skills');
|
|
226
|
-
const centralSkillsDir = getSkillsDir();
|
|
227
|
-
const projectSkillsDir = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
|
|
228
|
-
if (fs.existsSync(skillsDir)) {
|
|
229
|
-
const installedSkills = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
230
|
-
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
231
|
-
.map(d => d.name);
|
|
232
|
-
for (const skill of installedSkills) {
|
|
233
|
-
const versionSkillDir = path.join(skillsDir, skill);
|
|
234
|
-
const projectSourceDir = projectSkillsDir ? path.join(projectSkillsDir, skill) : null;
|
|
235
|
-
const centralSkillDir = path.join(centralSkillsDir, skill);
|
|
236
|
-
const hasProjectSource = projectSourceDir ? fs.existsSync(projectSourceDir) : false;
|
|
237
|
-
const hasUserSource = fs.existsSync(centralSkillDir);
|
|
238
|
-
if (!hasProjectSource && !hasUserSource) {
|
|
239
|
-
result.skills.push(skill);
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
const sourceDir = hasProjectSource ? projectSourceDir : centralSkillDir;
|
|
243
|
-
const allMatch = skillDirsMatch(sourceDir, versionSkillDir);
|
|
244
|
-
if (allMatch) {
|
|
245
|
-
result.skills.push(skill);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
// Hooks - check what files exist AND content matches central source
|
|
250
|
-
const hooksDir = path.join(configDir, 'hooks');
|
|
251
|
-
const centralHooksDir = getHooksDir();
|
|
252
|
-
const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
|
|
253
|
-
if (fs.existsSync(hooksDir)) {
|
|
254
|
-
const installedHooks = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
|
|
255
|
-
for (const hook of installedHooks) {
|
|
256
|
-
const projectFile = projectHooksDir ? path.join(projectHooksDir, hook) : null;
|
|
257
|
-
const centralFile = path.join(centralHooksDir, hook);
|
|
258
|
-
const versionFile = path.join(hooksDir, hook);
|
|
259
|
-
const hasProject = projectFile ? fs.existsSync(projectFile) : false;
|
|
260
|
-
const hasCentral = fs.existsSync(centralFile);
|
|
261
|
-
const sourceFile = hasProject ? projectFile : centralFile;
|
|
262
|
-
if (!hasProject && !hasCentral) {
|
|
263
|
-
result.hooks.push(hook);
|
|
264
|
-
continue;
|
|
265
|
-
}
|
|
266
|
-
try {
|
|
267
|
-
const centralContent = fs.readFileSync(sourceFile, 'utf-8');
|
|
268
|
-
const versionContent = fs.readFileSync(versionFile, 'utf-8');
|
|
269
|
-
if (centralContent === versionContent) {
|
|
270
|
-
result.hooks.push(hook);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
catch {
|
|
274
|
-
// If read fails, consider not synced
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
// Memory - check which memory files are actually in sync (content matches)
|
|
279
|
-
const memoryDir = getMemoryDir();
|
|
280
|
-
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, 'memory') : null;
|
|
281
|
-
const memoryFiles = new Set();
|
|
282
|
-
if (fs.existsSync(memoryDir)) {
|
|
283
|
-
fs.readdirSync(memoryDir).filter(f => f.endsWith('.md')).forEach(f => memoryFiles.add(f));
|
|
284
|
-
}
|
|
285
|
-
if (projectMemoryDir && fs.existsSync(projectMemoryDir)) {
|
|
286
|
-
fs.readdirSync(projectMemoryDir).filter(f => f.endsWith('.md')).forEach(f => memoryFiles.add(f));
|
|
287
|
-
}
|
|
288
|
-
for (const file of memoryFiles) {
|
|
289
|
-
const memName = file.replace(/\.md$/, '');
|
|
290
|
-
const targetName = file === 'AGENTS.md' ? agentConfig.instructionsFile : file;
|
|
291
|
-
const versionFile = path.join(configDir, targetName);
|
|
292
|
-
if (!fs.existsSync(versionFile))
|
|
293
|
-
continue;
|
|
294
|
-
const projectFile = projectMemoryDir ? path.join(projectMemoryDir, file) : null;
|
|
295
|
-
const centralFile = path.join(memoryDir, file);
|
|
296
|
-
const hasProject = projectFile ? fs.existsSync(projectFile) : false;
|
|
297
|
-
const hasCentral = fs.existsSync(centralFile);
|
|
298
|
-
const sourceFile = hasProject ? projectFile : centralFile;
|
|
299
|
-
if (!hasProject && !hasCentral) {
|
|
300
|
-
result.memory.push(memName);
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
try {
|
|
304
|
-
const centralContent = fs.readFileSync(sourceFile, 'utf-8');
|
|
305
|
-
const versionContent = fs.readFileSync(versionFile, 'utf-8');
|
|
306
|
-
if (centralContent === versionContent) {
|
|
307
|
-
result.memory.push(memName);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
catch {
|
|
311
|
-
// Ignore
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
// MCP - use canonical config path + parser per agent
|
|
315
|
-
if (MCP_CAPABLE_AGENTS.includes(agent)) {
|
|
316
|
-
const mcpConfigPath = getMcpConfigPathForHome(agent, versionHome);
|
|
317
|
-
if (fs.existsSync(mcpConfigPath)) {
|
|
318
|
-
try {
|
|
319
|
-
const servers = parseMcpConfig(agent, mcpConfigPath);
|
|
320
|
-
result.mcp = Object.keys(servers);
|
|
321
|
-
}
|
|
322
|
-
catch {
|
|
323
|
-
// Ignore parse errors
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
// Permissions - check agent-specific config files
|
|
328
|
-
const settingsPath = path.join(configDir, 'settings.json');
|
|
329
|
-
if (PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
|
|
330
|
-
if (agent === 'claude' && fs.existsSync(settingsPath)) {
|
|
331
|
-
// Claude: check settings.json permissions.allow and deny
|
|
332
|
-
try {
|
|
333
|
-
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
334
|
-
const allowRules = settings.permissions?.allow || [];
|
|
335
|
-
const denyRules = settings.permissions?.deny || [];
|
|
336
|
-
if (allowRules.length > 0 || denyRules.length > 0) {
|
|
337
|
-
const permGroups = discoverPermissionGroups();
|
|
338
|
-
const appliedGroups = [];
|
|
339
|
-
for (const group of permGroups) {
|
|
340
|
-
const groupSet = buildPermissionsFromGroups([group.name]);
|
|
341
|
-
// Empty groups (like header files) are considered synced if ANY permissions are applied
|
|
342
|
-
if (groupSet.allow.length === 0 && (!groupSet.deny || groupSet.deny.length === 0)) {
|
|
343
|
-
appliedGroups.push(group.name);
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
const hasAllowRule = groupSet.allow.some(rule => allowRules.includes(rule));
|
|
347
|
-
const hasDenyRule = groupSet.deny?.some(rule => denyRules.includes(rule)) || false;
|
|
348
|
-
if (hasAllowRule || hasDenyRule) {
|
|
349
|
-
appliedGroups.push(group.name);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
result.permissions = appliedGroups;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
catch {
|
|
356
|
-
// Ignore parse errors
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
else if (agent === 'codex') {
|
|
360
|
-
// Codex: config.toml for approval_policy/sandbox_mode, .rules for deny
|
|
361
|
-
const codexConfigPath = path.join(configDir, 'config.toml');
|
|
362
|
-
const codexRulesPath = path.join(configDir, 'rules', CODEX_RULES_FILENAME);
|
|
363
|
-
const hasConfig = fs.existsSync(codexConfigPath);
|
|
364
|
-
const hasRules = fs.existsSync(codexRulesPath);
|
|
365
|
-
if (hasConfig || hasRules) {
|
|
366
|
-
try {
|
|
367
|
-
// Codex format is lossy — all groups merge into a few keys.
|
|
368
|
-
// If any permission artifacts exist, all groups were applied together.
|
|
369
|
-
let hasPermKeys = false;
|
|
370
|
-
if (hasConfig) {
|
|
371
|
-
const content = fs.readFileSync(codexConfigPath, 'utf-8');
|
|
372
|
-
const config = TOML.parse(content);
|
|
373
|
-
hasPermKeys = !!(config.approval_policy || config.sandbox_mode || config.sandbox_workspace_write);
|
|
374
|
-
}
|
|
375
|
-
if (hasPermKeys || hasRules) {
|
|
376
|
-
result.permissions = discoverPermissionGroups().map(g => g.name);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
catch {
|
|
380
|
-
// Ignore parse errors
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
else if (agent === 'opencode') {
|
|
385
|
-
// OpenCode: opencode.jsonc for permission.bash
|
|
386
|
-
const opencodeConfigPath = path.join(configDir, 'opencode.jsonc');
|
|
387
|
-
if (fs.existsSync(opencodeConfigPath)) {
|
|
388
|
-
try {
|
|
389
|
-
const content = fs.readFileSync(opencodeConfigPath, 'utf-8');
|
|
390
|
-
const stripped = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
391
|
-
const config = JSON.parse(stripped);
|
|
392
|
-
if (config.permission && Object.keys(config.permission.bash || {}).length > 0) {
|
|
393
|
-
result.permissions = discoverPermissionGroups().map(g => g.name);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
catch {
|
|
397
|
-
// Ignore parse errors
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
// Subagents - check agent-specific locations
|
|
403
|
-
if (SUBAGENT_CAPABLE_AGENTS.includes(agent)) {
|
|
404
|
-
if (agent === 'claude') {
|
|
405
|
-
const agentsDir = path.join(configDir, 'agents');
|
|
406
|
-
if (fs.existsSync(agentsDir)) {
|
|
407
|
-
result.subagents = fs.readdirSync(agentsDir)
|
|
408
|
-
.filter(f => f.endsWith('.md'))
|
|
409
|
-
.map(f => f.replace('.md', ''));
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
else if (agent === 'openclaw') {
|
|
413
|
-
// OpenClaw: directories with AGENTS.md
|
|
414
|
-
const openclawDir = path.join(versionHome, '.openclaw');
|
|
415
|
-
if (fs.existsSync(openclawDir)) {
|
|
416
|
-
result.subagents = fs.readdirSync(openclawDir, { withFileTypes: true })
|
|
417
|
-
.filter(d => d.isDirectory() && fs.existsSync(path.join(openclawDir, d.name, 'AGENTS.md')))
|
|
418
|
-
.map(d => d.name);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
// Plugins - check which discovered plugins have their skills in the version
|
|
423
|
-
if (PLUGINS_CAPABLE_AGENTS.includes(agent)) {
|
|
424
|
-
const allPlugins = discoverPlugins();
|
|
425
|
-
for (const plugin of allPlugins) {
|
|
426
|
-
if (isPluginSynced(plugin, agent, versionHome)) {
|
|
427
|
-
result.plugins.push(plugin.name);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
return result;
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* Compare available resources with what's ACTUALLY synced to version home.
|
|
435
|
-
* Returns only NEW resources that haven't been synced yet.
|
|
436
|
-
* Source of truth: the actual files/config, NOT agents.yaml tracking.
|
|
437
|
-
*/
|
|
438
|
-
export function getNewResources(available, actuallySynced) {
|
|
439
|
-
return {
|
|
440
|
-
commands: available.commands.filter(c => !actuallySynced.commands.includes(c)),
|
|
441
|
-
skills: available.skills.filter(s => !actuallySynced.skills.includes(s)),
|
|
442
|
-
hooks: available.hooks.filter(h => !actuallySynced.hooks.includes(h)),
|
|
443
|
-
memory: available.memory.filter(m => !actuallySynced.memory.includes(m)),
|
|
444
|
-
mcp: available.mcp.filter(m => !actuallySynced.mcp.includes(m)),
|
|
445
|
-
permissions: available.permissions.filter(p => !actuallySynced.permissions.includes(p)),
|
|
446
|
-
subagents: available.subagents.filter(s => !actuallySynced.subagents.includes(s)),
|
|
447
|
-
plugins: available.plugins.filter(p => !actuallySynced.plugins.includes(p)),
|
|
448
|
-
// Promptcuts aren't version-scoped — the hook reads ~/.agents/promptcuts.yaml
|
|
449
|
-
// directly, so there is never a "new" per-version state to reconcile.
|
|
450
|
-
promptcuts: false,
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* Check if there are any new resources to sync.
|
|
455
|
-
*/
|
|
456
|
-
export function hasNewResources(diff, agent) {
|
|
457
|
-
const commandsApply = agent ? COMMANDS_CAPABLE_AGENTS.includes(agent) : true;
|
|
458
|
-
const hooksApply = agent ? AGENTS[agent].supportsHooks : true;
|
|
459
|
-
const mcpApply = agent ? MCP_CAPABLE_AGENTS.includes(agent) : true;
|
|
460
|
-
const permsApply = agent ? PERMISSIONS_CAPABLE_AGENTS.includes(agent) : true;
|
|
461
|
-
const subagentsApply = agent ? SUBAGENT_CAPABLE_AGENTS.includes(agent) : true;
|
|
462
|
-
const pluginsApply = agent ? PLUGINS_CAPABLE_AGENTS.includes(agent) : true;
|
|
463
|
-
return ((diff.commands.length > 0 && commandsApply) ||
|
|
464
|
-
diff.skills.length > 0 ||
|
|
465
|
-
(diff.hooks.length > 0 && hooksApply) ||
|
|
466
|
-
(diff.memory.length > 0 && commandsApply) ||
|
|
467
|
-
(diff.mcp.length > 0 && mcpApply) ||
|
|
468
|
-
(diff.permissions.length > 0 && permsApply) ||
|
|
469
|
-
(diff.subagents.length > 0 && subagentsApply) ||
|
|
470
|
-
(diff.plugins.length > 0 && pluginsApply));
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Build a summary string of new resources.
|
|
474
|
-
* E.g., "2 commands, 5 permission groups"
|
|
475
|
-
*/
|
|
476
|
-
function buildNewResourcesSummary(newResources, agent) {
|
|
477
|
-
const agentConfig = AGENTS[agent];
|
|
478
|
-
const parts = [];
|
|
479
|
-
if (newResources.commands.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
480
|
-
parts.push(`${newResources.commands.length} command${newResources.commands.length === 1 ? '' : 's'}`);
|
|
481
|
-
}
|
|
482
|
-
if (newResources.skills.length > 0) {
|
|
483
|
-
parts.push(`${newResources.skills.length} skill${newResources.skills.length === 1 ? '' : 's'}`);
|
|
484
|
-
}
|
|
485
|
-
if (newResources.hooks.length > 0 && agentConfig.supportsHooks) {
|
|
486
|
-
parts.push(`${newResources.hooks.length} hook${newResources.hooks.length === 1 ? '' : 's'}`);
|
|
487
|
-
}
|
|
488
|
-
if (newResources.memory.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
489
|
-
parts.push(`${newResources.memory.length} rule file${newResources.memory.length === 1 ? '' : 's'}`);
|
|
490
|
-
}
|
|
491
|
-
if (newResources.mcp.length > 0 && MCP_CAPABLE_AGENTS.includes(agent)) {
|
|
492
|
-
parts.push(`${newResources.mcp.length} MCP${newResources.mcp.length === 1 ? '' : 's'}`);
|
|
493
|
-
}
|
|
494
|
-
if (newResources.permissions.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
|
|
495
|
-
parts.push(`${newResources.permissions.length} permission group${newResources.permissions.length === 1 ? '' : 's'}`);
|
|
496
|
-
}
|
|
497
|
-
if (newResources.subagents.length > 0 && SUBAGENT_CAPABLE_AGENTS.includes(agent)) {
|
|
498
|
-
parts.push(`${newResources.subagents.length} subagent${newResources.subagents.length === 1 ? '' : 's'}`);
|
|
499
|
-
}
|
|
500
|
-
if (newResources.plugins.length > 0 && PLUGINS_CAPABLE_AGENTS.includes(agent)) {
|
|
501
|
-
parts.push(`${newResources.plugins.length} plugin${newResources.plugins.length === 1 ? '' : 's'}`);
|
|
502
|
-
}
|
|
503
|
-
return parts.join(', ');
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Prompt user to select which NEW resources to sync.
|
|
507
|
-
* Only shows resources that haven't been synced yet.
|
|
508
|
-
*/
|
|
509
|
-
export async function promptNewResourceSelection(agent, newResources) {
|
|
510
|
-
const agentConfig = AGENTS[agent];
|
|
511
|
-
const selection = {};
|
|
512
|
-
// Get permission group info for display
|
|
513
|
-
const permissionGroups = discoverPermissionGroups();
|
|
514
|
-
const newPermissionGroups = permissionGroups.filter(g => newResources.permissions.includes(g.name));
|
|
515
|
-
const totalNewPermissionRules = newPermissionGroups.reduce((sum, g) => sum + g.ruleCount, 0);
|
|
516
|
-
// Build the summary
|
|
517
|
-
const summary = buildNewResourcesSummary(newResources, agent);
|
|
518
|
-
console.log(chalk.cyan(`\nNew resources available:`));
|
|
519
|
-
console.log(chalk.gray(` ${summary}`));
|
|
520
|
-
// Ask how to handle new resources
|
|
521
|
-
const action = await select({
|
|
522
|
-
message: 'Sync new resources?',
|
|
523
|
-
choices: [
|
|
524
|
-
{ value: 'all', name: 'Yes, sync all new' },
|
|
525
|
-
{ value: 'specific', name: 'Select specific items' },
|
|
526
|
-
{ value: 'skip', name: 'Skip' },
|
|
527
|
-
],
|
|
528
|
-
default: 'all',
|
|
529
|
-
});
|
|
530
|
-
if (action === 'skip') {
|
|
531
|
-
return null;
|
|
532
|
-
}
|
|
533
|
-
if (action === 'all') {
|
|
534
|
-
// Sync all new resources
|
|
535
|
-
if (newResources.commands.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent))
|
|
536
|
-
selection.commands = newResources.commands;
|
|
537
|
-
if (newResources.skills.length > 0)
|
|
538
|
-
selection.skills = newResources.skills;
|
|
539
|
-
if (newResources.hooks.length > 0 && agentConfig.supportsHooks)
|
|
540
|
-
selection.hooks = newResources.hooks;
|
|
541
|
-
if (newResources.memory.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent))
|
|
542
|
-
selection.memory = newResources.memory;
|
|
543
|
-
if (newResources.mcp.length > 0 && MCP_CAPABLE_AGENTS.includes(agent))
|
|
544
|
-
selection.mcp = newResources.mcp;
|
|
545
|
-
if (newResources.permissions.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent))
|
|
546
|
-
selection.permissions = newResources.permissions;
|
|
547
|
-
if (newResources.subagents.length > 0 && SUBAGENT_CAPABLE_AGENTS.includes(agent))
|
|
548
|
-
selection.subagents = newResources.subagents;
|
|
549
|
-
if (newResources.plugins.length > 0 && PLUGINS_CAPABLE_AGENTS.includes(agent))
|
|
550
|
-
selection.plugins = newResources.plugins;
|
|
551
|
-
return selection;
|
|
552
|
-
}
|
|
553
|
-
// Select specific items for each category
|
|
554
|
-
if (newResources.commands.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
555
|
-
const selected = await checkbox({
|
|
556
|
-
message: 'Select new commands to sync:',
|
|
557
|
-
choices: newResources.commands.map(c => ({ name: c, value: c, checked: true })),
|
|
558
|
-
});
|
|
559
|
-
if (selected.length > 0)
|
|
560
|
-
selection.commands = selected;
|
|
561
|
-
}
|
|
562
|
-
if (newResources.skills.length > 0) {
|
|
563
|
-
const selected = await checkbox({
|
|
564
|
-
message: 'Select new skills to sync:',
|
|
565
|
-
choices: newResources.skills.map(s => ({ name: s, value: s, checked: true })),
|
|
566
|
-
});
|
|
567
|
-
if (selected.length > 0)
|
|
568
|
-
selection.skills = selected;
|
|
569
|
-
}
|
|
570
|
-
if (newResources.hooks.length > 0 && agentConfig.supportsHooks) {
|
|
571
|
-
const selected = await checkbox({
|
|
572
|
-
message: 'Select new hooks to sync:',
|
|
573
|
-
choices: newResources.hooks.map(h => ({ name: h, value: h, checked: true })),
|
|
574
|
-
});
|
|
575
|
-
if (selected.length > 0)
|
|
576
|
-
selection.hooks = selected;
|
|
577
|
-
}
|
|
578
|
-
if (newResources.memory.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
579
|
-
const selected = await checkbox({
|
|
580
|
-
message: 'Select new rule files to sync:',
|
|
581
|
-
choices: newResources.memory.map(m => ({ name: m, value: m, checked: true })),
|
|
582
|
-
});
|
|
583
|
-
if (selected.length > 0)
|
|
584
|
-
selection.memory = selected;
|
|
585
|
-
}
|
|
586
|
-
if (newResources.mcp.length > 0 && MCP_CAPABLE_AGENTS.includes(agent)) {
|
|
587
|
-
const selected = await checkbox({
|
|
588
|
-
message: 'Select new MCPs to sync:',
|
|
589
|
-
choices: newResources.mcp.map(m => ({ name: m, value: m, checked: true })),
|
|
590
|
-
});
|
|
591
|
-
if (selected.length > 0)
|
|
592
|
-
selection.mcp = selected;
|
|
593
|
-
}
|
|
594
|
-
if (newResources.permissions.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
|
|
595
|
-
const selected = await checkbox({
|
|
596
|
-
message: 'Select new permission groups to sync:',
|
|
597
|
-
choices: newPermissionGroups.map(g => ({
|
|
598
|
-
name: `${g.name} (${g.ruleCount} rules)`,
|
|
599
|
-
value: g.name,
|
|
600
|
-
checked: true,
|
|
601
|
-
})),
|
|
602
|
-
});
|
|
603
|
-
if (selected.length > 0)
|
|
604
|
-
selection.permissions = selected;
|
|
605
|
-
}
|
|
606
|
-
if (newResources.subagents.length > 0 && SUBAGENT_CAPABLE_AGENTS.includes(agent)) {
|
|
607
|
-
const selected = await checkbox({
|
|
608
|
-
message: 'Select new subagents to sync:',
|
|
609
|
-
choices: newResources.subagents.map(s => ({ name: s, value: s, checked: true })),
|
|
610
|
-
});
|
|
611
|
-
if (selected.length > 0)
|
|
612
|
-
selection.subagents = selected;
|
|
613
|
-
}
|
|
614
|
-
if (newResources.plugins.length > 0 && PLUGINS_CAPABLE_AGENTS.includes(agent)) {
|
|
615
|
-
const allPlugins = discoverPlugins();
|
|
616
|
-
const pluginMap = new Map(allPlugins.map(p => [p.name, p]));
|
|
617
|
-
const selected = await checkbox({
|
|
618
|
-
message: 'Select new plugins to sync:',
|
|
619
|
-
choices: newResources.plugins.map(name => {
|
|
620
|
-
const plugin = pluginMap.get(name);
|
|
621
|
-
const desc = plugin?.manifest.description;
|
|
622
|
-
return { name: desc ? `${name} - ${desc}` : name, value: name, checked: true };
|
|
623
|
-
}),
|
|
624
|
-
});
|
|
625
|
-
if (selected.length > 0)
|
|
626
|
-
selection.plugins = selected;
|
|
627
|
-
}
|
|
628
|
-
return selection;
|
|
629
|
-
}
|
|
630
|
-
/**
|
|
631
|
-
* Prompt user to select which resources to sync from ~/.agents/.
|
|
632
|
-
* Returns the selection, or null if user cancels.
|
|
633
|
-
*/
|
|
634
|
-
export async function promptResourceSelection(agent) {
|
|
635
|
-
const available = getAvailableResources();
|
|
636
|
-
const agentConfig = AGENTS[agent];
|
|
637
|
-
const selection = {};
|
|
638
|
-
// Get permission group info for display
|
|
639
|
-
const permissionGroups = discoverPermissionGroups();
|
|
640
|
-
const totalPermissionRules = permissionGroups.reduce((sum, g) => sum + g.ruleCount, 0);
|
|
641
|
-
const categories = [
|
|
642
|
-
{ key: 'commands', label: 'Commands', available: COMMANDS_CAPABLE_AGENTS.includes(agent) && available.commands.length > 0, displayCount: `${available.commands.length} available` },
|
|
643
|
-
{ key: 'skills', label: 'Skills', available: available.skills.length > 0, displayCount: `${available.skills.length} available` },
|
|
644
|
-
{ key: 'hooks', label: 'Hooks', available: agentConfig.supportsHooks && available.hooks.length > 0, displayCount: `${available.hooks.length} available` },
|
|
645
|
-
{ key: 'memory', label: 'Rules', available: COMMANDS_CAPABLE_AGENTS.includes(agent) && available.memory.length > 0, displayCount: `${available.memory.length} available` },
|
|
646
|
-
{ key: 'mcp', label: 'MCPs', available: MCP_CAPABLE_AGENTS.includes(agent) && available.mcp.length > 0, displayCount: `${available.mcp.length} available` },
|
|
647
|
-
{ key: 'permissions', label: 'Permissions', available: PERMISSIONS_CAPABLE_AGENTS.includes(agent) && permissionGroups.length > 0, displayCount: `${permissionGroups.length} groups, ${totalPermissionRules} rules` },
|
|
648
|
-
{ key: 'subagents', label: 'Subagents', available: SUBAGENT_CAPABLE_AGENTS.includes(agent) && available.subagents.length > 0, displayCount: `${available.subagents.length} available` },
|
|
649
|
-
{ key: 'plugins', label: 'Plugins', available: PLUGINS_CAPABLE_AGENTS.includes(agent) && available.plugins.length > 0, displayCount: `${available.plugins.length} available` },
|
|
650
|
-
];
|
|
651
|
-
const availableCategories = categories.filter(c => c.available);
|
|
652
|
-
if (availableCategories.length === 0) {
|
|
653
|
-
console.log(chalk.gray('No resources available in ~/.agents/'));
|
|
654
|
-
return {};
|
|
655
|
-
}
|
|
656
|
-
// Step 1: Select categories (with "Select All" shortcut at the top)
|
|
657
|
-
console.log();
|
|
658
|
-
const SELECT_ALL_KEY = '__select_all__';
|
|
659
|
-
const selectedCategories = await checkbox({
|
|
660
|
-
message: 'Which resources from ~/.agents/ would you like to sync?',
|
|
661
|
-
choices: [
|
|
662
|
-
{ name: chalk.bold('Select All (sync everything)'), value: SELECT_ALL_KEY, checked: false },
|
|
663
|
-
...availableCategories.map(c => ({
|
|
664
|
-
name: `${c.label} (${c.displayCount})`,
|
|
665
|
-
value: c.key,
|
|
666
|
-
checked: true, // Default all checked
|
|
667
|
-
})),
|
|
668
|
-
],
|
|
669
|
-
});
|
|
670
|
-
if (selectedCategories.length === 0) {
|
|
671
|
-
return {};
|
|
672
|
-
}
|
|
673
|
-
// If "Select All" was picked, sync everything without per-category prompts
|
|
674
|
-
if (selectedCategories.includes(SELECT_ALL_KEY)) {
|
|
675
|
-
for (const c of availableCategories) {
|
|
676
|
-
selection[c.key] = 'all';
|
|
677
|
-
}
|
|
678
|
-
return selection;
|
|
679
|
-
}
|
|
680
|
-
// Step 2: For each selected category, ask all/specific/skip
|
|
681
|
-
for (const category of selectedCategories) {
|
|
682
|
-
const categoryLabel = categories.find(c => c.key === category).label;
|
|
683
|
-
// Special handling for permissions - show groups
|
|
684
|
-
if (category === 'permissions') {
|
|
685
|
-
const choice = await select({
|
|
686
|
-
message: `${categoryLabel}:`,
|
|
687
|
-
choices: [
|
|
688
|
-
{ name: `Select all (${permissionGroups.length} groups)`, value: 'all' },
|
|
689
|
-
{ name: 'Select specific groups', value: 'specific' },
|
|
690
|
-
{ name: 'Skip', value: 'skip' },
|
|
691
|
-
],
|
|
692
|
-
default: 'all',
|
|
693
|
-
});
|
|
694
|
-
if (choice === 'all') {
|
|
695
|
-
selection.permissions = 'all';
|
|
696
|
-
}
|
|
697
|
-
else if (choice === 'specific') {
|
|
698
|
-
const selected = await checkbox({
|
|
699
|
-
message: 'Select permission groups to sync:',
|
|
700
|
-
choices: permissionGroups.map(g => ({
|
|
701
|
-
name: `${g.name} (${g.ruleCount} rules)`,
|
|
702
|
-
value: g.name,
|
|
703
|
-
checked: true,
|
|
704
|
-
})),
|
|
705
|
-
});
|
|
706
|
-
if (selected.length > 0) {
|
|
707
|
-
selection.permissions = selected;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
else {
|
|
712
|
-
// Standard handling for other categories
|
|
713
|
-
const items = available[category];
|
|
714
|
-
const choice = await select({
|
|
715
|
-
message: `${categoryLabel}:`,
|
|
716
|
-
choices: [
|
|
717
|
-
{ name: `Select all (${items.length})`, value: 'all' },
|
|
718
|
-
{ name: 'Select specific', value: 'specific' },
|
|
719
|
-
{ name: 'Skip', value: 'skip' },
|
|
720
|
-
],
|
|
721
|
-
default: 'all',
|
|
722
|
-
});
|
|
723
|
-
if (choice === 'all') {
|
|
724
|
-
selection[category] = 'all';
|
|
725
|
-
}
|
|
726
|
-
else if (choice === 'specific') {
|
|
727
|
-
const selected = await checkbox({
|
|
728
|
-
message: `Select ${categoryLabel.toLowerCase()} to sync:`,
|
|
729
|
-
choices: items.map(item => ({
|
|
730
|
-
name: item,
|
|
731
|
-
value: item,
|
|
732
|
-
checked: true,
|
|
733
|
-
})),
|
|
734
|
-
});
|
|
735
|
-
if (selected.length > 0) {
|
|
736
|
-
selection[category] = selected;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
// 'skip' means we don't set anything for this category
|
|
741
|
-
}
|
|
742
|
-
return selection;
|
|
743
|
-
}
|
|
744
|
-
/**
|
|
745
|
-
* Parse agent@version syntax.
|
|
746
|
-
* Examples:
|
|
747
|
-
* "claude@1.5.0" -> { agent: "claude", version: "1.5.0" }
|
|
748
|
-
* "claude" -> { agent: "claude", version: "latest" }
|
|
749
|
-
* "codex@latest" -> { agent: "codex", version: "latest" }
|
|
750
|
-
*/
|
|
751
|
-
export function parseAgentSpec(spec) {
|
|
752
|
-
const parts = spec.split('@');
|
|
753
|
-
const agentName = parts[0].toLowerCase();
|
|
754
|
-
const version = parts[1] || 'latest';
|
|
755
|
-
if (!AGENTS[agentName]) {
|
|
756
|
-
return null;
|
|
757
|
-
}
|
|
758
|
-
return {
|
|
759
|
-
agent: agentName,
|
|
760
|
-
version,
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Get the directory where a specific version is installed.
|
|
765
|
-
*/
|
|
766
|
-
export function getVersionDir(agent, version) {
|
|
767
|
-
return path.join(getVersionsDir(), agent, version);
|
|
768
|
-
}
|
|
769
|
-
/**
|
|
770
|
-
* Get the binary path for a specific agent version.
|
|
771
|
-
*/
|
|
772
|
-
export function getBinaryPath(agent, version) {
|
|
773
|
-
const versionDir = getVersionDir(agent, version);
|
|
774
|
-
const agentConfig = AGENTS[agent];
|
|
775
|
-
return path.join(versionDir, 'node_modules', '.bin', agentConfig.cliCommand);
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* Get the isolated HOME directory for a specific agent version.
|
|
779
|
-
* Each version has its own config isolation (like jobs sandbox).
|
|
780
|
-
*/
|
|
781
|
-
export function getVersionHomePath(agent, version) {
|
|
782
|
-
return path.join(getVersionDir(agent, version), 'home');
|
|
783
|
-
}
|
|
784
|
-
/**
|
|
785
|
-
* Check if a specific version is installed.
|
|
786
|
-
*/
|
|
787
|
-
export function isVersionInstalled(agent, version) {
|
|
788
|
-
const binaryPath = getBinaryPath(agent, version);
|
|
789
|
-
return fs.existsSync(binaryPath);
|
|
790
|
-
}
|
|
791
|
-
/**
|
|
792
|
-
* Get the latest available version from npm for an agent.
|
|
793
|
-
*/
|
|
794
|
-
export async function getLatestNpmVersion(agent) {
|
|
795
|
-
const agentConfig = AGENTS[agent];
|
|
796
|
-
if (!agentConfig.npmPackage)
|
|
797
|
-
return null;
|
|
798
|
-
try {
|
|
799
|
-
const { stdout } = await execAsync(`npm view ${agentConfig.npmPackage} version`);
|
|
800
|
-
return stdout.trim();
|
|
801
|
-
}
|
|
802
|
-
catch {
|
|
803
|
-
return null;
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
/**
|
|
807
|
-
* Check if 'latest' version is already installed (by resolving to actual version).
|
|
808
|
-
*/
|
|
809
|
-
export async function isLatestInstalled(agent) {
|
|
810
|
-
const latestVersion = await getLatestNpmVersion(agent);
|
|
811
|
-
if (!latestVersion) {
|
|
812
|
-
return { installed: false, version: null };
|
|
813
|
-
}
|
|
814
|
-
return { installed: isVersionInstalled(agent, latestVersion), version: latestVersion };
|
|
815
|
-
}
|
|
816
|
-
/**
|
|
817
|
-
* List all installed versions for an agent.
|
|
818
|
-
*/
|
|
819
|
-
export function listInstalledVersions(agent) {
|
|
820
|
-
const agentVersionsDir = path.join(getVersionsDir(), agent);
|
|
821
|
-
if (!fs.existsSync(agentVersionsDir)) {
|
|
822
|
-
return [];
|
|
823
|
-
}
|
|
824
|
-
const entries = fs.readdirSync(agentVersionsDir, { withFileTypes: true });
|
|
825
|
-
const versions = [];
|
|
826
|
-
for (const entry of entries) {
|
|
827
|
-
if (entry.isDirectory()) {
|
|
828
|
-
const binaryPath = getBinaryPath(agent, entry.name);
|
|
829
|
-
if (fs.existsSync(binaryPath)) {
|
|
830
|
-
versions.push(entry.name);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
return versions.sort(compareVersions);
|
|
835
|
-
}
|
|
836
|
-
/**
|
|
837
|
-
* Get the global default version for an agent.
|
|
838
|
-
*/
|
|
839
|
-
export function getGlobalDefault(agent) {
|
|
840
|
-
const meta = readMeta();
|
|
841
|
-
return meta.agents?.[agent] || null;
|
|
842
|
-
}
|
|
843
|
-
/**
|
|
844
|
-
* Set the global default version for an agent.
|
|
845
|
-
*/
|
|
846
|
-
export function setGlobalDefault(agent, version) {
|
|
847
|
-
const meta = readMeta();
|
|
848
|
-
if (!meta.agents) {
|
|
849
|
-
meta.agents = {};
|
|
850
|
-
}
|
|
851
|
-
if (version === undefined) {
|
|
852
|
-
delete meta.agents[agent];
|
|
853
|
-
}
|
|
854
|
-
else {
|
|
855
|
-
meta.agents[agent] = version;
|
|
856
|
-
}
|
|
857
|
-
writeMeta(meta);
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* Install a specific version of an agent.
|
|
861
|
-
*/
|
|
862
|
-
export async function installVersion(agent, version, onProgress) {
|
|
863
|
-
const agentConfig = AGENTS[agent];
|
|
864
|
-
if (!agentConfig.npmPackage) {
|
|
865
|
-
return { success: false, installedVersion: version, error: 'Agent has no npm package' };
|
|
866
|
-
}
|
|
867
|
-
ensureAgentsDir();
|
|
868
|
-
const versionDir = getVersionDir(agent, version);
|
|
869
|
-
// Create version directory and isolated home
|
|
870
|
-
fs.mkdirSync(versionDir, { recursive: true });
|
|
871
|
-
fs.mkdirSync(path.join(versionDir, 'home'), { recursive: true });
|
|
872
|
-
// Initialize package.json
|
|
873
|
-
const packageJson = {
|
|
874
|
-
name: `agents-${agent}-${version}`,
|
|
875
|
-
version: '1.0.0',
|
|
876
|
-
private: true,
|
|
877
|
-
};
|
|
878
|
-
fs.writeFileSync(path.join(versionDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
879
|
-
// Install the package
|
|
880
|
-
const packageSpec = version === 'latest'
|
|
881
|
-
? agentConfig.npmPackage
|
|
882
|
-
: `${agentConfig.npmPackage}@${version}`;
|
|
883
|
-
try {
|
|
884
|
-
onProgress?.(`Installing ${packageSpec}...`);
|
|
885
|
-
const { stdout } = await execAsync(`npm install ${packageSpec}`, { cwd: versionDir });
|
|
886
|
-
// Determine the actual installed version
|
|
887
|
-
let installedVersion = version;
|
|
888
|
-
if (version === 'latest') {
|
|
889
|
-
const pkgJsonPath = path.join(versionDir, 'node_modules', agentConfig.npmPackage.replace(/^@/, '').split('/')[0], 'package.json');
|
|
890
|
-
// Try to read the actual version from installed package
|
|
891
|
-
try {
|
|
892
|
-
const installedPkgPath = path.join(versionDir, 'node_modules', agentConfig.npmPackage, 'package.json');
|
|
893
|
-
if (fs.existsSync(installedPkgPath)) {
|
|
894
|
-
const installedPkg = JSON.parse(fs.readFileSync(installedPkgPath, 'utf-8'));
|
|
895
|
-
installedVersion = installedPkg.version;
|
|
896
|
-
// Rename the directory to the actual version
|
|
897
|
-
if (installedVersion !== 'latest') {
|
|
898
|
-
const actualVersionDir = getVersionDir(agent, installedVersion);
|
|
899
|
-
if (!fs.existsSync(actualVersionDir)) {
|
|
900
|
-
fs.renameSync(versionDir, actualVersionDir);
|
|
901
|
-
}
|
|
902
|
-
else {
|
|
903
|
-
// Already exists, remove the 'latest' dir
|
|
904
|
-
fs.rmSync(versionDir, { recursive: true, force: true });
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
catch (e) {
|
|
910
|
-
// Failed to determine version - this shouldn't happen
|
|
911
|
-
throw new Error(`Failed to determine installed version: ${e.message}`);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
// Create versioned alias (e.g., claude@2.0.65)
|
|
915
|
-
createVersionedAlias(agent, installedVersion);
|
|
916
|
-
// Claude reads its global config from CLAUDE_CONFIG_DIR/.claude.json —
|
|
917
|
-
// i.e. inside the per-version .claude dir — while the rest of agents-cli
|
|
918
|
-
// manages the home-level file. Symlink INSIDE to OUTSIDE so Claude and
|
|
919
|
-
// agents-cli see the same content.
|
|
920
|
-
if (agent === 'claude') {
|
|
921
|
-
try {
|
|
922
|
-
ensureClaudeInsideSymlink(installedVersion);
|
|
923
|
-
}
|
|
924
|
-
catch {
|
|
925
|
-
/* non-fatal; the install itself succeeded */
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
return { success: true, installedVersion };
|
|
929
|
-
}
|
|
930
|
-
catch (err) {
|
|
931
|
-
// Clean up on failure
|
|
932
|
-
if (fs.existsSync(versionDir)) {
|
|
933
|
-
fs.rmSync(versionDir, { recursive: true, force: true });
|
|
934
|
-
}
|
|
935
|
-
return { success: false, installedVersion: version, error: err.message };
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
/**
|
|
939
|
-
* Remove a specific version of an agent.
|
|
940
|
-
*/
|
|
941
|
-
export function removeVersion(agent, version) {
|
|
942
|
-
const versionDir = getVersionDir(agent, version);
|
|
943
|
-
if (!fs.existsSync(versionDir)) {
|
|
944
|
-
return false;
|
|
945
|
-
}
|
|
946
|
-
fs.rmSync(versionDir, { recursive: true, force: true });
|
|
947
|
-
// Remove versioned alias (e.g., claude@2.0.65)
|
|
948
|
-
removeVersionedAlias(agent, version);
|
|
949
|
-
// Clear resource tracking for this version
|
|
950
|
-
clearVersionResources(agent, version);
|
|
951
|
-
// Clear default if it was the removed version - user must explicitly pick a new one
|
|
952
|
-
if (getGlobalDefault(agent) === version) {
|
|
953
|
-
const meta = readMeta();
|
|
954
|
-
if (meta.agents?.[agent]) {
|
|
955
|
-
delete meta.agents[agent];
|
|
956
|
-
writeMeta(meta);
|
|
957
|
-
}
|
|
958
|
-
const remaining = listInstalledVersions(agent);
|
|
959
|
-
if (remaining.length > 0) {
|
|
960
|
-
console.log(chalk.yellow(`Default version removed. Run: agents use ${agent}@<version> to set a new default`));
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
// Clean up dangling config symlink if it pointed to the removed version
|
|
964
|
-
const symlinkVersion = getConfigSymlinkVersion(agent);
|
|
965
|
-
if (symlinkVersion === version) {
|
|
966
|
-
const configPath = path.join(os.homedir(), `.${agent}`);
|
|
967
|
-
try {
|
|
968
|
-
fs.unlinkSync(configPath);
|
|
969
|
-
}
|
|
970
|
-
catch {
|
|
971
|
-
// Ignore if already gone
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
return true;
|
|
975
|
-
}
|
|
976
|
-
/**
|
|
977
|
-
* Remove all versions of an agent.
|
|
978
|
-
*/
|
|
979
|
-
export function removeAllVersions(agent) {
|
|
980
|
-
const versions = listInstalledVersions(agent);
|
|
981
|
-
let removed = 0;
|
|
982
|
-
for (const version of versions) {
|
|
983
|
-
if (removeVersion(agent, version)) {
|
|
984
|
-
removed++;
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
// Clean up the agent directory
|
|
988
|
-
const agentDir = path.join(getVersionsDir(), agent);
|
|
989
|
-
if (fs.existsSync(agentDir)) {
|
|
990
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
991
|
-
}
|
|
992
|
-
return removed;
|
|
993
|
-
}
|
|
994
|
-
/**
|
|
995
|
-
* Get the resolved version for an agent in the current context.
|
|
996
|
-
* Checks project manifest first, then global default.
|
|
997
|
-
*/
|
|
998
|
-
export function resolveVersion(agent, projectPath) {
|
|
999
|
-
// Check project manifest
|
|
1000
|
-
if (projectPath) {
|
|
1001
|
-
const version = getProjectVersion(agent, projectPath);
|
|
1002
|
-
if (version) {
|
|
1003
|
-
return version;
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
// Fall back to global default
|
|
1007
|
-
return getGlobalDefault(agent);
|
|
1008
|
-
}
|
|
1009
|
-
/**
|
|
1010
|
-
* Get version specified in a project-root agents.yaml (not the user ~/.agents/agents.yaml).
|
|
1011
|
-
*/
|
|
1012
|
-
export function getProjectVersion(agent, startPath) {
|
|
1013
|
-
const userAgentsYaml = path.join(os.homedir(), '.agents', 'agents.yaml');
|
|
1014
|
-
let dir = path.resolve(startPath);
|
|
1015
|
-
while (dir !== path.dirname(dir)) {
|
|
1016
|
-
const manifestPath = path.join(dir, 'agents.yaml');
|
|
1017
|
-
if (manifestPath !== userAgentsYaml && fs.existsSync(manifestPath)) {
|
|
1018
|
-
try {
|
|
1019
|
-
const content = fs.readFileSync(manifestPath, 'utf-8');
|
|
1020
|
-
const parsed = yaml.parse(content);
|
|
1021
|
-
const version = parsed?.agents?.[agent];
|
|
1022
|
-
if (typeof version === 'string' && version.trim()) {
|
|
1023
|
-
return version.trim();
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
catch {
|
|
1027
|
-
// Ignore parsing errors
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
dir = path.dirname(dir);
|
|
1031
|
-
}
|
|
1032
|
-
return null;
|
|
1033
|
-
}
|
|
1034
|
-
/**
|
|
1035
|
-
* Compare semver versions for sorting.
|
|
1036
|
-
*/
|
|
1037
|
-
export function compareVersions(a, b) {
|
|
1038
|
-
const aParts = a.split('.').map((n) => parseInt(n, 10) || 0);
|
|
1039
|
-
const bParts = b.split('.').map((n) => parseInt(n, 10) || 0);
|
|
1040
|
-
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
1041
|
-
const aVal = aParts[i] || 0;
|
|
1042
|
-
const bVal = bParts[i] || 0;
|
|
1043
|
-
if (aVal !== bVal) {
|
|
1044
|
-
return aVal - bVal;
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
return 0;
|
|
1048
|
-
}
|
|
1049
|
-
/**
|
|
1050
|
-
* Get actual version from an installed 'latest' directory.
|
|
1051
|
-
*/
|
|
1052
|
-
export async function getInstalledVersion(agent, version) {
|
|
1053
|
-
const binaryPath = getBinaryPath(agent, version);
|
|
1054
|
-
if (!fs.existsSync(binaryPath)) {
|
|
1055
|
-
return null;
|
|
1056
|
-
}
|
|
1057
|
-
try {
|
|
1058
|
-
const { stdout } = await execAsync(`${binaryPath} --version`);
|
|
1059
|
-
const match = stdout.match(/(\d+\.\d+\.\d+)/);
|
|
1060
|
-
return match ? match[1] : version;
|
|
1061
|
-
}
|
|
1062
|
-
catch {
|
|
1063
|
-
return version;
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
/**
|
|
1067
|
-
* Get the diff between central resources (~/.agents/) and what's synced to a version.
|
|
1068
|
-
* Uses filesystem state - no tracking needed.
|
|
1069
|
-
*/
|
|
1070
|
-
export function getResourceDiff(agent, version) {
|
|
1071
|
-
const agentConfig = AGENTS[agent];
|
|
1072
|
-
const versionHome = getVersionHomePath(agent, version);
|
|
1073
|
-
const agentDir = path.join(versionHome, `.${agent}`);
|
|
1074
|
-
const diff = {
|
|
1075
|
-
commands: { added: [], dangling: [] },
|
|
1076
|
-
skills: { added: [], dangling: [] },
|
|
1077
|
-
hooks: { added: [], dangling: [] },
|
|
1078
|
-
memory: { added: [], dangling: [] },
|
|
1079
|
-
totalAdded: 0,
|
|
1080
|
-
totalDangling: 0,
|
|
1081
|
-
};
|
|
1082
|
-
// Helper to check symlink status
|
|
1083
|
-
const getSymlinkStatus = (linkPath) => {
|
|
1084
|
-
try {
|
|
1085
|
-
const stat = fs.lstatSync(linkPath);
|
|
1086
|
-
if (!stat.isSymbolicLink())
|
|
1087
|
-
return 'none';
|
|
1088
|
-
// Check if target exists
|
|
1089
|
-
try {
|
|
1090
|
-
fs.statSync(linkPath);
|
|
1091
|
-
return 'valid';
|
|
1092
|
-
}
|
|
1093
|
-
catch {
|
|
1094
|
-
return 'dangling';
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
catch {
|
|
1098
|
-
return 'none';
|
|
1099
|
-
}
|
|
1100
|
-
};
|
|
1101
|
-
// Commands: check directory symlink (or individual files for Gemini)
|
|
1102
|
-
const centralCommands = getCommandsDir();
|
|
1103
|
-
const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
|
|
1104
|
-
if (agentConfig.format === 'toml') {
|
|
1105
|
-
// Gemini: compare .md files in central vs .toml files in version
|
|
1106
|
-
if (fs.existsSync(centralCommands)) {
|
|
1107
|
-
const centralFiles = fs.readdirSync(centralCommands).filter(f => f.endsWith('.md'));
|
|
1108
|
-
const versionFiles = fs.existsSync(commandsTarget)
|
|
1109
|
-
? fs.readdirSync(commandsTarget).filter(f => f.endsWith('.toml'))
|
|
1110
|
-
: [];
|
|
1111
|
-
const versionNames = new Set(versionFiles.map(f => f.replace('.toml', '')));
|
|
1112
|
-
for (const file of centralFiles) {
|
|
1113
|
-
const name = file.replace('.md', '');
|
|
1114
|
-
if (!versionNames.has(name)) {
|
|
1115
|
-
diff.commands.added.push(file);
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
// Check for dangling (toml exists but no md source)
|
|
1119
|
-
const centralNames = new Set(centralFiles.map(f => f.replace('.md', '')));
|
|
1120
|
-
for (const file of versionFiles) {
|
|
1121
|
-
const name = file.replace('.toml', '');
|
|
1122
|
-
if (!centralNames.has(name)) {
|
|
1123
|
-
diff.commands.dangling.push(file);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
else {
|
|
1129
|
-
// Other agents: check directory symlink
|
|
1130
|
-
const status = getSymlinkStatus(commandsTarget);
|
|
1131
|
-
if (status === 'none' && fs.existsSync(centralCommands)) {
|
|
1132
|
-
const files = fs.readdirSync(centralCommands).filter(f => f.endsWith('.md'));
|
|
1133
|
-
diff.commands.added = files;
|
|
1134
|
-
}
|
|
1135
|
-
else if (status === 'dangling') {
|
|
1136
|
-
diff.commands.dangling = ['commands/'];
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
// Skills: check directory symlink (skip if agent natively reads ~/.agents/skills/)
|
|
1140
|
-
if (!agentConfig.nativeAgentsSkillsDir) {
|
|
1141
|
-
const centralSkills = getSkillsDir();
|
|
1142
|
-
const skillsTarget = path.join(agentDir, 'skills');
|
|
1143
|
-
const skillsStatus = getSymlinkStatus(skillsTarget);
|
|
1144
|
-
if (skillsStatus === 'none' && fs.existsSync(centralSkills)) {
|
|
1145
|
-
const dirs = fs.readdirSync(centralSkills).filter(f => {
|
|
1146
|
-
const stat = fs.statSync(path.join(centralSkills, f));
|
|
1147
|
-
return stat.isDirectory() && !f.startsWith('.');
|
|
1148
|
-
});
|
|
1149
|
-
diff.skills.added = dirs;
|
|
1150
|
-
}
|
|
1151
|
-
else if (skillsStatus === 'dangling') {
|
|
1152
|
-
diff.skills.dangling = ['skills/'];
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
// Hooks: check directory symlink (if agent supports hooks)
|
|
1156
|
-
if (agentConfig.supportsHooks) {
|
|
1157
|
-
const centralHooks = getHooksDir();
|
|
1158
|
-
const hooksTarget = path.join(agentDir, 'hooks');
|
|
1159
|
-
const hooksStatus = getSymlinkStatus(hooksTarget);
|
|
1160
|
-
if (hooksStatus === 'none' && fs.existsSync(centralHooks)) {
|
|
1161
|
-
const files = fs.readdirSync(centralHooks).filter(f => !f.startsWith('.'));
|
|
1162
|
-
diff.hooks.added = files;
|
|
1163
|
-
}
|
|
1164
|
-
else if (hooksStatus === 'dangling') {
|
|
1165
|
-
diff.hooks.dangling = ['hooks/'];
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
// Memory: check individual file symlinks
|
|
1169
|
-
const centralMemory = getMemoryDir();
|
|
1170
|
-
if (fs.existsSync(centralMemory)) {
|
|
1171
|
-
const memoryFiles = fs.readdirSync(centralMemory).filter(f => f.endsWith('.md'));
|
|
1172
|
-
for (const file of memoryFiles) {
|
|
1173
|
-
const targetName = file === 'AGENTS.md' ? agentConfig.instructionsFile : file;
|
|
1174
|
-
const targetPath = path.join(agentDir, targetName);
|
|
1175
|
-
const status = getSymlinkStatus(targetPath);
|
|
1176
|
-
if (status === 'none') {
|
|
1177
|
-
diff.memory.added.push(file);
|
|
1178
|
-
}
|
|
1179
|
-
else if (status === 'dangling') {
|
|
1180
|
-
diff.memory.dangling.push(targetName);
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
// Calculate totals
|
|
1185
|
-
diff.totalAdded = diff.commands.added.length + diff.skills.added.length +
|
|
1186
|
-
diff.hooks.added.length + diff.memory.added.length;
|
|
1187
|
-
diff.totalDangling = diff.commands.dangling.length + diff.skills.dangling.length +
|
|
1188
|
-
diff.hooks.dangling.length + diff.memory.dangling.length;
|
|
1189
|
-
return diff;
|
|
1190
|
-
}
|
|
1191
|
-
/**
|
|
1192
|
-
* Sync central resources (~/.agents/) into a specific version's config directory.
|
|
1193
|
-
* Copies selected resources from central storage into {versionHome}/.{agent}/.
|
|
1194
|
-
*
|
|
1195
|
-
* @param agent - The agent ID
|
|
1196
|
-
* @param version - The version string
|
|
1197
|
-
* @param selection - Optional resource selection. If not provided, syncs all resources.
|
|
1198
|
-
*
|
|
1199
|
-
* For Gemini: commands are converted from markdown to TOML.
|
|
1200
|
-
*/
|
|
1201
|
-
export function syncResourcesToVersion(agent, version, selection, options = {}) {
|
|
1202
|
-
const agentConfig = AGENTS[agent];
|
|
1203
|
-
const versionHome = getVersionHomePath(agent, version);
|
|
1204
|
-
const agentDir = path.join(versionHome, `.${agent}`);
|
|
1205
|
-
fs.mkdirSync(agentDir, { recursive: true });
|
|
1206
|
-
const result = { commands: false, skills: false, hooks: false, memory: [], permissions: false, mcp: [], subagents: [], plugins: [] };
|
|
1207
|
-
const cwd = options.cwd || process.cwd();
|
|
1208
|
-
const projectAgentsDir = options.projectDir || getProjectAgentsDir(cwd);
|
|
1209
|
-
const available = getAvailableResources(cwd);
|
|
1210
|
-
// Helper: remove a path (symlink or real) if it exists
|
|
1211
|
-
const removePath = (p) => {
|
|
1212
|
-
try {
|
|
1213
|
-
const stat = fs.lstatSync(p);
|
|
1214
|
-
if (stat.isSymbolicLink() || stat.isFile()) {
|
|
1215
|
-
fs.unlinkSync(p);
|
|
1216
|
-
}
|
|
1217
|
-
else if (stat.isDirectory()) {
|
|
1218
|
-
fs.rmSync(p, { recursive: true, force: true });
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
catch { /* file already removed or inaccessible */ }
|
|
1222
|
-
};
|
|
1223
|
-
// Helper: copy a directory recursively
|
|
1224
|
-
const copyDir = (src, dest) => {
|
|
1225
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
1226
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
1227
|
-
for (const entry of entries) {
|
|
1228
|
-
const srcPath = path.join(src, entry.name);
|
|
1229
|
-
const destPath = path.join(dest, entry.name);
|
|
1230
|
-
if (entry.isDirectory()) {
|
|
1231
|
-
copyDir(srcPath, destPath);
|
|
1232
|
-
}
|
|
1233
|
-
else {
|
|
1234
|
-
fs.copyFileSync(srcPath, destPath);
|
|
1235
|
-
}
|
|
1236
|
-
}
|
|
1237
|
-
};
|
|
1238
|
-
// Helper: resolve selection to list of items
|
|
1239
|
-
const resolveSelection = (sel, available) => {
|
|
1240
|
-
if (sel === 'all')
|
|
1241
|
-
return available;
|
|
1242
|
-
if (Array.isArray(sel))
|
|
1243
|
-
return sel;
|
|
1244
|
-
return [];
|
|
1245
|
-
};
|
|
1246
|
-
// Sync commands
|
|
1247
|
-
const commandsToSync = selection
|
|
1248
|
-
? resolveSelection(selection.commands, available.commands)
|
|
1249
|
-
: available.commands; // No selection = sync all
|
|
1250
|
-
if (commandsToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
1251
|
-
const centralCommands = getCommandsDir();
|
|
1252
|
-
const projectCommandsDir = projectAgentsDir ? path.join(projectAgentsDir, 'commands') : null;
|
|
1253
|
-
const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
|
|
1254
|
-
fs.mkdirSync(commandsTarget, { recursive: true });
|
|
1255
|
-
const syncedCommands = [];
|
|
1256
|
-
for (const cmd of commandsToSync) {
|
|
1257
|
-
const projectSource = projectCommandsDir ? path.join(projectCommandsDir, `${cmd}.md`) : null;
|
|
1258
|
-
const userSource = path.join(centralCommands, `${cmd}.md`);
|
|
1259
|
-
const srcFile = projectSource && fs.existsSync(projectSource) ? projectSource : userSource;
|
|
1260
|
-
if (!fs.existsSync(srcFile))
|
|
1261
|
-
continue;
|
|
1262
|
-
if (agentConfig.format === 'toml') {
|
|
1263
|
-
const content = fs.readFileSync(srcFile, 'utf-8');
|
|
1264
|
-
const tomlContent = markdownToToml(cmd, content);
|
|
1265
|
-
fs.writeFileSync(path.join(commandsTarget, `${cmd}.toml`), tomlContent);
|
|
1266
|
-
}
|
|
1267
|
-
else {
|
|
1268
|
-
fs.copyFileSync(srcFile, path.join(commandsTarget, `${cmd}.md`));
|
|
1269
|
-
}
|
|
1270
|
-
syncedCommands.push(cmd);
|
|
1271
|
-
}
|
|
1272
|
-
result.commands = syncedCommands.length > 0;
|
|
1273
|
-
if (syncedCommands.length > 0) {
|
|
1274
|
-
recordVersionResources(agent, version, 'commands', syncedCommands);
|
|
1275
|
-
}
|
|
1276
|
-
}
|
|
1277
|
-
// Sync skills (skip if agent natively reads ~/.agents/skills/)
|
|
1278
|
-
if (agentConfig.nativeAgentsSkillsDir) {
|
|
1279
|
-
// Clean up stale skills symlink/dir — agent reads from ~/.agents/skills/ directly
|
|
1280
|
-
const skillsTarget = path.join(agentDir, 'skills');
|
|
1281
|
-
removePath(skillsTarget);
|
|
1282
|
-
}
|
|
1283
|
-
else {
|
|
1284
|
-
const skillsToSync = selection
|
|
1285
|
-
? resolveSelection(selection.skills, available.skills)
|
|
1286
|
-
: available.skills;
|
|
1287
|
-
if (skillsToSync.length > 0) {
|
|
1288
|
-
const centralSkills = getSkillsDir();
|
|
1289
|
-
const projectSkills = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
|
|
1290
|
-
const skillsTarget = path.join(agentDir, 'skills');
|
|
1291
|
-
fs.mkdirSync(skillsTarget, { recursive: true });
|
|
1292
|
-
const syncedSkills = [];
|
|
1293
|
-
for (const skill of skillsToSync) {
|
|
1294
|
-
const projectSource = projectSkills ? path.join(projectSkills, skill) : null;
|
|
1295
|
-
const srcDir = projectSource && fs.existsSync(projectSource) ? projectSource : path.join(centralSkills, skill);
|
|
1296
|
-
if (!fs.existsSync(srcDir))
|
|
1297
|
-
continue;
|
|
1298
|
-
const destDir = path.join(skillsTarget, skill);
|
|
1299
|
-
removePath(destDir);
|
|
1300
|
-
copyDir(srcDir, destDir);
|
|
1301
|
-
syncedSkills.push(skill);
|
|
1302
|
-
}
|
|
1303
|
-
result.skills = syncedSkills.length > 0;
|
|
1304
|
-
if (syncedSkills.length > 0) {
|
|
1305
|
-
recordVersionResources(agent, version, 'skills', syncedSkills);
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
// Sync hooks (if agent supports them)
|
|
1310
|
-
if (agentConfig.supportsHooks) {
|
|
1311
|
-
// Version gate: Codex hooks require >= CODEX_HOOKS_MIN_VERSION
|
|
1312
|
-
if (agent === 'codex' && compareVersions(version, CODEX_HOOKS_MIN_VERSION) < 0) {
|
|
1313
|
-
console.warn(`hooks skipped: codex@${version} < ${CODEX_HOOKS_MIN_VERSION}`);
|
|
1314
|
-
}
|
|
1315
|
-
else {
|
|
1316
|
-
const hooksToSync = selection
|
|
1317
|
-
? resolveSelection(selection.hooks, available.hooks)
|
|
1318
|
-
: available.hooks;
|
|
1319
|
-
if (hooksToSync.length > 0) {
|
|
1320
|
-
const centralHooks = getHooksDir();
|
|
1321
|
-
const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
|
|
1322
|
-
const hooksTarget = path.join(agentDir, 'hooks');
|
|
1323
|
-
fs.mkdirSync(hooksTarget, { recursive: true });
|
|
1324
|
-
const syncedHooks = [];
|
|
1325
|
-
for (const hook of hooksToSync) {
|
|
1326
|
-
const projectSource = projectHooksDir ? path.join(projectHooksDir, hook) : null;
|
|
1327
|
-
const srcFile = projectSource && fs.existsSync(projectSource) ? projectSource : path.join(centralHooks, hook);
|
|
1328
|
-
if (!fs.existsSync(srcFile))
|
|
1329
|
-
continue;
|
|
1330
|
-
const destFile = path.join(hooksTarget, hook);
|
|
1331
|
-
fs.copyFileSync(srcFile, destFile);
|
|
1332
|
-
fs.chmodSync(destFile, 0o755);
|
|
1333
|
-
syncedHooks.push(hook);
|
|
1334
|
-
}
|
|
1335
|
-
// Remove orphan hook files that exist in version home but not in central
|
|
1336
|
-
const centralHookNames = new Set(fs.existsSync(getHooksDir())
|
|
1337
|
-
? fs.readdirSync(getHooksDir()).filter(f => !f.startsWith('.'))
|
|
1338
|
-
: []);
|
|
1339
|
-
if (fs.existsSync(hooksTarget)) {
|
|
1340
|
-
for (const file of fs.readdirSync(hooksTarget).filter(f => !f.startsWith('.'))) {
|
|
1341
|
-
if (!centralHookNames.has(file)) {
|
|
1342
|
-
removePath(path.join(hooksTarget, file));
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
result.hooks = syncedHooks.length > 0;
|
|
1347
|
-
if (syncedHooks.length > 0) {
|
|
1348
|
-
recordVersionResources(agent, version, 'hooks', syncedHooks);
|
|
1349
|
-
}
|
|
1350
|
-
if (agent === 'claude' || agent === 'codex') {
|
|
1351
|
-
registerHooksToSettings(agent, versionHome);
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
// Sync memory files
|
|
1357
|
-
const memoryToSync = selection
|
|
1358
|
-
? resolveSelection(selection.memory, available.memory)
|
|
1359
|
-
: available.memory;
|
|
1360
|
-
if (memoryToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
1361
|
-
const centralMemory = getMemoryDir();
|
|
1362
|
-
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, 'memory') : null;
|
|
1363
|
-
const syncedMemory = [];
|
|
1364
|
-
const agentSupportsImports = !!agentConfig.capabilities.memoryImports;
|
|
1365
|
-
for (const mem of memoryToSync) {
|
|
1366
|
-
const projectSource = projectMemoryDir ? path.join(projectMemoryDir, `${mem}.md`) : null;
|
|
1367
|
-
const srcFile = projectSource && fs.existsSync(projectSource)
|
|
1368
|
-
? projectSource
|
|
1369
|
-
: path.join(centralMemory, `${mem}.md`);
|
|
1370
|
-
if (!fs.existsSync(srcFile))
|
|
1371
|
-
continue;
|
|
1372
|
-
const targetName = mem === 'AGENTS' ? agentConfig.instructionsFile : `${mem}.md`;
|
|
1373
|
-
const destFile = path.join(agentDir, targetName);
|
|
1374
|
-
removePath(destFile);
|
|
1375
|
-
// For the primary memory file (AGENTS.md), agents that don't natively
|
|
1376
|
-
// resolve @-imports get a compiled (inlined) copy + sidecar manifest.
|
|
1377
|
-
// Everything else (secondary memory files, @-capable agents) gets a
|
|
1378
|
-
// straight copy.
|
|
1379
|
-
if (mem === 'AGENTS' && !agentSupportsImports) {
|
|
1380
|
-
compileMemoryForAgent(agent, version);
|
|
1381
|
-
}
|
|
1382
|
-
else {
|
|
1383
|
-
fs.copyFileSync(srcFile, destFile);
|
|
1384
|
-
}
|
|
1385
|
-
result.memory.push(targetName);
|
|
1386
|
-
syncedMemory.push(mem);
|
|
1387
|
-
}
|
|
1388
|
-
if (syncedMemory.length > 0) {
|
|
1389
|
-
recordVersionResources(agent, version, 'memory', syncedMemory);
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
// Apply permissions (if agent supports them).
|
|
1393
|
-
// Groups live in ~/.agents/permissions/groups/. Optional recipes in
|
|
1394
|
-
// ~/.agents/permissions/sets/<name>.yaml pick a subset via `includes:`.
|
|
1395
|
-
// If AGENTS_PERMISSION_SET is set, we resolve that recipe and use its
|
|
1396
|
-
// includes list as the group filter (intersected with groups on disk).
|
|
1397
|
-
const permissionGroups = discoverPermissionGroups();
|
|
1398
|
-
const allGroupNames = permissionGroups.map(g => g.name);
|
|
1399
|
-
const activeSetName = getActivePermissionSetName();
|
|
1400
|
-
let setFilteredGroups = null;
|
|
1401
|
-
if (activeSetName) {
|
|
1402
|
-
const recipe = readPermissionSetRecipe(activeSetName);
|
|
1403
|
-
if (recipe) {
|
|
1404
|
-
const available = new Set(allGroupNames);
|
|
1405
|
-
setFilteredGroups = recipe.includes.filter(g => available.has(g));
|
|
1406
|
-
}
|
|
1407
|
-
else {
|
|
1408
|
-
console.warn(`${PERMISSION_SET_ENV_VAR}=${activeSetName} but no recipe at ~/.agents/permissions/sets/${activeSetName}.yaml — falling back to all groups`);
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
let permsToSync;
|
|
1412
|
-
if (selection) {
|
|
1413
|
-
permsToSync = resolveSelection(selection.permissions, allGroupNames);
|
|
1414
|
-
// If a set recipe is active, the recipe's includes list always wins —
|
|
1415
|
-
// even when the caller passed an explicit array via selection. Without
|
|
1416
|
-
// this intersection, `agents add`'s buildAutomaticSelection would pass
|
|
1417
|
-
// every group name discovered on disk (including 99-deny), bypassing
|
|
1418
|
-
// the sandbox filter.
|
|
1419
|
-
if (setFilteredGroups) {
|
|
1420
|
-
const filterSet = new Set(setFilteredGroups);
|
|
1421
|
-
permsToSync = permsToSync.filter(g => filterSet.has(g));
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
else {
|
|
1425
|
-
permsToSync = PERMISSIONS_CAPABLE_AGENTS.includes(agent)
|
|
1426
|
-
? (setFilteredGroups ?? allGroupNames)
|
|
1427
|
-
: [];
|
|
1428
|
-
}
|
|
1429
|
-
if (permsToSync.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
|
|
1430
|
-
// Build permissions from selected groups
|
|
1431
|
-
const builtPerms = buildPermissionsFromGroups(permsToSync);
|
|
1432
|
-
if (builtPerms.allow.length > 0 || (builtPerms.deny && builtPerms.deny.length > 0)) {
|
|
1433
|
-
const permResult = applyPermsToVersion(agent, builtPerms, versionHome, true);
|
|
1434
|
-
result.permissions = permResult.success;
|
|
1435
|
-
if (permResult.success) {
|
|
1436
|
-
recordVersionResources(agent, version, 'permissions', permsToSync);
|
|
1437
|
-
}
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
// Install MCP servers (if agent supports them)
|
|
1441
|
-
// For Claude/Codex: uses CLI commands (claude mcp add, codex mcp add)
|
|
1442
|
-
// For others: edits config files directly
|
|
1443
|
-
const mcpToSync = selection
|
|
1444
|
-
? resolveSelection(selection.mcp, available.mcp)
|
|
1445
|
-
: (MCP_CAPABLE_AGENTS.includes(agent) ? available.mcp : []);
|
|
1446
|
-
if (mcpToSync.length > 0 && MCP_CAPABLE_AGENTS.includes(agent)) {
|
|
1447
|
-
const mcpResult = installMcpServers(agent, version, versionHome, mcpToSync, { cwd });
|
|
1448
|
-
result.mcp = mcpResult.applied;
|
|
1449
|
-
if (mcpResult.applied.length > 0) {
|
|
1450
|
-
recordVersionResources(agent, version, 'mcp', mcpResult.applied);
|
|
1451
|
-
}
|
|
1452
|
-
}
|
|
1453
|
-
// Sync subagents (claude and openclaw only)
|
|
1454
|
-
const subagentsToSync = selection
|
|
1455
|
-
? resolveSelection(selection.subagents, available.subagents)
|
|
1456
|
-
: (SUBAGENT_CAPABLE_AGENTS.includes(agent) ? available.subagents : []);
|
|
1457
|
-
if (subagentsToSync.length > 0 && SUBAGENT_CAPABLE_AGENTS.includes(agent)) {
|
|
1458
|
-
const allSubagents = listInstalledSubagents();
|
|
1459
|
-
const subagentsMap = new Map(allSubagents.map(s => [s.name, s]));
|
|
1460
|
-
for (const name of subagentsToSync) {
|
|
1461
|
-
const subagent = subagentsMap.get(name);
|
|
1462
|
-
if (!subagent)
|
|
1463
|
-
continue;
|
|
1464
|
-
try {
|
|
1465
|
-
if (agent === 'claude') {
|
|
1466
|
-
// Claude: flatten to single .md file
|
|
1467
|
-
const agentsDir = path.join(agentDir, 'agents');
|
|
1468
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
1469
|
-
const transformed = transformSubagentForClaude(subagent.path);
|
|
1470
|
-
fs.writeFileSync(path.join(agentsDir, `${subagent.name}.md`), transformed);
|
|
1471
|
-
result.subagents.push(subagent.name);
|
|
1472
|
-
}
|
|
1473
|
-
else if (agent === 'openclaw') {
|
|
1474
|
-
// OpenClaw: copy full directory, rename AGENT.md -> AGENTS.md
|
|
1475
|
-
const targetDir = path.join(versionHome, '.openclaw', subagent.name);
|
|
1476
|
-
const syncResult = syncSubagentToOpenclaw(subagent.path, targetDir);
|
|
1477
|
-
if (syncResult.success) {
|
|
1478
|
-
result.subagents.push(subagent.name);
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
catch { /* resource sync failed for this item */ }
|
|
1483
|
-
}
|
|
1484
|
-
if (result.subagents.length > 0) {
|
|
1485
|
-
recordVersionResources(agent, version, 'subagents', result.subagents);
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
// Sync plugins (claude and openclaw)
|
|
1489
|
-
const pluginsToSync = selection
|
|
1490
|
-
? resolveSelection(selection.plugins, available.plugins)
|
|
1491
|
-
: (PLUGINS_CAPABLE_AGENTS.includes(agent) ? available.plugins : []);
|
|
1492
|
-
if (pluginsToSync.length > 0 && PLUGINS_CAPABLE_AGENTS.includes(agent)) {
|
|
1493
|
-
const allPlugins = discoverPlugins();
|
|
1494
|
-
const pluginMap = new Map(allPlugins.map(p => [p.name, p]));
|
|
1495
|
-
// Clean orphaned plugin skills from plugins that no longer exist
|
|
1496
|
-
const activePluginNames = new Set(allPlugins.map(p => p.name));
|
|
1497
|
-
cleanOrphanedPluginSkills(agent, versionHome, activePluginNames);
|
|
1498
|
-
for (const name of pluginsToSync) {
|
|
1499
|
-
const plugin = pluginMap.get(name);
|
|
1500
|
-
if (!plugin || !pluginSupportsAgent(plugin, agent))
|
|
1501
|
-
continue;
|
|
1502
|
-
const pluginResult = syncPluginToVersion(plugin, agent, versionHome);
|
|
1503
|
-
if (pluginResult.success) {
|
|
1504
|
-
result.plugins.push(name);
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
if (result.plugins.length > 0) {
|
|
1508
|
-
recordVersionResources(agent, version, 'plugins', result.plugins);
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
return result;
|
|
1512
|
-
}
|
|
1513
|
-
/**
|
|
1514
|
-
* Get the effective HOME directory for an agent.
|
|
1515
|
-
* If version-managed with a resolved version, returns the version's home directory.
|
|
1516
|
-
* Otherwise returns the real HOME.
|
|
1517
|
-
*/
|
|
1518
|
-
export function getEffectiveHome(agentId) {
|
|
1519
|
-
const resolved = resolveVersion(agentId, process.cwd());
|
|
1520
|
-
if (resolved && isVersionInstalled(agentId, resolved)) {
|
|
1521
|
-
return getVersionHomePath(agentId, resolved);
|
|
1522
|
-
}
|
|
1523
|
-
return os.homedir();
|
|
1524
|
-
}
|
|
1525
|
-
/**
|
|
1526
|
-
* Resolve a comma-separated --agents list into concrete version selections.
|
|
1527
|
-
* Bare agents target the default version, or the newest installed version when no default exists.
|
|
1528
|
-
* Explicit agent@version targets only that installed version.
|
|
1529
|
-
*/
|
|
1530
|
-
export function resolveAgentVersionTargets(value, availableAgents, options = {}) {
|
|
1531
|
-
const selectedAgents = [];
|
|
1532
|
-
const versionSelections = new Map();
|
|
1533
|
-
const explicitSelections = new Set();
|
|
1534
|
-
const targets = value
|
|
1535
|
-
.split(',')
|
|
1536
|
-
.map((item) => item.trim())
|
|
1537
|
-
.filter(Boolean);
|
|
1538
|
-
for (const target of targets) {
|
|
1539
|
-
const atIndex = target.indexOf('@');
|
|
1540
|
-
const agentToken = (atIndex === -1 ? target : target.slice(0, atIndex)).trim();
|
|
1541
|
-
const versionToken = atIndex === -1 ? null : target.slice(atIndex + 1).trim();
|
|
1542
|
-
if (!agentToken) {
|
|
1543
|
-
continue;
|
|
1544
|
-
}
|
|
1545
|
-
if (atIndex !== -1 && !versionToken) {
|
|
1546
|
-
throw new Error(`Missing version in --agents entry '${target}'. Use agent@x.y.z or agent@default.`);
|
|
1547
|
-
}
|
|
1548
|
-
const agentId = resolveAgentName(agentToken);
|
|
1549
|
-
if (!agentId || !availableAgents.includes(agentId)) {
|
|
1550
|
-
throw new Error(formatAgentError(agentToken, [...availableAgents]));
|
|
1551
|
-
}
|
|
1552
|
-
if (!selectedAgents.includes(agentId)) {
|
|
1553
|
-
selectedAgents.push(agentId);
|
|
1554
|
-
}
|
|
1555
|
-
if (explicitSelections.has(agentId) && !versionToken) {
|
|
1556
|
-
continue;
|
|
1557
|
-
}
|
|
1558
|
-
const installedVersions = listInstalledVersions(agentId);
|
|
1559
|
-
const defaultVersion = getGlobalDefault(agentId);
|
|
1560
|
-
if (!versionToken) {
|
|
1561
|
-
if (installedVersions.length === 0) {
|
|
1562
|
-
continue;
|
|
1563
|
-
}
|
|
1564
|
-
versionSelections.set(agentId, options.allVersions
|
|
1565
|
-
? [...installedVersions]
|
|
1566
|
-
: [defaultVersion || installedVersions[installedVersions.length - 1]]);
|
|
1567
|
-
continue;
|
|
1568
|
-
}
|
|
1569
|
-
if (installedVersions.length === 0) {
|
|
1570
|
-
throw new Error(`No managed versions are installed for ${AGENTS[agentId].name}. Run: agents add ${agentId}@latest`);
|
|
1571
|
-
}
|
|
1572
|
-
if (versionToken === 'default') {
|
|
1573
|
-
if (!defaultVersion) {
|
|
1574
|
-
throw new Error(`No default version set for ${AGENTS[agentId].name}. Run: agents use ${agentId}@<version>`);
|
|
1575
|
-
}
|
|
1576
|
-
const explicitVersions = explicitSelections.has(agentId)
|
|
1577
|
-
? (versionSelections.get(agentId) || [])
|
|
1578
|
-
: [];
|
|
1579
|
-
if (!explicitVersions.includes(defaultVersion)) {
|
|
1580
|
-
explicitVersions.push(defaultVersion);
|
|
1581
|
-
}
|
|
1582
|
-
versionSelections.set(agentId, explicitVersions);
|
|
1583
|
-
explicitSelections.add(agentId);
|
|
1584
|
-
continue;
|
|
1585
|
-
}
|
|
1586
|
-
if (!installedVersions.includes(versionToken)) {
|
|
1587
|
-
throw new Error(`Version ${versionToken} is not installed for ${AGENTS[agentId].name}. Installed versions: ${installedVersions.join(', ')}`);
|
|
1588
|
-
}
|
|
1589
|
-
const explicitVersions = explicitSelections.has(agentId)
|
|
1590
|
-
? (versionSelections.get(agentId) || [])
|
|
1591
|
-
: [];
|
|
1592
|
-
if (!explicitVersions.includes(versionToken)) {
|
|
1593
|
-
explicitVersions.push(versionToken);
|
|
1594
|
-
}
|
|
1595
|
-
versionSelections.set(agentId, explicitVersions);
|
|
1596
|
-
explicitSelections.add(agentId);
|
|
1597
|
-
}
|
|
1598
|
-
return { selectedAgents, versionSelections };
|
|
1599
|
-
}
|
|
1600
|
-
/**
|
|
1601
|
-
* Resolve a comma-separated --agents list into install/apply targets.
|
|
1602
|
-
* Bare agents target the default version (or newest installed version) when managed,
|
|
1603
|
-
* and fall back to the agent's effective HOME when unmanaged.
|
|
1604
|
-
* Explicit agent@version targets only that installed version.
|
|
1605
|
-
*/
|
|
1606
|
-
export function resolveInstalledAgentTargets(value, availableAgents, options = {}) {
|
|
1607
|
-
const selectedAgents = [];
|
|
1608
|
-
const directAgents = [];
|
|
1609
|
-
const versionSelections = new Map();
|
|
1610
|
-
const targets = value
|
|
1611
|
-
.split(',')
|
|
1612
|
-
.map((item) => item.trim())
|
|
1613
|
-
.filter(Boolean);
|
|
1614
|
-
const addVersionTarget = (agentId, version) => {
|
|
1615
|
-
const versions = versionSelections.get(agentId) || [];
|
|
1616
|
-
if (!versions.includes(version)) {
|
|
1617
|
-
versions.push(version);
|
|
1618
|
-
versionSelections.set(agentId, versions);
|
|
1619
|
-
}
|
|
1620
|
-
const directIndex = directAgents.indexOf(agentId);
|
|
1621
|
-
if (directIndex !== -1) {
|
|
1622
|
-
directAgents.splice(directIndex, 1);
|
|
1623
|
-
}
|
|
1624
|
-
};
|
|
1625
|
-
for (const target of targets) {
|
|
1626
|
-
const atIndex = target.indexOf('@');
|
|
1627
|
-
const agentToken = (atIndex === -1 ? target : target.slice(0, atIndex)).trim();
|
|
1628
|
-
const versionToken = atIndex === -1 ? null : target.slice(atIndex + 1).trim();
|
|
1629
|
-
if (!agentToken) {
|
|
1630
|
-
continue;
|
|
1631
|
-
}
|
|
1632
|
-
if (atIndex !== -1 && !versionToken) {
|
|
1633
|
-
throw new Error(`Missing version in --agents entry '${target}'. Use agent@x.y.z or agent@default.`);
|
|
1634
|
-
}
|
|
1635
|
-
const agentId = resolveAgentName(agentToken);
|
|
1636
|
-
if (!agentId || !availableAgents.includes(agentId)) {
|
|
1637
|
-
throw new Error(formatAgentError(agentToken, [...availableAgents]));
|
|
1638
|
-
}
|
|
1639
|
-
if (!selectedAgents.includes(agentId)) {
|
|
1640
|
-
selectedAgents.push(agentId);
|
|
1641
|
-
}
|
|
1642
|
-
const installedVersions = listInstalledVersions(agentId);
|
|
1643
|
-
const defaultVersion = getGlobalDefault(agentId);
|
|
1644
|
-
if (!versionToken) {
|
|
1645
|
-
if (installedVersions.length === 0) {
|
|
1646
|
-
if (!directAgents.includes(agentId)) {
|
|
1647
|
-
directAgents.push(agentId);
|
|
1648
|
-
}
|
|
1649
|
-
continue;
|
|
1650
|
-
}
|
|
1651
|
-
const targetVersions = options.allVersions
|
|
1652
|
-
? [...installedVersions]
|
|
1653
|
-
: [defaultVersion || installedVersions[installedVersions.length - 1]];
|
|
1654
|
-
for (const version of targetVersions) {
|
|
1655
|
-
addVersionTarget(agentId, version);
|
|
1656
|
-
}
|
|
1657
|
-
continue;
|
|
1658
|
-
}
|
|
1659
|
-
if (versionToken === 'default') {
|
|
1660
|
-
if (!defaultVersion) {
|
|
1661
|
-
throw new Error(`No default version set for ${AGENTS[agentId].name}. Run: agents use ${agentId}@<version>`);
|
|
1662
|
-
}
|
|
1663
|
-
addVersionTarget(agentId, defaultVersion);
|
|
1664
|
-
continue;
|
|
1665
|
-
}
|
|
1666
|
-
if (installedVersions.length === 0) {
|
|
1667
|
-
throw new Error(`No managed versions are installed for ${AGENTS[agentId].name}. Run: agents add ${agentId}@latest`);
|
|
1668
|
-
}
|
|
1669
|
-
if (!installedVersions.includes(versionToken)) {
|
|
1670
|
-
throw new Error(`Version ${versionToken} is not installed for ${AGENTS[agentId].name}. Installed versions: ${installedVersions.join(', ')}`);
|
|
1671
|
-
}
|
|
1672
|
-
addVersionTarget(agentId, versionToken);
|
|
1673
|
-
}
|
|
1674
|
-
return { selectedAgents, directAgents, versionSelections };
|
|
1675
|
-
}
|
|
1676
|
-
/**
|
|
1677
|
-
* Resolve configured manifest targets into direct homes and managed versions.
|
|
1678
|
-
*/
|
|
1679
|
-
export function resolveConfiguredAgentTargets(agents, agentVersions, availableAgents, options = {}) {
|
|
1680
|
-
const targetSpecs = [];
|
|
1681
|
-
const broadTargets = agents ? [...agents] : [...availableAgents];
|
|
1682
|
-
for (const agentId of broadTargets) {
|
|
1683
|
-
if (availableAgents.includes(agentId)) {
|
|
1684
|
-
targetSpecs.push(agentId);
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
if (agentVersions) {
|
|
1688
|
-
for (const [agentId, versions] of Object.entries(agentVersions)) {
|
|
1689
|
-
if (!availableAgents.includes(agentId) || !versions)
|
|
1690
|
-
continue;
|
|
1691
|
-
for (const version of versions) {
|
|
1692
|
-
targetSpecs.push(`${agentId}@${version}`);
|
|
1693
|
-
}
|
|
1694
|
-
}
|
|
1695
|
-
}
|
|
1696
|
-
if (targetSpecs.length === 0) {
|
|
1697
|
-
return {
|
|
1698
|
-
selectedAgents: [],
|
|
1699
|
-
directAgents: [],
|
|
1700
|
-
versionSelections: new Map(),
|
|
1701
|
-
};
|
|
1702
|
-
}
|
|
1703
|
-
return resolveInstalledAgentTargets(targetSpecs.join(','), availableAgents, options);
|
|
1704
|
-
}
|
|
1705
|
-
/**
|
|
1706
|
-
* Prompt user to select agents and versions for resource installation.
|
|
1707
|
-
* Returns selected agents and their version selections.
|
|
1708
|
-
*/
|
|
1709
|
-
export async function promptAgentVersionSelection(availableAgents, options = {}) {
|
|
1710
|
-
const versionSelections = new Map();
|
|
1711
|
-
// Filter to installed agents (only those with versions managed by agents CLI)
|
|
1712
|
-
const installedAgents = availableAgents.filter((id) => {
|
|
1713
|
-
const versions = listInstalledVersions(id);
|
|
1714
|
-
return versions.length > 0;
|
|
1715
|
-
});
|
|
1716
|
-
if (installedAgents.length === 0) {
|
|
1717
|
-
return { selectedAgents: [], versionSelections };
|
|
1718
|
-
}
|
|
1719
|
-
const formatAgentLabel = (agentId) => {
|
|
1720
|
-
const versions = listInstalledVersions(agentId);
|
|
1721
|
-
const defaultVer = getGlobalDefault(agentId);
|
|
1722
|
-
if (versions.length === 0)
|
|
1723
|
-
return `${AGENTS[agentId].name} ${chalk.gray('(not installed)')}`;
|
|
1724
|
-
if (defaultVer)
|
|
1725
|
-
return `${AGENTS[agentId].name} ${chalk.gray(`(active: ${defaultVer})`)}`;
|
|
1726
|
-
return `${AGENTS[agentId].name} ${chalk.gray(`(${versions[0]})`)}`;
|
|
1727
|
-
};
|
|
1728
|
-
let selectedAgents;
|
|
1729
|
-
if (options.skipPrompts) {
|
|
1730
|
-
// Auto-select all installed agents with default versions
|
|
1731
|
-
selectedAgents = [...installedAgents];
|
|
1732
|
-
for (const agentId of selectedAgents) {
|
|
1733
|
-
const versions = listInstalledVersions(agentId);
|
|
1734
|
-
if (versions.length > 0) {
|
|
1735
|
-
const defaultVer = getGlobalDefault(agentId);
|
|
1736
|
-
versionSelections.set(agentId, defaultVer ? [defaultVer] : [versions[versions.length - 1]]);
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
else {
|
|
1741
|
-
// Prompt for agent selection
|
|
1742
|
-
const checkboxResult = await checkbox({
|
|
1743
|
-
message: 'Which agents should receive these resources?',
|
|
1744
|
-
choices: [
|
|
1745
|
-
{ name: chalk.bold('All'), value: 'all', checked: true },
|
|
1746
|
-
...installedAgents.map((id) => ({
|
|
1747
|
-
name: ` ${formatAgentLabel(id)}`,
|
|
1748
|
-
value: id,
|
|
1749
|
-
checked: false,
|
|
1750
|
-
})),
|
|
1751
|
-
],
|
|
1752
|
-
});
|
|
1753
|
-
if (checkboxResult.includes('all')) {
|
|
1754
|
-
selectedAgents = [...installedAgents];
|
|
1755
|
-
}
|
|
1756
|
-
else {
|
|
1757
|
-
selectedAgents = checkboxResult;
|
|
1758
|
-
}
|
|
1759
|
-
// Version selection per agent
|
|
1760
|
-
for (const agentId of selectedAgents) {
|
|
1761
|
-
const versions = listInstalledVersions(agentId);
|
|
1762
|
-
if (versions.length === 0)
|
|
1763
|
-
continue;
|
|
1764
|
-
if (versions.length === 1) {
|
|
1765
|
-
versionSelections.set(agentId, [versions[0]]);
|
|
1766
|
-
continue;
|
|
1767
|
-
}
|
|
1768
|
-
const defaultVer = getGlobalDefault(agentId);
|
|
1769
|
-
const versionEmails = await Promise.all(versions.map((v) => getAccountEmail(agentId, getVersionHomePath(agentId, v)).then((email) => ({ v, email }))));
|
|
1770
|
-
const versionEmailMap = new Map(versionEmails.map((e) => [e.v, e.email]));
|
|
1771
|
-
const maxLabelLen = Math.max(...versions.map((v) => (v === defaultVer ? `${v} (default)` : v).length));
|
|
1772
|
-
const versionResult = await checkbox({
|
|
1773
|
-
message: `Which versions of ${AGENTS[agentId].name} should receive these resources?`,
|
|
1774
|
-
choices: [
|
|
1775
|
-
{ name: chalk.bold('All versions'), value: 'all', checked: false },
|
|
1776
|
-
...versions.map((v) => {
|
|
1777
|
-
const base = v === defaultVer ? `${v} (default)` : v;
|
|
1778
|
-
let label = base.padEnd(maxLabelLen);
|
|
1779
|
-
const email = versionEmailMap.get(v);
|
|
1780
|
-
if (email)
|
|
1781
|
-
label += chalk.cyan(` ${email}`);
|
|
1782
|
-
return { name: label, value: v, checked: v === defaultVer };
|
|
1783
|
-
}),
|
|
1784
|
-
],
|
|
1785
|
-
});
|
|
1786
|
-
if (versionResult.includes('all')) {
|
|
1787
|
-
versionSelections.set(agentId, [...versions]);
|
|
1788
|
-
}
|
|
1789
|
-
else {
|
|
1790
|
-
versionSelections.set(agentId, versionResult);
|
|
1791
|
-
}
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
return { selectedAgents, versionSelections };
|
|
1795
|
-
}
|
|
1796
|
-
//# sourceMappingURL=versions.js.map
|