@phnx-labs/agents-cli 1.12.0 → 1.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -1
- package/README.md +308 -297
- package/dist/commands/alias.d.ts +11 -0
- package/dist/commands/alias.js +117 -0
- package/dist/commands/beta.d.ts +2 -0
- package/dist/commands/beta.js +53 -0
- package/dist/commands/cloud.d.ts +10 -0
- package/dist/commands/cloud.js +408 -0
- package/dist/commands/commands.d.ts +9 -1
- package/dist/commands/commands.js +24 -172
- package/dist/commands/daemon.d.ts +8 -1
- package/dist/commands/daemon.js +13 -5
- package/dist/commands/doctor.d.ts +15 -0
- package/dist/commands/doctor.js +132 -0
- package/dist/commands/drive.d.ts +8 -1
- package/dist/commands/drive.js +20 -3
- package/dist/commands/exec.d.ts +8 -1
- package/dist/commands/exec.js +207 -20
- package/dist/commands/factory.d.ts +19 -0
- package/dist/commands/factory.js +71 -0
- package/dist/commands/fork.d.ts +8 -1
- package/dist/commands/fork.js +11 -4
- package/dist/commands/hooks.d.ts +9 -1
- package/dist/commands/hooks.js +30 -182
- package/dist/commands/init.d.ts +15 -1
- package/dist/commands/init.js +168 -74
- package/dist/commands/mcp.d.ts +9 -1
- package/dist/commands/mcp.js +11 -7
- package/dist/commands/models.d.ts +8 -1
- package/dist/commands/models.js +45 -6
- package/dist/commands/packages.d.ts +8 -1
- package/dist/commands/packages.js +13 -7
- package/dist/commands/permissions.d.ts +9 -1
- package/dist/commands/permissions.js +3 -3
- package/dist/commands/plugins.d.ts +8 -1
- package/dist/commands/plugins.js +13 -2
- package/dist/commands/profiles.d.ts +11 -0
- package/dist/commands/profiles.js +291 -0
- package/dist/commands/prune.d.ts +22 -0
- package/dist/commands/prune.js +191 -0
- package/dist/commands/pty.d.ts +1 -1
- package/dist/commands/pty.js +2 -1
- package/dist/commands/pull.d.ts +8 -1
- package/dist/commands/pull.js +93 -129
- package/dist/commands/refresh-memory.d.ts +7 -1
- package/dist/commands/refresh-memory.js +7 -1
- package/dist/commands/repo.d.ts +15 -0
- package/dist/commands/repo.js +570 -0
- package/dist/commands/resource-view.d.ts +10 -3
- package/dist/commands/resource-view.js +18 -5
- package/dist/commands/routines.d.ts +8 -1
- package/dist/commands/routines.js +17 -4
- package/dist/commands/rules.d.ts +9 -1
- package/dist/commands/rules.js +16 -11
- package/dist/commands/secrets.d.ts +10 -0
- package/dist/commands/secrets.js +518 -0
- package/dist/commands/sessions-picker.d.ts +2 -1
- package/dist/commands/sessions-picker.js +88 -11
- package/dist/commands/sessions-tail.d.ts +19 -0
- package/dist/commands/sessions-tail.js +235 -0
- package/dist/commands/sessions.d.ts +2 -1
- package/dist/commands/sessions.js +288 -7
- package/dist/commands/skills.d.ts +9 -1
- package/dist/commands/skills.js +28 -178
- package/dist/commands/status.d.ts +7 -1
- package/dist/commands/status.js +7 -1
- package/dist/commands/subagents.d.ts +8 -1
- package/dist/commands/subagents.js +11 -1
- package/dist/commands/sync.d.ts +8 -1
- package/dist/commands/sync.js +8 -1
- package/dist/commands/teams-picker.d.ts +4 -1
- package/dist/commands/teams-picker.js +55 -3
- package/dist/commands/teams.d.ts +15 -1
- package/dist/commands/teams.js +323 -69
- package/dist/commands/usage.d.ts +11 -0
- package/dist/commands/usage.js +60 -0
- package/dist/commands/utils.d.ts +6 -1
- package/dist/commands/utils.js +6 -1
- package/dist/commands/versions.d.ts +8 -1
- package/dist/commands/versions.js +4 -3
- package/dist/commands/view.d.ts +47 -2
- package/dist/commands/view.js +353 -20
- package/dist/index.d.ts +7 -2
- package/dist/index.js +205 -38
- package/dist/lib/acp/client.d.ts +31 -0
- package/dist/lib/acp/client.js +117 -0
- package/dist/lib/acp/harnesses.d.ts +26 -0
- package/dist/lib/acp/harnesses.js +65 -0
- package/dist/lib/acp/run.d.ts +18 -0
- package/dist/lib/acp/run.js +39 -0
- package/dist/lib/agents.d.ts +74 -2
- package/dist/lib/agents.js +207 -23
- package/dist/lib/artifact-actions.d.ts +8 -4
- package/dist/lib/artifact-actions.js +8 -6
- package/dist/lib/auto-pull-worker.d.ts +11 -0
- package/dist/lib/auto-pull-worker.js +121 -0
- package/dist/lib/auto-pull.d.ts +31 -0
- package/dist/lib/auto-pull.js +97 -0
- package/dist/lib/beta.d.ts +23 -0
- package/dist/lib/beta.js +90 -0
- package/dist/lib/capabilities.d.ts +29 -0
- package/dist/lib/capabilities.js +74 -0
- package/dist/lib/cloud/codex.d.ts +25 -0
- package/dist/lib/cloud/codex.js +250 -0
- package/dist/lib/cloud/factory.d.ts +31 -0
- package/dist/lib/cloud/factory.js +53 -0
- package/dist/lib/cloud/registry.d.ts +15 -0
- package/dist/lib/cloud/registry.js +67 -0
- package/dist/lib/cloud/rush.d.ts +75 -0
- package/dist/lib/cloud/rush.js +438 -0
- package/dist/lib/cloud/store.d.ts +22 -0
- package/dist/lib/cloud/store.js +115 -0
- package/dist/lib/cloud/stream.d.ts +23 -0
- package/dist/lib/cloud/stream.js +194 -0
- package/dist/lib/cloud/types.d.ts +205 -0
- package/dist/lib/cloud/types.js +34 -0
- package/dist/lib/command-skills.d.ts +20 -0
- package/dist/lib/command-skills.js +142 -0
- package/dist/lib/commands.d.ts +22 -2
- package/dist/lib/commands.js +51 -11
- package/dist/lib/convert.d.ts +10 -1
- package/dist/lib/convert.js +9 -1
- package/dist/lib/daemon.d.ts +21 -1
- package/dist/lib/daemon.js +97 -4
- package/dist/lib/drive-sync.d.ts +18 -1
- package/dist/lib/drive-sync.js +57 -15
- package/dist/lib/exec.d.ts +25 -5
- package/dist/lib/exec.js +72 -27
- package/dist/lib/fs-walk.d.ts +2 -0
- package/dist/lib/fs-walk.js +40 -0
- package/dist/lib/fuzzy.d.ts +53 -0
- package/dist/lib/fuzzy.js +72 -0
- package/dist/lib/gemini-settings.d.ts +4 -0
- package/dist/lib/gemini-settings.js +33 -0
- package/dist/lib/git.d.ts +12 -2
- package/dist/lib/git.js +17 -6
- package/dist/lib/help.d.ts +20 -1
- package/dist/lib/help.js +45 -6
- package/dist/lib/hooks/match.d.ts +32 -0
- package/dist/lib/hooks/match.js +120 -0
- package/dist/lib/hooks.d.ts +17 -4
- package/dist/lib/hooks.js +191 -21
- package/dist/lib/manifest.d.ts +6 -1
- package/dist/lib/manifest.js +15 -4
- package/dist/lib/markdown.d.ts +0 -1
- package/dist/lib/markdown.js +6 -1
- package/dist/lib/mcp.d.ts +0 -1
- package/dist/lib/mcp.js +29 -33
- package/dist/lib/memory-compile.d.ts +13 -3
- package/dist/lib/memory-compile.js +31 -9
- package/dist/lib/memory.d.ts +14 -7
- package/dist/lib/memory.js +67 -38
- package/dist/lib/migrate.d.ts +8 -0
- package/dist/lib/migrate.js +85 -0
- package/dist/lib/models.d.ts +25 -11
- package/dist/lib/models.js +405 -16
- package/dist/lib/onepassword.d.ts +63 -0
- package/dist/lib/onepassword.js +186 -0
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.js +20 -0
- package/dist/lib/permissions.d.ts +24 -2
- package/dist/lib/permissions.js +117 -48
- package/dist/lib/picker.d.ts +10 -1
- package/dist/lib/picker.js +15 -1
- package/dist/lib/plugins.d.ts +7 -1
- package/dist/lib/plugins.js +10 -1
- package/dist/lib/profiles-presets.d.ts +24 -0
- package/dist/lib/profiles-presets.js +103 -0
- package/dist/lib/profiles.d.ts +69 -0
- package/dist/lib/profiles.js +144 -0
- package/dist/lib/pty-client.d.ts +1 -1
- package/dist/lib/pty-client.js +0 -1
- package/dist/lib/pty-server.d.ts +16 -2
- package/dist/lib/pty-server.js +92 -3
- package/dist/lib/registry.d.ts +23 -3
- package/dist/lib/registry.js +153 -8
- package/dist/lib/resources.d.ts +28 -1
- package/dist/lib/resources.js +79 -1
- package/dist/lib/rotate.d.ts +89 -0
- package/dist/lib/rotate.js +327 -0
- package/dist/lib/routines.d.ts +29 -1
- package/dist/lib/routines.js +32 -5
- package/dist/lib/runner.d.ts +14 -1
- package/dist/lib/runner.js +22 -3
- package/dist/lib/sandbox.d.ts +16 -1
- package/dist/lib/sandbox.js +39 -16
- package/dist/lib/scheduler.d.ts +8 -1
- package/dist/lib/scheduler.js +8 -1
- package/dist/lib/secrets/AgentsKeychain.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/AgentsKeychain.app/Contents/Info.plist +22 -0
- package/dist/lib/secrets/AgentsKeychain.app/Contents/MacOS/AgentsKeychain +0 -0
- package/dist/lib/secrets/AgentsKeychain.app/Contents/_CodeSignature/CodeResources +123 -0
- package/dist/lib/secrets/AgentsKeychain.app/Contents/embedded.provisionprofile +0 -0
- package/dist/lib/secrets/bundles.d.ts +39 -0
- package/dist/lib/secrets/bundles.js +189 -0
- package/dist/lib/secrets/index.d.ts +55 -0
- package/dist/lib/secrets/index.js +211 -0
- package/dist/lib/secrets/profiles.d.ts +10 -0
- package/dist/lib/secrets/profiles.js +13 -0
- package/dist/lib/session/active.d.ts +43 -0
- package/dist/lib/session/active.js +392 -0
- package/dist/lib/session/artifacts.d.ts +16 -0
- package/dist/lib/session/artifacts.js +95 -0
- package/dist/lib/session/cloud.d.ts +30 -0
- package/dist/lib/session/cloud.js +121 -0
- package/dist/lib/session/db.d.ts +23 -2
- package/dist/lib/session/db.js +76 -12
- package/dist/lib/session/discover.d.ts +19 -4
- package/dist/lib/session/discover.js +344 -48
- package/dist/lib/session/parse.d.ts +28 -1
- package/dist/lib/session/parse.js +267 -9
- package/dist/lib/session/prompt.d.ts +9 -1
- package/dist/lib/session/prompt.js +17 -3
- package/dist/lib/session/render.d.ts +13 -1
- package/dist/lib/session/render.js +20 -1
- package/dist/lib/session/team-filter.d.ts +9 -1
- package/dist/lib/session/team-filter.js +11 -2
- package/dist/lib/session/types.d.ts +24 -2
- package/dist/lib/session/types.js +10 -2
- package/dist/lib/shims.d.ts +93 -5
- package/dist/lib/shims.js +380 -67
- package/dist/lib/skills.d.ts +27 -2
- package/dist/lib/skills.js +127 -65
- package/dist/lib/sqlite.d.ts +43 -0
- package/dist/lib/sqlite.js +94 -0
- package/dist/lib/state.d.ts +114 -22
- package/dist/lib/state.js +323 -138
- package/dist/lib/subagents.d.ts +9 -1
- package/dist/lib/subagents.js +70 -63
- package/dist/lib/sync-manifest.d.ts +81 -0
- package/dist/lib/sync-manifest.js +450 -0
- package/dist/lib/teams/agents.d.ts +103 -5
- package/dist/lib/teams/agents.js +414 -91
- package/dist/lib/teams/api.d.ts +26 -3
- package/dist/lib/teams/api.js +63 -3
- package/dist/lib/teams/debug.d.ts +6 -1
- package/dist/lib/teams/debug.js +6 -1
- package/dist/lib/teams/file_ops.d.ts +7 -1
- package/dist/lib/teams/file_ops.js +7 -1
- package/dist/lib/teams/index.d.ts +15 -0
- package/dist/lib/teams/index.js +14 -0
- package/dist/lib/teams/parsers.d.ts +4 -1
- package/dist/lib/teams/parsers.js +11 -1
- package/dist/lib/teams/persistence.d.ts +15 -1
- package/dist/lib/teams/persistence.js +102 -20
- package/dist/lib/teams/registry.d.ts +12 -1
- package/dist/lib/teams/registry.js +116 -33
- package/dist/lib/teams/summarizer.d.ts +15 -1
- package/dist/lib/teams/summarizer.js +14 -1
- package/dist/lib/teams/supervisor.d.ts +48 -0
- package/dist/lib/teams/supervisor.js +73 -0
- package/dist/lib/template.d.ts +8 -6
- package/dist/lib/template.js +8 -6
- package/dist/lib/types.d.ts +147 -8
- package/dist/lib/types.js +26 -3
- package/dist/lib/usage.d.ts +48 -1
- package/dist/lib/usage.js +97 -16
- package/dist/lib/version-duplicates.d.ts +21 -0
- package/dist/lib/version-duplicates.js +90 -0
- package/dist/lib/versions.d.ts +39 -4
- package/dist/lib/versions.js +401 -111
- package/package.json +33 -18
- package/scripts/postinstall.js +126 -30
- 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/commands.d.ts.map +0 -1
- package/dist/commands/commands.js.map +0 -1
- package/dist/commands/daemon.d.ts.map +0 -1
- package/dist/commands/daemon.js.map +0 -1
- package/dist/commands/drive.d.ts.map +0 -1
- package/dist/commands/drive.js.map +0 -1
- package/dist/commands/exec.d.ts.map +0 -1
- package/dist/commands/exec.js.map +0 -1
- package/dist/commands/fork.d.ts.map +0 -1
- package/dist/commands/fork.js.map +0 -1
- package/dist/commands/hooks.d.ts.map +0 -1
- package/dist/commands/hooks.js.map +0 -1
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/mcp.d.ts.map +0 -1
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/models.d.ts.map +0 -1
- package/dist/commands/models.js.map +0 -1
- package/dist/commands/packages.d.ts.map +0 -1
- package/dist/commands/packages.js.map +0 -1
- package/dist/commands/permissions.d.ts.map +0 -1
- package/dist/commands/permissions.js.map +0 -1
- package/dist/commands/plugins.d.ts.map +0 -1
- package/dist/commands/plugins.js.map +0 -1
- package/dist/commands/pty.d.ts.map +0 -1
- package/dist/commands/pty.js.map +0 -1
- package/dist/commands/pull.d.ts.map +0 -1
- package/dist/commands/pull.js.map +0 -1
- package/dist/commands/push.d.ts +0 -3
- package/dist/commands/push.d.ts.map +0 -1
- package/dist/commands/push.js +0 -180
- package/dist/commands/push.js.map +0 -1
- package/dist/commands/refresh-memory.d.ts.map +0 -1
- package/dist/commands/refresh-memory.js.map +0 -1
- package/dist/commands/resource-view.d.ts.map +0 -1
- package/dist/commands/resource-view.js.map +0 -1
- package/dist/commands/routines.d.ts.map +0 -1
- package/dist/commands/routines.js.map +0 -1
- package/dist/commands/rules.d.ts.map +0 -1
- package/dist/commands/rules.js.map +0 -1
- package/dist/commands/sessions-picker.d.ts.map +0 -1
- package/dist/commands/sessions-picker.js.map +0 -1
- package/dist/commands/sessions.d.ts.map +0 -1
- package/dist/commands/sessions.js.map +0 -1
- package/dist/commands/skills.d.ts.map +0 -1
- package/dist/commands/skills.js.map +0 -1
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/subagents.d.ts.map +0 -1
- package/dist/commands/subagents.js.map +0 -1
- package/dist/commands/sync.d.ts.map +0 -1
- package/dist/commands/sync.js.map +0 -1
- package/dist/commands/teams-picker.d.ts.map +0 -1
- package/dist/commands/teams-picker.js.map +0 -1
- package/dist/commands/teams.d.ts.map +0 -1
- package/dist/commands/teams.js.map +0 -1
- package/dist/commands/utils.d.ts.map +0 -1
- package/dist/commands/utils.js.map +0 -1
- package/dist/commands/versions.d.ts.map +0 -1
- package/dist/commands/versions.js.map +0 -1
- package/dist/commands/view.d.ts.map +0 -1
- package/dist/commands/view.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- 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 -184
- package/dist/lib/__tests__/models.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 -218
- package/dist/lib/__tests__/usage.test.js.map +0 -1
- package/dist/lib/agents.d.ts.map +0 -1
- package/dist/lib/agents.js.map +0 -1
- package/dist/lib/artifact-actions.d.ts.map +0 -1
- package/dist/lib/artifact-actions.js.map +0 -1
- package/dist/lib/commands.d.ts.map +0 -1
- package/dist/lib/commands.js.map +0 -1
- package/dist/lib/convert.d.ts.map +0 -1
- package/dist/lib/convert.js.map +0 -1
- package/dist/lib/daemon.d.ts.map +0 -1
- package/dist/lib/daemon.js.map +0 -1
- package/dist/lib/drive-sync.d.ts.map +0 -1
- package/dist/lib/drive-sync.js.map +0 -1
- package/dist/lib/exec.d.ts.map +0 -1
- package/dist/lib/exec.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 -110
- package/dist/lib/factory.js.map +0 -1
- package/dist/lib/git.d.ts.map +0 -1
- package/dist/lib/git.js.map +0 -1
- package/dist/lib/help.d.ts.map +0 -1
- package/dist/lib/help.js.map +0 -1
- package/dist/lib/hooks.d.ts.map +0 -1
- package/dist/lib/hooks.js.map +0 -1
- package/dist/lib/manifest.d.ts.map +0 -1
- package/dist/lib/manifest.js.map +0 -1
- package/dist/lib/markdown.d.ts.map +0 -1
- package/dist/lib/markdown.js.map +0 -1
- package/dist/lib/mcp.d.ts.map +0 -1
- package/dist/lib/mcp.js.map +0 -1
- package/dist/lib/memory-compile.d.ts.map +0 -1
- package/dist/lib/memory-compile.js.map +0 -1
- package/dist/lib/memory.d.ts.map +0 -1
- package/dist/lib/memory.js.map +0 -1
- package/dist/lib/models.d.ts.map +0 -1
- package/dist/lib/models.js.map +0 -1
- package/dist/lib/permissions.d.ts.map +0 -1
- package/dist/lib/permissions.js.map +0 -1
- package/dist/lib/picker.d.ts.map +0 -1
- package/dist/lib/picker.js.map +0 -1
- package/dist/lib/plugins.d.ts.map +0 -1
- package/dist/lib/plugins.js.map +0 -1
- package/dist/lib/pty-client.d.ts.map +0 -1
- package/dist/lib/pty-client.js.map +0 -1
- package/dist/lib/pty-server.d.ts.map +0 -1
- package/dist/lib/pty-server.js.map +0 -1
- package/dist/lib/registry.d.ts.map +0 -1
- package/dist/lib/registry.js.map +0 -1
- package/dist/lib/resources.d.ts.map +0 -1
- package/dist/lib/resources.js.map +0 -1
- package/dist/lib/routines.d.ts.map +0 -1
- package/dist/lib/routines.js.map +0 -1
- package/dist/lib/runner.d.ts.map +0 -1
- package/dist/lib/runner.js.map +0 -1
- package/dist/lib/sandbox.d.ts.map +0 -1
- package/dist/lib/sandbox.js.map +0 -1
- package/dist/lib/scheduler.d.ts.map +0 -1
- package/dist/lib/scheduler.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/db.d.ts.map +0 -1
- package/dist/lib/session/db.js.map +0 -1
- package/dist/lib/session/discover.d.ts.map +0 -1
- package/dist/lib/session/discover.js.map +0 -1
- package/dist/lib/session/parse.d.ts.map +0 -1
- package/dist/lib/session/parse.js.map +0 -1
- package/dist/lib/session/prompt.d.ts.map +0 -1
- 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.map +0 -1
- package/dist/lib/session/render.js.map +0 -1
- package/dist/lib/session/team-filter.d.ts.map +0 -1
- 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.map +0 -1
- package/dist/lib/session/types.js.map +0 -1
- package/dist/lib/shims.d.ts.map +0 -1
- package/dist/lib/shims.js.map +0 -1
- package/dist/lib/skills.d.ts.map +0 -1
- package/dist/lib/skills.js.map +0 -1
- package/dist/lib/state.d.ts.map +0 -1
- package/dist/lib/state.js.map +0 -1
- package/dist/lib/subagents.d.ts.map +0 -1
- package/dist/lib/subagents.js.map +0 -1
- package/dist/lib/teams/agents.d.ts.map +0 -1
- package/dist/lib/teams/agents.js.map +0 -1
- package/dist/lib/teams/api.d.ts.map +0 -1
- 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.map +0 -1
- package/dist/lib/teams/debug.js.map +0 -1
- package/dist/lib/teams/file_ops.d.ts.map +0 -1
- package/dist/lib/teams/file_ops.js.map +0 -1
- package/dist/lib/teams/parsers.d.ts.map +0 -1
- package/dist/lib/teams/parsers.js.map +0 -1
- package/dist/lib/teams/persistence.d.ts.map +0 -1
- 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.map +0 -1
- package/dist/lib/teams/registry.js.map +0 -1
- package/dist/lib/teams/summarizer.d.ts.map +0 -1
- package/dist/lib/teams/summarizer.js.map +0 -1
- package/dist/lib/template.d.ts.map +0 -1
- package/dist/lib/template.js.map +0 -1
- package/dist/lib/types.d.ts.map +0 -1
- package/dist/lib/types.js.map +0 -1
- package/dist/lib/usage.d.ts.map +0 -1
- package/dist/lib/usage.js.map +0 -1
- package/dist/lib/versions.d.ts.map +0 -1
- package/dist/lib/versions.js.map +0 -1
- package/scripts/rebuild-sqlite.sh +0 -46
package/dist/lib/versions.js
CHANGED
|
@@ -1,23 +1,53 @@
|
|
|
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-system/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
|
+
*/
|
|
1
17
|
import * as fs from 'fs';
|
|
2
18
|
import * as path from 'path';
|
|
3
19
|
import * as os from 'os';
|
|
4
|
-
import
|
|
20
|
+
import * as yaml from 'yaml';
|
|
21
|
+
import { exec, execFile } from 'child_process';
|
|
5
22
|
import { promisify } from 'util';
|
|
6
23
|
import chalk from 'chalk';
|
|
7
24
|
import * as TOML from 'smol-toml';
|
|
8
25
|
import { checkbox, select } from '@inquirer/prompts';
|
|
9
|
-
import { getVersionsDir, ensureAgentsDir, readMeta, writeMeta, getCommandsDir, getSkillsDir, getHooksDir,
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
26
|
+
import { getVersionsDir, ensureAgentsDir, readMeta, writeMeta, getCommandsDir, getSkillsDir, getHooksDir, getResolvedRulesDir, getUserRulesDir, clearVersionResources, recordVersionResources, getProjectAgentsDir, getPromptcutsPath, getEnabledExtraRepos, getAgentsDir, getUserAgentsDir } from './state.js';
|
|
27
|
+
import { resolveResource } from './resources.js';
|
|
28
|
+
import { AGENTS, getAccountEmail, MCP_CAPABLE_AGENTS, COMMANDS_CAPABLE_AGENTS, getMcpConfigPathForHome, parseMcpConfig, resolveAgentName, formatAgentError } from './agents.js';
|
|
29
|
+
import { applyPermissionsToVersion as applyPermsToVersion, PERMISSIONS_CAPABLE_AGENTS, discoverPermissionGroups, buildPermissionsFromGroups, CODEX_RULES_FILENAME, getActivePermissionSetName, readPermissionSetRecipe, PERMISSION_SET_ENV_VAR } from './permissions.js';
|
|
12
30
|
import { installMcpServers } from './mcp.js';
|
|
13
31
|
import { markdownToToml } from './convert.js';
|
|
14
|
-
import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion } from './shims.js';
|
|
32
|
+
import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion, ensureClaudeInsideSymlink } from './shims.js';
|
|
15
33
|
import { listInstalledSubagents, transformSubagentForClaude, syncSubagentToOpenclaw, SUBAGENT_CAPABLE_AGENTS } from './subagents.js';
|
|
16
34
|
import { registerHooksToSettings } from './hooks.js';
|
|
35
|
+
import { supports, explainSkip } from './capabilities.js';
|
|
17
36
|
import { discoverPlugins, syncPluginToVersion, isPluginSynced, pluginSupportsAgent, cleanOrphanedPluginSkills } from './plugins.js';
|
|
18
37
|
import { compileMemoryForAgent } from './memory-compile.js';
|
|
38
|
+
import { loadSyncManifest, saveSyncManifest, buildManifest, isSyncStale } from './sync-manifest.js';
|
|
19
39
|
import { PLUGINS_CAPABLE_AGENTS } from './agents.js';
|
|
40
|
+
import { safeJoin } from './paths.js';
|
|
41
|
+
import { installCommandSkillToVersion, listCommandSkillsInVersion, shouldInstallCommandAsSkill } from './command-skills.js';
|
|
42
|
+
/** Promisified exec for running shell commands. */
|
|
20
43
|
const execAsync = promisify(exec);
|
|
44
|
+
const execFileAsync = promisify(execFile);
|
|
45
|
+
const RULES_DOC_FILENAME = 'README.md';
|
|
46
|
+
// Strict shape for an agent version string. Anything outside this is rejected
|
|
47
|
+
// at parse time so it can't reach an exec/shell boundary or get interpolated
|
|
48
|
+
// into a generated bash alias. Must allow "latest" plus npm-dist-tag /
|
|
49
|
+
// semver-shaped values (digits, dots, dashes, +, _).
|
|
50
|
+
const VERSION_RE = /^(?:latest|[A-Za-z0-9._+-]{1,64})$/;
|
|
21
51
|
/**
|
|
22
52
|
* Get all available resources from ~/.agents/.
|
|
23
53
|
*/
|
|
@@ -31,14 +61,22 @@ export function getAvailableResources(cwd = process.cwd()) {
|
|
|
31
61
|
permissions: [],
|
|
32
62
|
subagents: [],
|
|
33
63
|
plugins: [],
|
|
64
|
+
promptcuts: false,
|
|
34
65
|
};
|
|
35
66
|
const projectAgentsDir = getProjectAgentsDir(cwd);
|
|
36
|
-
const userBase =
|
|
67
|
+
const userBase = getUserAgentsDir();
|
|
68
|
+
const systemBase = getAgentsDir();
|
|
37
69
|
const resourceBases = [];
|
|
38
70
|
if (projectAgentsDir) {
|
|
39
71
|
resourceBases.push({ scope: 'project', base: projectAgentsDir });
|
|
40
72
|
}
|
|
41
73
|
resourceBases.push({ scope: 'user', base: userBase });
|
|
74
|
+
resourceBases.push({ scope: 'user', base: systemBase });
|
|
75
|
+
// Extra DotAgent repos registered via `agents repo add`. Ordered last so
|
|
76
|
+
// project/user/system names win on collision.
|
|
77
|
+
for (const extra of getEnabledExtraRepos()) {
|
|
78
|
+
resourceBases.push({ scope: 'user', base: extra.dir });
|
|
79
|
+
}
|
|
42
80
|
// Commands (*.md files)
|
|
43
81
|
const commandNames = new Set();
|
|
44
82
|
for (const { base } of resourceBases) {
|
|
@@ -79,22 +117,25 @@ export function getAvailableResources(cwd = process.cwd()) {
|
|
|
79
117
|
}
|
|
80
118
|
}
|
|
81
119
|
result.hooks = Array.from(hookNames);
|
|
82
|
-
//
|
|
120
|
+
// Rules (*.md files, excluding symlinks and README)
|
|
121
|
+
// Scan 'rules/' first (canonical), then 'memory/' (legacy name) for backward compat.
|
|
83
122
|
const memoryNames = new Set();
|
|
84
123
|
for (const { base } of resourceBases) {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
124
|
+
for (const subdir of ['rules', 'memory']) {
|
|
125
|
+
const memoryDir = path.join(base, subdir);
|
|
126
|
+
if (!fs.existsSync(memoryDir))
|
|
127
|
+
continue;
|
|
128
|
+
const names = fs.readdirSync(memoryDir)
|
|
129
|
+
.filter(f => {
|
|
130
|
+
if (!f.endsWith('.md') || f === RULES_DOC_FILENAME)
|
|
131
|
+
return false;
|
|
132
|
+
const stat = fs.lstatSync(path.join(memoryDir, f));
|
|
133
|
+
return !stat.isSymbolicLink();
|
|
134
|
+
})
|
|
135
|
+
.map(f => f.replace(/\.md$/, ''));
|
|
136
|
+
for (const name of names) {
|
|
137
|
+
memoryNames.add(name);
|
|
138
|
+
}
|
|
98
139
|
}
|
|
99
140
|
}
|
|
100
141
|
result.memory = Array.from(memoryNames);
|
|
@@ -143,14 +184,28 @@ export function getAvailableResources(cwd = process.cwd()) {
|
|
|
143
184
|
// Plugins (directories with .claude-plugin/plugin.json)
|
|
144
185
|
const allPlugins = discoverPlugins();
|
|
145
186
|
result.plugins = allPlugins.map(p => p.name);
|
|
187
|
+
// Promptcuts — single file at ~/.agents/promptcuts.yaml, not per-agent.
|
|
188
|
+
// Project-scoped .agents/promptcuts.yaml is intentionally not supported
|
|
189
|
+
// (user-global shortcuts only — they follow the user, not the repo).
|
|
190
|
+
result.promptcuts = fs.existsSync(getPromptcutsPath());
|
|
146
191
|
return result;
|
|
147
192
|
}
|
|
193
|
+
// Files/dirs that are never synced into a version home (OS metadata, local tooling).
|
|
194
|
+
const SKILL_COPY_IGNORE = new Set(['.DS_Store', '.git', '.gitignore', '.venv', '__pycache__', 'node_modules']);
|
|
195
|
+
function shouldSkillEntryBeSkipped(name) {
|
|
196
|
+
return SKILL_COPY_IGNORE.has(name);
|
|
197
|
+
}
|
|
148
198
|
/**
|
|
149
199
|
* Recursively compare two directories: every file in src must exist in dest with identical content.
|
|
200
|
+
* Skips the same entries that copyDir skips (symlinks and SKILL_COPY_IGNORE members).
|
|
150
201
|
*/
|
|
151
202
|
function skillDirsMatch(src, dest) {
|
|
152
203
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
153
204
|
for (const entry of entries) {
|
|
205
|
+
if (entry.isSymbolicLink())
|
|
206
|
+
continue;
|
|
207
|
+
if (shouldSkillEntryBeSkipped(entry.name))
|
|
208
|
+
continue;
|
|
154
209
|
const srcPath = path.join(src, entry.name);
|
|
155
210
|
const destPath = path.join(dest, entry.name);
|
|
156
211
|
if (entry.isDirectory()) {
|
|
@@ -188,6 +243,7 @@ export function getActuallySyncedResources(agent, version, options = {}) {
|
|
|
188
243
|
permissions: [],
|
|
189
244
|
subagents: [],
|
|
190
245
|
plugins: [],
|
|
246
|
+
promptcuts: false,
|
|
191
247
|
};
|
|
192
248
|
// Commands - check what files exist in version home
|
|
193
249
|
const commandsDir = path.join(configDir, agentConfig.commandsSubdir);
|
|
@@ -201,21 +257,27 @@ export function getActuallySyncedResources(agent, version, options = {}) {
|
|
|
201
257
|
const skillsDir = path.join(configDir, 'skills');
|
|
202
258
|
const centralSkillsDir = getSkillsDir();
|
|
203
259
|
const projectSkillsDir = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
|
|
260
|
+
const userAgentsDir = getUserAgentsDir();
|
|
261
|
+
const extraRepos = getEnabledExtraRepos();
|
|
204
262
|
if (fs.existsSync(skillsDir)) {
|
|
205
263
|
const installedSkills = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
206
264
|
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
207
265
|
.map(d => d.name);
|
|
208
266
|
for (const skill of installedSkills) {
|
|
209
267
|
const versionSkillDir = path.join(skillsDir, skill);
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
268
|
+
const sourceCandidates = [
|
|
269
|
+
projectSkillsDir ? path.join(projectSkillsDir, skill) : null,
|
|
270
|
+
path.join(userAgentsDir, 'skills', skill),
|
|
271
|
+
path.join(centralSkillsDir, skill),
|
|
272
|
+
...extraRepos.map((e) => path.join(e.dir, 'skills', skill)),
|
|
273
|
+
];
|
|
274
|
+
const sourceDir = sourceCandidates.find((p) => p && fs.existsSync(p)) || null;
|
|
275
|
+
if (!sourceDir) {
|
|
276
|
+
// True orphan — no source in project, primary, or any extra. Still
|
|
277
|
+
// count as synced so version-home cleanup knows it's accounted for.
|
|
215
278
|
result.skills.push(skill);
|
|
216
279
|
continue;
|
|
217
280
|
}
|
|
218
|
-
const sourceDir = hasProjectSource ? projectSourceDir : centralSkillDir;
|
|
219
281
|
const allMatch = skillDirsMatch(sourceDir, versionSkillDir);
|
|
220
282
|
if (allMatch) {
|
|
221
283
|
result.skills.push(skill);
|
|
@@ -226,16 +288,19 @@ export function getActuallySyncedResources(agent, version, options = {}) {
|
|
|
226
288
|
const hooksDir = path.join(configDir, 'hooks');
|
|
227
289
|
const centralHooksDir = getHooksDir();
|
|
228
290
|
const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
|
|
291
|
+
const userHooksDir = path.join(userAgentsDir, 'hooks');
|
|
229
292
|
if (fs.existsSync(hooksDir)) {
|
|
230
293
|
const installedHooks = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
|
|
231
294
|
for (const hook of installedHooks) {
|
|
232
295
|
const projectFile = projectHooksDir ? path.join(projectHooksDir, hook) : null;
|
|
233
296
|
const centralFile = path.join(centralHooksDir, hook);
|
|
297
|
+
const userFile = path.join(userHooksDir, hook);
|
|
234
298
|
const versionFile = path.join(hooksDir, hook);
|
|
235
299
|
const hasProject = projectFile ? fs.existsSync(projectFile) : false;
|
|
300
|
+
const hasUser = fs.existsSync(userFile);
|
|
236
301
|
const hasCentral = fs.existsSync(centralFile);
|
|
237
|
-
const sourceFile = hasProject ? projectFile : centralFile;
|
|
238
|
-
if (!hasProject && !hasCentral) {
|
|
302
|
+
const sourceFile = hasProject ? projectFile : hasUser ? userFile : centralFile;
|
|
303
|
+
if (!hasProject && !hasCentral && !hasUser) {
|
|
239
304
|
result.hooks.push(hook);
|
|
240
305
|
continue;
|
|
241
306
|
}
|
|
@@ -251,15 +316,19 @@ export function getActuallySyncedResources(agent, version, options = {}) {
|
|
|
251
316
|
}
|
|
252
317
|
}
|
|
253
318
|
}
|
|
254
|
-
//
|
|
255
|
-
const memoryDir =
|
|
256
|
-
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, '
|
|
319
|
+
// Rules - check which instruction files are actually in sync (content matches)
|
|
320
|
+
const memoryDir = getResolvedRulesDir();
|
|
321
|
+
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, 'rules') : null;
|
|
322
|
+
const userMemoryDir = getUserRulesDir();
|
|
257
323
|
const memoryFiles = new Set();
|
|
258
324
|
if (fs.existsSync(memoryDir)) {
|
|
259
|
-
fs.readdirSync(memoryDir).filter(f => f.endsWith('.md')).forEach(f => memoryFiles.add(f));
|
|
325
|
+
fs.readdirSync(memoryDir).filter(f => f.endsWith('.md') && f !== RULES_DOC_FILENAME).forEach(f => memoryFiles.add(f));
|
|
260
326
|
}
|
|
261
327
|
if (projectMemoryDir && fs.existsSync(projectMemoryDir)) {
|
|
262
|
-
fs.readdirSync(projectMemoryDir).filter(f => f.endsWith('.md')).forEach(f => memoryFiles.add(f));
|
|
328
|
+
fs.readdirSync(projectMemoryDir).filter(f => f.endsWith('.md') && f !== RULES_DOC_FILENAME).forEach(f => memoryFiles.add(f));
|
|
329
|
+
}
|
|
330
|
+
if (fs.existsSync(userMemoryDir)) {
|
|
331
|
+
fs.readdirSync(userMemoryDir).filter(f => f.endsWith('.md') && f !== RULES_DOC_FILENAME).forEach(f => memoryFiles.add(f));
|
|
263
332
|
}
|
|
264
333
|
for (const file of memoryFiles) {
|
|
265
334
|
const memName = file.replace(/\.md$/, '');
|
|
@@ -269,10 +338,12 @@ export function getActuallySyncedResources(agent, version, options = {}) {
|
|
|
269
338
|
continue;
|
|
270
339
|
const projectFile = projectMemoryDir ? path.join(projectMemoryDir, file) : null;
|
|
271
340
|
const centralFile = path.join(memoryDir, file);
|
|
341
|
+
const userFile = path.join(userMemoryDir, file);
|
|
272
342
|
const hasProject = projectFile ? fs.existsSync(projectFile) : false;
|
|
343
|
+
const hasUser = fs.existsSync(userFile);
|
|
273
344
|
const hasCentral = fs.existsSync(centralFile);
|
|
274
|
-
const sourceFile = hasProject ? projectFile : centralFile;
|
|
275
|
-
if (!hasProject && !hasCentral) {
|
|
345
|
+
const sourceFile = hasProject ? projectFile : hasUser ? userFile : centralFile;
|
|
346
|
+
if (!hasProject && !hasCentral && !hasUser) {
|
|
276
347
|
result.memory.push(memName);
|
|
277
348
|
continue;
|
|
278
349
|
}
|
|
@@ -421,6 +492,9 @@ export function getNewResources(available, actuallySynced) {
|
|
|
421
492
|
permissions: available.permissions.filter(p => !actuallySynced.permissions.includes(p)),
|
|
422
493
|
subagents: available.subagents.filter(s => !actuallySynced.subagents.includes(s)),
|
|
423
494
|
plugins: available.plugins.filter(p => !actuallySynced.plugins.includes(p)),
|
|
495
|
+
// Promptcuts aren't version-scoped — the hook reads ~/.agents/promptcuts.yaml
|
|
496
|
+
// directly, so there is never a "new" per-version state to reconcile.
|
|
497
|
+
promptcuts: false,
|
|
424
498
|
};
|
|
425
499
|
}
|
|
426
500
|
/**
|
|
@@ -643,8 +717,9 @@ export async function promptResourceSelection(agent) {
|
|
|
643
717
|
if (selectedCategories.length === 0) {
|
|
644
718
|
return {};
|
|
645
719
|
}
|
|
646
|
-
// If "Select All" was picked, sync everything without per-category prompts
|
|
647
|
-
|
|
720
|
+
// If "Select All" was picked, or all individual categories are selected, sync everything without per-category prompts
|
|
721
|
+
const allCategoryKeys = availableCategories.map(c => c.key);
|
|
722
|
+
if (selectedCategories.includes(SELECT_ALL_KEY) || allCategoryKeys.every(k => selectedCategories.includes(k))) {
|
|
648
723
|
for (const c of availableCategories) {
|
|
649
724
|
selection[c.key] = 'all';
|
|
650
725
|
}
|
|
@@ -728,6 +803,11 @@ export function parseAgentSpec(spec) {
|
|
|
728
803
|
if (!AGENTS[agentName]) {
|
|
729
804
|
return null;
|
|
730
805
|
}
|
|
806
|
+
// Reject any version string that could escape an exec context or a
|
|
807
|
+
// bash-shim interpolation. Real agent versions are semver-shaped or "latest".
|
|
808
|
+
if (!VERSION_RE.test(version)) {
|
|
809
|
+
return null;
|
|
810
|
+
}
|
|
731
811
|
return {
|
|
732
812
|
agent: agentName,
|
|
733
813
|
version,
|
|
@@ -769,7 +849,7 @@ export async function getLatestNpmVersion(agent) {
|
|
|
769
849
|
if (!agentConfig.npmPackage)
|
|
770
850
|
return null;
|
|
771
851
|
try {
|
|
772
|
-
const { stdout } = await
|
|
852
|
+
const { stdout } = await execFileAsync('npm', ['view', agentConfig.npmPackage, 'version']);
|
|
773
853
|
return stdout.trim();
|
|
774
854
|
}
|
|
775
855
|
catch {
|
|
@@ -853,9 +933,15 @@ export async function installVersion(agent, version, onProgress) {
|
|
|
853
933
|
const packageSpec = version === 'latest'
|
|
854
934
|
? agentConfig.npmPackage
|
|
855
935
|
: `${agentConfig.npmPackage}@${version}`;
|
|
936
|
+
// Defense-in-depth: even if a future caller bypasses parseAgentSpec, the
|
|
937
|
+
// version string never reaches /bin/sh because we use execFile (argv form)
|
|
938
|
+
// and re-validate here.
|
|
939
|
+
if (version !== 'latest' && !VERSION_RE.test(version)) {
|
|
940
|
+
throw new Error(`Invalid version: ${JSON.stringify(version)}`);
|
|
941
|
+
}
|
|
856
942
|
try {
|
|
857
943
|
onProgress?.(`Installing ${packageSpec}...`);
|
|
858
|
-
const { stdout } = await
|
|
944
|
+
const { stdout } = await execFileAsync('npm', ['install', packageSpec], { cwd: versionDir });
|
|
859
945
|
// Determine the actual installed version
|
|
860
946
|
let installedVersion = version;
|
|
861
947
|
if (version === 'latest') {
|
|
@@ -873,8 +959,10 @@ export async function installVersion(agent, version, onProgress) {
|
|
|
873
959
|
fs.renameSync(versionDir, actualVersionDir);
|
|
874
960
|
}
|
|
875
961
|
else {
|
|
876
|
-
// Already exists
|
|
877
|
-
|
|
962
|
+
// Already exists — drop the 'latest' install artifacts but keep
|
|
963
|
+
// `home/` (may contain conversation history from sessions that
|
|
964
|
+
// ran while the user was on `latest`).
|
|
965
|
+
removeInstallArtifacts(versionDir);
|
|
878
966
|
}
|
|
879
967
|
}
|
|
880
968
|
}
|
|
@@ -886,25 +974,52 @@ export async function installVersion(agent, version, onProgress) {
|
|
|
886
974
|
}
|
|
887
975
|
// Create versioned alias (e.g., claude@2.0.65)
|
|
888
976
|
createVersionedAlias(agent, installedVersion);
|
|
977
|
+
// Claude reads its global config from CLAUDE_CONFIG_DIR/.claude.json —
|
|
978
|
+
// i.e. inside the per-version .claude dir — while the rest of agents-cli
|
|
979
|
+
// manages the home-level file. Symlink INSIDE to OUTSIDE so Claude and
|
|
980
|
+
// agents-cli see the same content.
|
|
981
|
+
if (agent === 'claude') {
|
|
982
|
+
try {
|
|
983
|
+
ensureClaudeInsideSymlink(installedVersion);
|
|
984
|
+
}
|
|
985
|
+
catch {
|
|
986
|
+
/* non-fatal; the install itself succeeded */
|
|
987
|
+
}
|
|
988
|
+
}
|
|
889
989
|
return { success: true, installedVersion };
|
|
890
990
|
}
|
|
891
991
|
catch (err) {
|
|
892
|
-
// Clean up on failure
|
|
992
|
+
// Clean up on failure — preserve `home/` in case a prior install left
|
|
993
|
+
// conversation history behind that we must not wipe on a failed reinstall.
|
|
893
994
|
if (fs.existsSync(versionDir)) {
|
|
894
|
-
|
|
995
|
+
removeInstallArtifacts(versionDir);
|
|
895
996
|
}
|
|
896
997
|
return { success: false, installedVersion: version, error: err.message };
|
|
897
998
|
}
|
|
898
999
|
}
|
|
899
1000
|
/**
|
|
900
|
-
* Remove a
|
|
1001
|
+
* Remove install artifacts from a version directory, preserving `home/` which
|
|
1002
|
+
* contains the user's conversation history, sessions, history.jsonl, tasks,
|
|
1003
|
+
* todos, file-history, etc. Called by removeVersion so that uninstalling a
|
|
1004
|
+
* version never deletes the user's transcripts.
|
|
1005
|
+
*/
|
|
1006
|
+
function removeInstallArtifacts(versionDir) {
|
|
1007
|
+
for (const entry of fs.readdirSync(versionDir)) {
|
|
1008
|
+
if (entry === 'home')
|
|
1009
|
+
continue;
|
|
1010
|
+
fs.rmSync(path.join(versionDir, entry), { recursive: true, force: true });
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Remove a specific version of an agent. Preserves `home/` under the version
|
|
1015
|
+
* directory so conversation history survives reinstalls.
|
|
901
1016
|
*/
|
|
902
1017
|
export function removeVersion(agent, version) {
|
|
903
1018
|
const versionDir = getVersionDir(agent, version);
|
|
904
1019
|
if (!fs.existsSync(versionDir)) {
|
|
905
1020
|
return false;
|
|
906
1021
|
}
|
|
907
|
-
|
|
1022
|
+
removeInstallArtifacts(versionDir);
|
|
908
1023
|
// Remove versioned alias (e.g., claude@2.0.65)
|
|
909
1024
|
removeVersionedAlias(agent, version);
|
|
910
1025
|
// Clear resource tracking for this version
|
|
@@ -935,7 +1050,9 @@ export function removeVersion(agent, version) {
|
|
|
935
1050
|
return true;
|
|
936
1051
|
}
|
|
937
1052
|
/**
|
|
938
|
-
* Remove all versions of an agent.
|
|
1053
|
+
* Remove all versions of an agent. Preserves each version's `home/` directory
|
|
1054
|
+
* so conversation history is never deleted; the per-version folders (now
|
|
1055
|
+
* containing only `home/`) remain under the agent dir.
|
|
939
1056
|
*/
|
|
940
1057
|
export function removeAllVersions(agent) {
|
|
941
1058
|
const versions = listInstalledVersions(agent);
|
|
@@ -945,11 +1062,6 @@ export function removeAllVersions(agent) {
|
|
|
945
1062
|
removed++;
|
|
946
1063
|
}
|
|
947
1064
|
}
|
|
948
|
-
// Clean up the agent directory
|
|
949
|
-
const agentDir = path.join(getVersionsDir(), agent);
|
|
950
|
-
if (fs.existsSync(agentDir)) {
|
|
951
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
952
|
-
}
|
|
953
1065
|
return removed;
|
|
954
1066
|
}
|
|
955
1067
|
/**
|
|
@@ -968,19 +1080,71 @@ export function resolveVersion(agent, projectPath) {
|
|
|
968
1080
|
return getGlobalDefault(agent);
|
|
969
1081
|
}
|
|
970
1082
|
/**
|
|
971
|
-
*
|
|
1083
|
+
* Normalize a user-supplied @version token across CLI subcommands.
|
|
1084
|
+
*
|
|
1085
|
+
* undefined / "" / "default" -> undefined (caller falls back to project pin or global default)
|
|
1086
|
+
* "latest" -> highest installed version (process.exit if none installed)
|
|
1087
|
+
* "x.y.z" (installed) -> "x.y.z"
|
|
1088
|
+
* "x.y.z" (not installed) -> process.exit with installed-list hint
|
|
1089
|
+
*
|
|
1090
|
+
* Use this anywhere the user can type `agents <cmd> claude@<token>` to keep the
|
|
1091
|
+
* vocabulary consistent. Subcommands with different semantics for `latest`
|
|
1092
|
+
* (install/remove/use, where `latest` means npm-latest) keep their existing
|
|
1093
|
+
* parsing.
|
|
1094
|
+
*/
|
|
1095
|
+
export function resolveVersionAlias(agent, raw) {
|
|
1096
|
+
if (!raw || raw === 'default')
|
|
1097
|
+
return undefined;
|
|
1098
|
+
if (raw === 'latest') {
|
|
1099
|
+
const installed = listInstalledVersions(agent);
|
|
1100
|
+
if (installed.length === 0) {
|
|
1101
|
+
console.error(chalk.red(`No ${agent} versions installed.`));
|
|
1102
|
+
console.error(chalk.gray(`Install one: agents versions install ${agent}`));
|
|
1103
|
+
process.exit(1);
|
|
1104
|
+
}
|
|
1105
|
+
return installed[installed.length - 1];
|
|
1106
|
+
}
|
|
1107
|
+
if (!isVersionInstalled(agent, raw)) {
|
|
1108
|
+
const installed = listInstalledVersions(agent);
|
|
1109
|
+
console.error(chalk.red(`${agent}@${raw} is not installed.`));
|
|
1110
|
+
if (installed.length > 0) {
|
|
1111
|
+
console.error(chalk.gray(`Installed: ${installed.join(', ')}`));
|
|
1112
|
+
}
|
|
1113
|
+
console.error(chalk.gray(`Install it: agents versions install ${agent}@${raw}`));
|
|
1114
|
+
process.exit(1);
|
|
1115
|
+
}
|
|
1116
|
+
return raw;
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Loose variant of resolveVersionAlias for record-filter contexts (sessions,
|
|
1120
|
+
* team history). Same `default`/`latest` semantics, but explicit versions
|
|
1121
|
+
* pass through unchanged so historical records of uninstalled versions remain
|
|
1122
|
+
* queryable.
|
|
1123
|
+
*/
|
|
1124
|
+
export function resolveVersionAliasLoose(agent, raw) {
|
|
1125
|
+
if (!raw || raw === 'default')
|
|
1126
|
+
return undefined;
|
|
1127
|
+
if (raw === 'latest') {
|
|
1128
|
+
const installed = listInstalledVersions(agent);
|
|
1129
|
+
return installed.length > 0 ? installed[installed.length - 1] : undefined;
|
|
1130
|
+
}
|
|
1131
|
+
return raw;
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Get version specified in a project-root agents.yaml (not the user ~/.agents-system/agents.yaml).
|
|
972
1135
|
*/
|
|
973
1136
|
export function getProjectVersion(agent, startPath) {
|
|
1137
|
+
const userAgentsYaml = path.join(getAgentsDir(), 'agents.yaml');
|
|
974
1138
|
let dir = path.resolve(startPath);
|
|
975
1139
|
while (dir !== path.dirname(dir)) {
|
|
976
|
-
const manifestPath = path.join(dir, '
|
|
977
|
-
if (fs.existsSync(manifestPath)) {
|
|
1140
|
+
const manifestPath = path.join(dir, 'agents.yaml');
|
|
1141
|
+
if (manifestPath !== userAgentsYaml && fs.existsSync(manifestPath)) {
|
|
978
1142
|
try {
|
|
979
1143
|
const content = fs.readFileSync(manifestPath, 'utf-8');
|
|
980
|
-
|
|
981
|
-
const
|
|
982
|
-
if (
|
|
983
|
-
return
|
|
1144
|
+
const parsed = yaml.parse(content);
|
|
1145
|
+
const version = parsed?.agents?.[agent];
|
|
1146
|
+
if (typeof version === 'string' && version.trim()) {
|
|
1147
|
+
return version.trim();
|
|
984
1148
|
}
|
|
985
1149
|
}
|
|
986
1150
|
catch {
|
|
@@ -1015,7 +1179,7 @@ export async function getInstalledVersion(agent, version) {
|
|
|
1015
1179
|
return null;
|
|
1016
1180
|
}
|
|
1017
1181
|
try {
|
|
1018
|
-
const { stdout } = await
|
|
1182
|
+
const { stdout } = await execFileAsync(binaryPath, ['--version']);
|
|
1019
1183
|
const match = stdout.match(/(\d+\.\d+\.\d+)/);
|
|
1020
1184
|
return match ? match[1] : version;
|
|
1021
1185
|
}
|
|
@@ -1058,10 +1222,28 @@ export function getResourceDiff(agent, version) {
|
|
|
1058
1222
|
return 'none';
|
|
1059
1223
|
}
|
|
1060
1224
|
};
|
|
1061
|
-
// Commands: check directory symlink (or individual files for Gemini)
|
|
1225
|
+
// Commands: check directory symlink (or individual files for Gemini / generated skills for newer Codex)
|
|
1062
1226
|
const centralCommands = getCommandsDir();
|
|
1063
1227
|
const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
|
|
1064
|
-
if (
|
|
1228
|
+
if (shouldInstallCommandAsSkill(agent, version)) {
|
|
1229
|
+
const centralFiles = fs.existsSync(centralCommands)
|
|
1230
|
+
? fs.readdirSync(centralCommands).filter(f => f.endsWith('.md'))
|
|
1231
|
+
: [];
|
|
1232
|
+
const centralNames = new Set(centralFiles.map(f => f.replace('.md', '')));
|
|
1233
|
+
const versionNames = new Set(listCommandSkillsInVersion(agentDir));
|
|
1234
|
+
for (const file of centralFiles) {
|
|
1235
|
+
const name = file.replace('.md', '');
|
|
1236
|
+
if (!versionNames.has(name)) {
|
|
1237
|
+
diff.commands.added.push(file);
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
for (const name of versionNames) {
|
|
1241
|
+
if (!centralNames.has(name)) {
|
|
1242
|
+
diff.commands.dangling.push(`${name}.md`);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
else if (agentConfig.format === 'toml') {
|
|
1065
1247
|
// Gemini: compare .md files in central vs .toml files in version
|
|
1066
1248
|
if (fs.existsSync(centralCommands)) {
|
|
1067
1249
|
const centralFiles = fs.readdirSync(centralCommands).filter(f => f.endsWith('.md'));
|
|
@@ -1125,10 +1307,10 @@ export function getResourceDiff(agent, version) {
|
|
|
1125
1307
|
diff.hooks.dangling = ['hooks/'];
|
|
1126
1308
|
}
|
|
1127
1309
|
}
|
|
1128
|
-
//
|
|
1129
|
-
const centralMemory =
|
|
1310
|
+
// Rules: check individual file symlinks
|
|
1311
|
+
const centralMemory = getResolvedRulesDir();
|
|
1130
1312
|
if (fs.existsSync(centralMemory)) {
|
|
1131
|
-
const memoryFiles = fs.readdirSync(centralMemory).filter(f => f.endsWith('.md'));
|
|
1313
|
+
const memoryFiles = fs.readdirSync(centralMemory).filter(f => f.endsWith('.md') && f !== RULES_DOC_FILENAME);
|
|
1132
1314
|
for (const file of memoryFiles) {
|
|
1133
1315
|
const targetName = file === 'AGENTS.md' ? agentConfig.instructionsFile : file;
|
|
1134
1316
|
const targetPath = path.join(agentDir, targetName);
|
|
@@ -1166,7 +1348,20 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1166
1348
|
const result = { commands: false, skills: false, hooks: false, memory: [], permissions: false, mcp: [], subagents: [], plugins: [] };
|
|
1167
1349
|
const cwd = options.cwd || process.cwd();
|
|
1168
1350
|
const projectAgentsDir = options.projectDir || getProjectAgentsDir(cwd);
|
|
1351
|
+
const userAgentsDir = getUserAgentsDir();
|
|
1352
|
+
// Extra DotAgent repos registered via `agents repo add`. Looked up last so
|
|
1353
|
+
// project/user/system repos win on name collisions.
|
|
1354
|
+
const extraRepos = getEnabledExtraRepos();
|
|
1169
1355
|
const available = getAvailableResources(cwd);
|
|
1356
|
+
// Fast guard: skip the entire sync when no selection is active and nothing
|
|
1357
|
+
// has changed since the last full sync. Drops steady-state cost from ~16s
|
|
1358
|
+
// (unconditional file copies) to ~2ms (stat calls + manifest read).
|
|
1359
|
+
if (!selection && !options.force) {
|
|
1360
|
+
const manifest = loadSyncManifest(agent, version);
|
|
1361
|
+
if (manifest && !isSyncStale(manifest, available, agent, version, cwd)) {
|
|
1362
|
+
return { commands: false, skills: false, hooks: false, memory: [], permissions: false, mcp: [], subagents: [], plugins: [] };
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1170
1365
|
// Helper: remove a path (symlink or real) if it exists
|
|
1171
1366
|
const removePath = (p) => {
|
|
1172
1367
|
try {
|
|
@@ -1185,12 +1380,16 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1185
1380
|
fs.mkdirSync(dest, { recursive: true });
|
|
1186
1381
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
1187
1382
|
for (const entry of entries) {
|
|
1188
|
-
|
|
1189
|
-
|
|
1383
|
+
if (entry.isSymbolicLink())
|
|
1384
|
+
continue;
|
|
1385
|
+
if (shouldSkillEntryBeSkipped(entry.name))
|
|
1386
|
+
continue;
|
|
1387
|
+
const srcPath = safeJoin(src, entry.name);
|
|
1388
|
+
const destPath = safeJoin(dest, entry.name);
|
|
1190
1389
|
if (entry.isDirectory()) {
|
|
1191
1390
|
copyDir(srcPath, destPath);
|
|
1192
1391
|
}
|
|
1193
|
-
else {
|
|
1392
|
+
else if (entry.isFile()) {
|
|
1194
1393
|
fs.copyFileSync(srcPath, destPath);
|
|
1195
1394
|
}
|
|
1196
1395
|
}
|
|
@@ -1199,8 +1398,10 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1199
1398
|
const resolveSelection = (sel, available) => {
|
|
1200
1399
|
if (sel === 'all')
|
|
1201
1400
|
return available;
|
|
1202
|
-
if (Array.isArray(sel))
|
|
1203
|
-
|
|
1401
|
+
if (Array.isArray(sel)) {
|
|
1402
|
+
const availableSet = new Set(available);
|
|
1403
|
+
return sel.filter((item) => availableSet.has(item));
|
|
1404
|
+
}
|
|
1204
1405
|
return [];
|
|
1205
1406
|
};
|
|
1206
1407
|
// Sync commands
|
|
@@ -1208,24 +1409,38 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1208
1409
|
? resolveSelection(selection.commands, available.commands)
|
|
1209
1410
|
: available.commands; // No selection = sync all
|
|
1210
1411
|
if (commandsToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
1211
|
-
const centralCommands = getCommandsDir();
|
|
1212
|
-
const projectCommandsDir = projectAgentsDir ? path.join(projectAgentsDir, 'commands') : null;
|
|
1213
1412
|
const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
|
|
1214
|
-
|
|
1413
|
+
const commandsAsSkills = shouldInstallCommandAsSkill(agent, version);
|
|
1414
|
+
if (commandsAsSkills) {
|
|
1415
|
+
removePath(commandsTarget);
|
|
1416
|
+
}
|
|
1417
|
+
else {
|
|
1418
|
+
fs.mkdirSync(commandsTarget, { recursive: true });
|
|
1419
|
+
}
|
|
1215
1420
|
const syncedCommands = [];
|
|
1216
1421
|
for (const cmd of commandsToSync) {
|
|
1217
|
-
const
|
|
1218
|
-
|
|
1219
|
-
const srcFile = projectSource && fs.existsSync(projectSource) ? projectSource : userSource;
|
|
1220
|
-
if (!fs.existsSync(srcFile))
|
|
1422
|
+
const resolved = resolveResource('commands', `${cmd}.md`, cwd);
|
|
1423
|
+
if (!resolved || fs.lstatSync(resolved.path).isSymbolicLink())
|
|
1221
1424
|
continue;
|
|
1222
|
-
|
|
1425
|
+
const srcFile = resolved.path;
|
|
1426
|
+
if (commandsAsSkills) {
|
|
1427
|
+
const skillSourceDirs = [
|
|
1428
|
+
projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null,
|
|
1429
|
+
path.join(userAgentsDir, 'skills'),
|
|
1430
|
+
getSkillsDir(),
|
|
1431
|
+
...extraRepos.map((e) => path.join(e.dir, 'skills')),
|
|
1432
|
+
];
|
|
1433
|
+
const installed = installCommandSkillToVersion(agentDir, cmd, srcFile, skillSourceDirs);
|
|
1434
|
+
if (!installed.success)
|
|
1435
|
+
continue;
|
|
1436
|
+
}
|
|
1437
|
+
else if (agentConfig.format === 'toml') {
|
|
1223
1438
|
const content = fs.readFileSync(srcFile, 'utf-8');
|
|
1224
1439
|
const tomlContent = markdownToToml(cmd, content);
|
|
1225
|
-
fs.writeFileSync(
|
|
1440
|
+
fs.writeFileSync(safeJoin(commandsTarget, `${cmd}.toml`), tomlContent);
|
|
1226
1441
|
}
|
|
1227
1442
|
else {
|
|
1228
|
-
fs.copyFileSync(srcFile,
|
|
1443
|
+
fs.copyFileSync(srcFile, safeJoin(commandsTarget, `${cmd}.md`));
|
|
1229
1444
|
}
|
|
1230
1445
|
syncedCommands.push(cmd);
|
|
1231
1446
|
}
|
|
@@ -1245,17 +1460,17 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1245
1460
|
? resolveSelection(selection.skills, available.skills)
|
|
1246
1461
|
: available.skills;
|
|
1247
1462
|
if (skillsToSync.length > 0) {
|
|
1248
|
-
const centralSkills = getSkillsDir();
|
|
1249
|
-
const projectSkills = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
|
|
1250
1463
|
const skillsTarget = path.join(agentDir, 'skills');
|
|
1251
1464
|
fs.mkdirSync(skillsTarget, { recursive: true });
|
|
1252
1465
|
const syncedSkills = [];
|
|
1253
1466
|
for (const skill of skillsToSync) {
|
|
1254
|
-
const
|
|
1255
|
-
const srcDir =
|
|
1256
|
-
|
|
1467
|
+
const resolved = resolveResource('skills', skill, cwd);
|
|
1468
|
+
const srcDir = resolved && fs.existsSync(resolved.path) && fs.lstatSync(resolved.path).isDirectory()
|
|
1469
|
+
? resolved.path
|
|
1470
|
+
: null;
|
|
1471
|
+
if (!srcDir)
|
|
1257
1472
|
continue;
|
|
1258
|
-
const destDir =
|
|
1473
|
+
const destDir = safeJoin(skillsTarget, skill);
|
|
1259
1474
|
removePath(destDir);
|
|
1260
1475
|
copyDir(srcDir, destDir);
|
|
1261
1476
|
syncedSkills.push(skill);
|
|
@@ -1266,11 +1481,11 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1266
1481
|
}
|
|
1267
1482
|
}
|
|
1268
1483
|
}
|
|
1269
|
-
// Sync hooks (if agent supports them)
|
|
1484
|
+
// Sync hooks (if agent supports them at this version)
|
|
1485
|
+
const hooksGate = supports(agent, 'hooks', version);
|
|
1270
1486
|
if (agentConfig.supportsHooks) {
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
console.warn(`hooks skipped: codex@${version} < ${CODEX_HOOKS_MIN_VERSION}`);
|
|
1487
|
+
if (!hooksGate.ok) {
|
|
1488
|
+
console.warn(explainSkip(agent, 'hooks', hooksGate, version) + ' -- skipped');
|
|
1274
1489
|
}
|
|
1275
1490
|
else {
|
|
1276
1491
|
const hooksToSync = selection
|
|
@@ -1278,25 +1493,63 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1278
1493
|
: available.hooks;
|
|
1279
1494
|
if (hooksToSync.length > 0) {
|
|
1280
1495
|
const centralHooks = getHooksDir();
|
|
1281
|
-
const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
|
|
1282
1496
|
const hooksTarget = path.join(agentDir, 'hooks');
|
|
1283
1497
|
fs.mkdirSync(hooksTarget, { recursive: true });
|
|
1284
1498
|
const syncedHooks = [];
|
|
1285
1499
|
for (const hook of hooksToSync) {
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1500
|
+
// Hooks are executable shell scripts that run on agent events. We
|
|
1501
|
+
// intentionally do NOT pull from the project's own .agents/hooks/
|
|
1502
|
+
// directory: that would let any cloned public repo plant an
|
|
1503
|
+
// executable that fires the next time the user runs `agents use`
|
|
1504
|
+
// inside that repo. Hooks must come from the user's central
|
|
1505
|
+
// ~/.agents/hooks/ or an explicitly enabled extra repo.
|
|
1506
|
+
const candidates = [
|
|
1507
|
+
safeJoin(path.join(userAgentsDir, 'hooks'), hook),
|
|
1508
|
+
safeJoin(centralHooks, hook),
|
|
1509
|
+
...extraRepos.map((e) => safeJoin(path.join(e.dir, 'hooks'), hook)),
|
|
1510
|
+
];
|
|
1511
|
+
const srcFile = candidates.find((p) => p && fs.existsSync(p) && !fs.lstatSync(p).isSymbolicLink()) || null;
|
|
1512
|
+
if (!srcFile)
|
|
1289
1513
|
continue;
|
|
1290
|
-
const destFile =
|
|
1514
|
+
const destFile = safeJoin(hooksTarget, hook);
|
|
1291
1515
|
fs.copyFileSync(srcFile, destFile);
|
|
1292
1516
|
fs.chmodSync(destFile, 0o755);
|
|
1293
1517
|
syncedHooks.push(hook);
|
|
1294
1518
|
}
|
|
1519
|
+
// Remove orphan hook files that exist in version home but not in any trusted source.
|
|
1520
|
+
const centralHookNames = new Set(fs.existsSync(getHooksDir())
|
|
1521
|
+
? fs.readdirSync(getHooksDir()).filter(f => !f.startsWith('.'))
|
|
1522
|
+
: []);
|
|
1523
|
+
{
|
|
1524
|
+
const hooksDir = path.join(userAgentsDir, 'hooks');
|
|
1525
|
+
if (fs.existsSync(hooksDir)) {
|
|
1526
|
+
for (const file of fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'))) {
|
|
1527
|
+
centralHookNames.add(file);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
for (const extra of extraRepos) {
|
|
1532
|
+
const hooksDir = path.join(extra.dir, 'hooks');
|
|
1533
|
+
if (fs.existsSync(hooksDir)) {
|
|
1534
|
+
for (const file of fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'))) {
|
|
1535
|
+
centralHookNames.add(file);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
if (fs.existsSync(hooksTarget)) {
|
|
1540
|
+
for (const file of fs.readdirSync(hooksTarget).filter(f => !f.startsWith('.'))) {
|
|
1541
|
+
if (!centralHookNames.has(file)) {
|
|
1542
|
+
removePath(safeJoin(hooksTarget, file));
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1295
1546
|
result.hooks = syncedHooks.length > 0;
|
|
1296
1547
|
if (syncedHooks.length > 0) {
|
|
1297
1548
|
recordVersionResources(agent, version, 'hooks', syncedHooks);
|
|
1298
1549
|
}
|
|
1299
|
-
|
|
1550
|
+
// Register hooks into agent-native settings.json/hooks.json. Gemini
|
|
1551
|
+
// shipped hooks in 0.26.0; gate already passed above so this is safe.
|
|
1552
|
+
if (agent === 'claude' || agent === 'codex' || agent === 'gemini') {
|
|
1300
1553
|
registerHooksToSettings(agent, versionHome);
|
|
1301
1554
|
}
|
|
1302
1555
|
}
|
|
@@ -1307,19 +1560,23 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1307
1560
|
? resolveSelection(selection.memory, available.memory)
|
|
1308
1561
|
: available.memory;
|
|
1309
1562
|
if (memoryToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
1310
|
-
const centralMemory =
|
|
1311
|
-
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, '
|
|
1563
|
+
const centralMemory = getResolvedRulesDir();
|
|
1564
|
+
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, 'rules') : null;
|
|
1565
|
+
const userMemoryDir = getUserRulesDir();
|
|
1312
1566
|
const syncedMemory = [];
|
|
1313
1567
|
const agentSupportsImports = !!agentConfig.capabilities.memoryImports;
|
|
1314
1568
|
for (const mem of memoryToSync) {
|
|
1315
|
-
const
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1569
|
+
const candidates = [
|
|
1570
|
+
projectMemoryDir ? safeJoin(projectMemoryDir, `${mem}.md`) : null,
|
|
1571
|
+
safeJoin(userMemoryDir, `${mem}.md`),
|
|
1572
|
+
safeJoin(centralMemory, `${mem}.md`),
|
|
1573
|
+
...extraRepos.map((e) => safeJoin(path.join(e.dir, 'rules'), `${mem}.md`)),
|
|
1574
|
+
];
|
|
1575
|
+
const srcFile = candidates.find((p) => p && fs.existsSync(p) && !fs.lstatSync(p).isSymbolicLink()) || null;
|
|
1576
|
+
if (!srcFile)
|
|
1320
1577
|
continue;
|
|
1321
1578
|
const targetName = mem === 'AGENTS' ? agentConfig.instructionsFile : `${mem}.md`;
|
|
1322
|
-
const destFile =
|
|
1579
|
+
const destFile = safeJoin(agentDir, targetName);
|
|
1323
1580
|
removePath(destFile);
|
|
1324
1581
|
// For the primary memory file (AGENTS.md), agents that don't natively
|
|
1325
1582
|
// resolve @-imports get a compiled (inlined) copy + sidecar manifest.
|
|
@@ -1338,13 +1595,43 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1338
1595
|
recordVersionResources(agent, version, 'memory', syncedMemory);
|
|
1339
1596
|
}
|
|
1340
1597
|
}
|
|
1341
|
-
// Apply permissions (if agent supports them)
|
|
1342
|
-
//
|
|
1598
|
+
// Apply permissions (if agent supports them).
|
|
1599
|
+
// Groups live in ~/.agents/permissions/groups/. Optional recipes in
|
|
1600
|
+
// ~/.agents/permissions/sets/<name>.yaml pick a subset via `includes:`.
|
|
1601
|
+
// If AGENTS_PERMISSION_SET is set, we resolve that recipe and use its
|
|
1602
|
+
// includes list as the group filter (intersected with groups on disk).
|
|
1343
1603
|
const permissionGroups = discoverPermissionGroups();
|
|
1344
1604
|
const allGroupNames = permissionGroups.map(g => g.name);
|
|
1345
|
-
const
|
|
1346
|
-
|
|
1347
|
-
|
|
1605
|
+
const activeSetName = getActivePermissionSetName();
|
|
1606
|
+
let setFilteredGroups = null;
|
|
1607
|
+
if (activeSetName) {
|
|
1608
|
+
const recipe = readPermissionSetRecipe(activeSetName);
|
|
1609
|
+
if (recipe) {
|
|
1610
|
+
const available = new Set(allGroupNames);
|
|
1611
|
+
setFilteredGroups = recipe.includes.filter(g => available.has(g));
|
|
1612
|
+
}
|
|
1613
|
+
else {
|
|
1614
|
+
console.warn(`${PERMISSION_SET_ENV_VAR}=${activeSetName} but no recipe at ~/.agents/permissions/sets/${activeSetName}.yaml — falling back to all groups`);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
let permsToSync;
|
|
1618
|
+
if (selection) {
|
|
1619
|
+
permsToSync = resolveSelection(selection.permissions, allGroupNames);
|
|
1620
|
+
// If a set recipe is active, the recipe's includes list always wins —
|
|
1621
|
+
// even when the caller passed an explicit array via selection. Without
|
|
1622
|
+
// this intersection, `agents add`'s buildAutomaticSelection would pass
|
|
1623
|
+
// every group name discovered on disk (including 99-deny), bypassing
|
|
1624
|
+
// the sandbox filter.
|
|
1625
|
+
if (setFilteredGroups) {
|
|
1626
|
+
const filterSet = new Set(setFilteredGroups);
|
|
1627
|
+
permsToSync = permsToSync.filter(g => filterSet.has(g));
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
else {
|
|
1631
|
+
permsToSync = PERMISSIONS_CAPABLE_AGENTS.includes(agent)
|
|
1632
|
+
? (setFilteredGroups ?? allGroupNames)
|
|
1633
|
+
: [];
|
|
1634
|
+
}
|
|
1348
1635
|
if (permsToSync.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
|
|
1349
1636
|
// Build permissions from selected groups
|
|
1350
1637
|
const builtPerms = buildPermissionsFromGroups(permsToSync);
|
|
@@ -1386,12 +1673,12 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1386
1673
|
const agentsDir = path.join(agentDir, 'agents');
|
|
1387
1674
|
fs.mkdirSync(agentsDir, { recursive: true });
|
|
1388
1675
|
const transformed = transformSubagentForClaude(subagent.path);
|
|
1389
|
-
fs.writeFileSync(
|
|
1676
|
+
fs.writeFileSync(safeJoin(agentsDir, `${subagent.name}.md`), transformed);
|
|
1390
1677
|
result.subagents.push(subagent.name);
|
|
1391
1678
|
}
|
|
1392
1679
|
else if (agent === 'openclaw') {
|
|
1393
1680
|
// OpenClaw: copy full directory, rename AGENT.md -> AGENTS.md
|
|
1394
|
-
const targetDir = path.join(versionHome, '.openclaw', subagent.name);
|
|
1681
|
+
const targetDir = safeJoin(path.join(versionHome, '.openclaw'), subagent.name);
|
|
1395
1682
|
const syncResult = syncSubagentToOpenclaw(subagent.path, targetDir);
|
|
1396
1683
|
if (syncResult.success) {
|
|
1397
1684
|
result.subagents.push(subagent.name);
|
|
@@ -1427,6 +1714,10 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1427
1714
|
recordVersionResources(agent, version, 'plugins', result.plugins);
|
|
1428
1715
|
}
|
|
1429
1716
|
}
|
|
1717
|
+
// Write manifest after a successful full sync so the next launch can skip this work.
|
|
1718
|
+
if (!selection) {
|
|
1719
|
+
saveSyncManifest(agent, version, buildManifest(agent, version, available, cwd));
|
|
1720
|
+
}
|
|
1430
1721
|
return result;
|
|
1431
1722
|
}
|
|
1432
1723
|
/**
|
|
@@ -1712,4 +2003,3 @@ export async function promptAgentVersionSelection(availableAgents, options = {})
|
|
|
1712
2003
|
}
|
|
1713
2004
|
return { selectedAgents, versionSelections };
|
|
1714
2005
|
}
|
|
1715
|
-
//# sourceMappingURL=versions.js.map
|