@phnx-labs/agents-cli 0.1.0 → 1.14.1
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 +283 -372
- 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 +8 -1
- package/dist/commands/cloud.js +108 -22
- 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 +96 -27
- 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 +16 -4
- 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 +9 -1
- package/dist/commands/profiles.js +56 -7
- 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 +58 -128
- 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 +8 -1
- package/dist/commands/secrets.js +235 -63
- 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 +188 -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 +317 -24
- package/dist/index.d.ts +7 -2
- package/dist/index.js +172 -34
- 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 +197 -21
- 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 +9 -3
- package/dist/lib/cloud/codex.js +53 -13
- package/dist/lib/cloud/factory.d.ts +8 -3
- package/dist/lib/cloud/factory.js +19 -3
- package/dist/lib/cloud/registry.d.ts +10 -1
- package/dist/lib/cloud/registry.js +14 -3
- package/dist/lib/cloud/rush.d.ts +63 -3
- package/dist/lib/cloud/rush.js +273 -20
- package/dist/lib/cloud/store.d.ts +13 -1
- package/dist/lib/cloud/store.js +23 -4
- package/dist/lib/cloud/stream.d.ts +6 -1
- package/dist/lib/cloud/stream.js +95 -39
- package/dist/lib/cloud/types.d.ts +153 -8
- package/dist/lib/cloud/types.js +34 -2
- 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 +23 -6
- package/dist/lib/exec.js +53 -17
- 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 +119 -101
- 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 +10 -4
- package/dist/lib/models.js +36 -15
- 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 +10 -1
- package/dist/lib/profiles-presets.js +9 -1
- package/dist/lib/profiles.d.ts +35 -1
- package/dist/lib/profiles.js +36 -15
- 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 +40 -13
- package/dist/lib/rotate.js +238 -40
- 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 → secrets/bundles.d.ts} +12 -2
- package/dist/lib/{secrets-bundles.js → secrets/bundles.js} +38 -17
- 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 +12 -1
- package/dist/lib/session/artifacts.js +25 -5
- 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 +16 -2
- package/dist/lib/session/types.js +10 -2
- package/dist/lib/shims.d.ts +64 -5
- package/dist/lib/shims.js +309 -47
- 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 +112 -27
- package/dist/lib/state.js +320 -148
- 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 +32 -1
- package/dist/lib/usage.js +70 -6
- package/dist/lib/version-duplicates.d.ts +21 -0
- package/dist/lib/version-duplicates.js +90 -0
- package/dist/lib/versions.d.ts +33 -4
- package/dist/lib/versions.js +376 -108
- package/package.json +32 -17
- 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/cloud.d.ts.map +0 -1
- package/dist/commands/cloud.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/profiles.d.ts.map +0 -1
- package/dist/commands/profiles.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/secrets.d.ts.map +0 -1
- package/dist/commands/secrets.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 -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 -39
- 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.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/cloud/codex.d.ts.map +0 -1
- package/dist/lib/cloud/codex.js.map +0 -1
- package/dist/lib/cloud/factory.d.ts.map +0 -1
- package/dist/lib/cloud/factory.js.map +0 -1
- package/dist/lib/cloud/registry.d.ts.map +0 -1
- package/dist/lib/cloud/registry.js.map +0 -1
- package/dist/lib/cloud/rush.d.ts.map +0 -1
- package/dist/lib/cloud/rush.js.map +0 -1
- package/dist/lib/cloud/store.d.ts.map +0 -1
- package/dist/lib/cloud/store.js.map +0 -1
- package/dist/lib/cloud/stream.d.ts.map +0 -1
- package/dist/lib/cloud/stream.js.map +0 -1
- package/dist/lib/cloud/types.d.ts.map +0 -1
- package/dist/lib/cloud/types.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/profiles-keychain.d.ts +0 -3
- package/dist/lib/profiles-keychain.d.ts.map +0 -1
- package/dist/lib/profiles-keychain.js +0 -10
- package/dist/lib/profiles-keychain.js.map +0 -1
- package/dist/lib/profiles-presets.d.ts.map +0 -1
- package/dist/lib/profiles-presets.js.map +0 -1
- package/dist/lib/profiles.d.ts.map +0 -1
- package/dist/lib/profiles.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/rotate.d.ts.map +0 -1
- package/dist/lib/rotate.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/secrets-bundles.d.ts.map +0 -1
- package/dist/lib/secrets-bundles.js.map +0 -1
- package/dist/lib/secrets.d.ts +0 -27
- package/dist/lib/secrets.d.ts.map +0 -1
- package/dist/lib/secrets.js +0 -127
- 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/artifacts.d.ts.map +0 -1
- package/dist/lib/session/artifacts.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,24 +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
20
|
import * as yaml from 'yaml';
|
|
5
|
-
import { exec } from 'child_process';
|
|
21
|
+
import { exec, execFile } from 'child_process';
|
|
6
22
|
import { promisify } from 'util';
|
|
7
23
|
import chalk from 'chalk';
|
|
8
24
|
import * as TOML from 'smol-toml';
|
|
9
25
|
import { checkbox, select } from '@inquirer/prompts';
|
|
10
|
-
import { getVersionsDir, ensureAgentsDir, readMeta, writeMeta, getCommandsDir, getSkillsDir, getHooksDir,
|
|
11
|
-
import {
|
|
12
|
-
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';
|
|
13
30
|
import { installMcpServers } from './mcp.js';
|
|
14
31
|
import { markdownToToml } from './convert.js';
|
|
15
|
-
import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion } from './shims.js';
|
|
32
|
+
import { createVersionedAlias, removeVersionedAlias, getConfigSymlinkVersion, ensureClaudeInsideSymlink } from './shims.js';
|
|
16
33
|
import { listInstalledSubagents, transformSubagentForClaude, syncSubagentToOpenclaw, SUBAGENT_CAPABLE_AGENTS } from './subagents.js';
|
|
17
34
|
import { registerHooksToSettings } from './hooks.js';
|
|
35
|
+
import { supports, explainSkip } from './capabilities.js';
|
|
18
36
|
import { discoverPlugins, syncPluginToVersion, isPluginSynced, pluginSupportsAgent, cleanOrphanedPluginSkills } from './plugins.js';
|
|
19
37
|
import { compileMemoryForAgent } from './memory-compile.js';
|
|
38
|
+
import { loadSyncManifest, saveSyncManifest, buildManifest, isSyncStale } from './sync-manifest.js';
|
|
20
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. */
|
|
21
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})$/;
|
|
22
51
|
/**
|
|
23
52
|
* Get all available resources from ~/.agents/.
|
|
24
53
|
*/
|
|
@@ -35,12 +64,19 @@ export function getAvailableResources(cwd = process.cwd()) {
|
|
|
35
64
|
promptcuts: false,
|
|
36
65
|
};
|
|
37
66
|
const projectAgentsDir = getProjectAgentsDir(cwd);
|
|
38
|
-
const userBase =
|
|
67
|
+
const userBase = getUserAgentsDir();
|
|
68
|
+
const systemBase = getAgentsDir();
|
|
39
69
|
const resourceBases = [];
|
|
40
70
|
if (projectAgentsDir) {
|
|
41
71
|
resourceBases.push({ scope: 'project', base: projectAgentsDir });
|
|
42
72
|
}
|
|
43
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
|
+
}
|
|
44
80
|
// Commands (*.md files)
|
|
45
81
|
const commandNames = new Set();
|
|
46
82
|
for (const { base } of resourceBases) {
|
|
@@ -81,22 +117,25 @@ export function getAvailableResources(cwd = process.cwd()) {
|
|
|
81
117
|
}
|
|
82
118
|
}
|
|
83
119
|
result.hooks = Array.from(hookNames);
|
|
84
|
-
//
|
|
120
|
+
// Rules (*.md files, excluding symlinks and README)
|
|
121
|
+
// Scan 'rules/' first (canonical), then 'memory/' (legacy name) for backward compat.
|
|
85
122
|
const memoryNames = new Set();
|
|
86
123
|
for (const { base } of resourceBases) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
+
}
|
|
100
139
|
}
|
|
101
140
|
}
|
|
102
141
|
result.memory = Array.from(memoryNames);
|
|
@@ -151,12 +190,22 @@ export function getAvailableResources(cwd = process.cwd()) {
|
|
|
151
190
|
result.promptcuts = fs.existsSync(getPromptcutsPath());
|
|
152
191
|
return result;
|
|
153
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
|
+
}
|
|
154
198
|
/**
|
|
155
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).
|
|
156
201
|
*/
|
|
157
202
|
function skillDirsMatch(src, dest) {
|
|
158
203
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
159
204
|
for (const entry of entries) {
|
|
205
|
+
if (entry.isSymbolicLink())
|
|
206
|
+
continue;
|
|
207
|
+
if (shouldSkillEntryBeSkipped(entry.name))
|
|
208
|
+
continue;
|
|
160
209
|
const srcPath = path.join(src, entry.name);
|
|
161
210
|
const destPath = path.join(dest, entry.name);
|
|
162
211
|
if (entry.isDirectory()) {
|
|
@@ -208,21 +257,27 @@ export function getActuallySyncedResources(agent, version, options = {}) {
|
|
|
208
257
|
const skillsDir = path.join(configDir, 'skills');
|
|
209
258
|
const centralSkillsDir = getSkillsDir();
|
|
210
259
|
const projectSkillsDir = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
|
|
260
|
+
const userAgentsDir = getUserAgentsDir();
|
|
261
|
+
const extraRepos = getEnabledExtraRepos();
|
|
211
262
|
if (fs.existsSync(skillsDir)) {
|
|
212
263
|
const installedSkills = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
213
264
|
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
214
265
|
.map(d => d.name);
|
|
215
266
|
for (const skill of installedSkills) {
|
|
216
267
|
const versionSkillDir = path.join(skillsDir, skill);
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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.
|
|
222
278
|
result.skills.push(skill);
|
|
223
279
|
continue;
|
|
224
280
|
}
|
|
225
|
-
const sourceDir = hasProjectSource ? projectSourceDir : centralSkillDir;
|
|
226
281
|
const allMatch = skillDirsMatch(sourceDir, versionSkillDir);
|
|
227
282
|
if (allMatch) {
|
|
228
283
|
result.skills.push(skill);
|
|
@@ -233,16 +288,19 @@ export function getActuallySyncedResources(agent, version, options = {}) {
|
|
|
233
288
|
const hooksDir = path.join(configDir, 'hooks');
|
|
234
289
|
const centralHooksDir = getHooksDir();
|
|
235
290
|
const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
|
|
291
|
+
const userHooksDir = path.join(userAgentsDir, 'hooks');
|
|
236
292
|
if (fs.existsSync(hooksDir)) {
|
|
237
293
|
const installedHooks = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
|
|
238
294
|
for (const hook of installedHooks) {
|
|
239
295
|
const projectFile = projectHooksDir ? path.join(projectHooksDir, hook) : null;
|
|
240
296
|
const centralFile = path.join(centralHooksDir, hook);
|
|
297
|
+
const userFile = path.join(userHooksDir, hook);
|
|
241
298
|
const versionFile = path.join(hooksDir, hook);
|
|
242
299
|
const hasProject = projectFile ? fs.existsSync(projectFile) : false;
|
|
300
|
+
const hasUser = fs.existsSync(userFile);
|
|
243
301
|
const hasCentral = fs.existsSync(centralFile);
|
|
244
|
-
const sourceFile = hasProject ? projectFile : centralFile;
|
|
245
|
-
if (!hasProject && !hasCentral) {
|
|
302
|
+
const sourceFile = hasProject ? projectFile : hasUser ? userFile : centralFile;
|
|
303
|
+
if (!hasProject && !hasCentral && !hasUser) {
|
|
246
304
|
result.hooks.push(hook);
|
|
247
305
|
continue;
|
|
248
306
|
}
|
|
@@ -258,15 +316,19 @@ export function getActuallySyncedResources(agent, version, options = {}) {
|
|
|
258
316
|
}
|
|
259
317
|
}
|
|
260
318
|
}
|
|
261
|
-
//
|
|
262
|
-
const memoryDir =
|
|
263
|
-
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();
|
|
264
323
|
const memoryFiles = new Set();
|
|
265
324
|
if (fs.existsSync(memoryDir)) {
|
|
266
|
-
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));
|
|
267
326
|
}
|
|
268
327
|
if (projectMemoryDir && fs.existsSync(projectMemoryDir)) {
|
|
269
|
-
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));
|
|
270
332
|
}
|
|
271
333
|
for (const file of memoryFiles) {
|
|
272
334
|
const memName = file.replace(/\.md$/, '');
|
|
@@ -276,10 +338,12 @@ export function getActuallySyncedResources(agent, version, options = {}) {
|
|
|
276
338
|
continue;
|
|
277
339
|
const projectFile = projectMemoryDir ? path.join(projectMemoryDir, file) : null;
|
|
278
340
|
const centralFile = path.join(memoryDir, file);
|
|
341
|
+
const userFile = path.join(userMemoryDir, file);
|
|
279
342
|
const hasProject = projectFile ? fs.existsSync(projectFile) : false;
|
|
343
|
+
const hasUser = fs.existsSync(userFile);
|
|
280
344
|
const hasCentral = fs.existsSync(centralFile);
|
|
281
|
-
const sourceFile = hasProject ? projectFile : centralFile;
|
|
282
|
-
if (!hasProject && !hasCentral) {
|
|
345
|
+
const sourceFile = hasProject ? projectFile : hasUser ? userFile : centralFile;
|
|
346
|
+
if (!hasProject && !hasCentral && !hasUser) {
|
|
283
347
|
result.memory.push(memName);
|
|
284
348
|
continue;
|
|
285
349
|
}
|
|
@@ -653,8 +717,9 @@ export async function promptResourceSelection(agent) {
|
|
|
653
717
|
if (selectedCategories.length === 0) {
|
|
654
718
|
return {};
|
|
655
719
|
}
|
|
656
|
-
// If "Select All" was picked, sync everything without per-category prompts
|
|
657
|
-
|
|
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))) {
|
|
658
723
|
for (const c of availableCategories) {
|
|
659
724
|
selection[c.key] = 'all';
|
|
660
725
|
}
|
|
@@ -738,6 +803,11 @@ export function parseAgentSpec(spec) {
|
|
|
738
803
|
if (!AGENTS[agentName]) {
|
|
739
804
|
return null;
|
|
740
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
|
+
}
|
|
741
811
|
return {
|
|
742
812
|
agent: agentName,
|
|
743
813
|
version,
|
|
@@ -779,7 +849,7 @@ export async function getLatestNpmVersion(agent) {
|
|
|
779
849
|
if (!agentConfig.npmPackage)
|
|
780
850
|
return null;
|
|
781
851
|
try {
|
|
782
|
-
const { stdout } = await
|
|
852
|
+
const { stdout } = await execFileAsync('npm', ['view', agentConfig.npmPackage, 'version']);
|
|
783
853
|
return stdout.trim();
|
|
784
854
|
}
|
|
785
855
|
catch {
|
|
@@ -863,9 +933,15 @@ export async function installVersion(agent, version, onProgress) {
|
|
|
863
933
|
const packageSpec = version === 'latest'
|
|
864
934
|
? agentConfig.npmPackage
|
|
865
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
|
+
}
|
|
866
942
|
try {
|
|
867
943
|
onProgress?.(`Installing ${packageSpec}...`);
|
|
868
|
-
const { stdout } = await
|
|
944
|
+
const { stdout } = await execFileAsync('npm', ['install', packageSpec], { cwd: versionDir });
|
|
869
945
|
// Determine the actual installed version
|
|
870
946
|
let installedVersion = version;
|
|
871
947
|
if (version === 'latest') {
|
|
@@ -883,8 +959,10 @@ export async function installVersion(agent, version, onProgress) {
|
|
|
883
959
|
fs.renameSync(versionDir, actualVersionDir);
|
|
884
960
|
}
|
|
885
961
|
else {
|
|
886
|
-
// Already exists
|
|
887
|
-
|
|
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);
|
|
888
966
|
}
|
|
889
967
|
}
|
|
890
968
|
}
|
|
@@ -896,25 +974,52 @@ export async function installVersion(agent, version, onProgress) {
|
|
|
896
974
|
}
|
|
897
975
|
// Create versioned alias (e.g., claude@2.0.65)
|
|
898
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
|
+
}
|
|
899
989
|
return { success: true, installedVersion };
|
|
900
990
|
}
|
|
901
991
|
catch (err) {
|
|
902
|
-
// 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.
|
|
903
994
|
if (fs.existsSync(versionDir)) {
|
|
904
|
-
|
|
995
|
+
removeInstallArtifacts(versionDir);
|
|
905
996
|
}
|
|
906
997
|
return { success: false, installedVersion: version, error: err.message };
|
|
907
998
|
}
|
|
908
999
|
}
|
|
909
1000
|
/**
|
|
910
|
-
* 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.
|
|
911
1016
|
*/
|
|
912
1017
|
export function removeVersion(agent, version) {
|
|
913
1018
|
const versionDir = getVersionDir(agent, version);
|
|
914
1019
|
if (!fs.existsSync(versionDir)) {
|
|
915
1020
|
return false;
|
|
916
1021
|
}
|
|
917
|
-
|
|
1022
|
+
removeInstallArtifacts(versionDir);
|
|
918
1023
|
// Remove versioned alias (e.g., claude@2.0.65)
|
|
919
1024
|
removeVersionedAlias(agent, version);
|
|
920
1025
|
// Clear resource tracking for this version
|
|
@@ -945,7 +1050,9 @@ export function removeVersion(agent, version) {
|
|
|
945
1050
|
return true;
|
|
946
1051
|
}
|
|
947
1052
|
/**
|
|
948
|
-
* 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.
|
|
949
1056
|
*/
|
|
950
1057
|
export function removeAllVersions(agent) {
|
|
951
1058
|
const versions = listInstalledVersions(agent);
|
|
@@ -955,11 +1062,6 @@ export function removeAllVersions(agent) {
|
|
|
955
1062
|
removed++;
|
|
956
1063
|
}
|
|
957
1064
|
}
|
|
958
|
-
// Clean up the agent directory
|
|
959
|
-
const agentDir = path.join(getVersionsDir(), agent);
|
|
960
|
-
if (fs.existsSync(agentDir)) {
|
|
961
|
-
fs.rmSync(agentDir, { recursive: true, force: true });
|
|
962
|
-
}
|
|
963
1065
|
return removed;
|
|
964
1066
|
}
|
|
965
1067
|
/**
|
|
@@ -978,10 +1080,61 @@ export function resolveVersion(agent, projectPath) {
|
|
|
978
1080
|
return getGlobalDefault(agent);
|
|
979
1081
|
}
|
|
980
1082
|
/**
|
|
981
|
-
*
|
|
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).
|
|
982
1135
|
*/
|
|
983
1136
|
export function getProjectVersion(agent, startPath) {
|
|
984
|
-
const userAgentsYaml = path.join(
|
|
1137
|
+
const userAgentsYaml = path.join(getAgentsDir(), 'agents.yaml');
|
|
985
1138
|
let dir = path.resolve(startPath);
|
|
986
1139
|
while (dir !== path.dirname(dir)) {
|
|
987
1140
|
const manifestPath = path.join(dir, 'agents.yaml');
|
|
@@ -1026,7 +1179,7 @@ export async function getInstalledVersion(agent, version) {
|
|
|
1026
1179
|
return null;
|
|
1027
1180
|
}
|
|
1028
1181
|
try {
|
|
1029
|
-
const { stdout } = await
|
|
1182
|
+
const { stdout } = await execFileAsync(binaryPath, ['--version']);
|
|
1030
1183
|
const match = stdout.match(/(\d+\.\d+\.\d+)/);
|
|
1031
1184
|
return match ? match[1] : version;
|
|
1032
1185
|
}
|
|
@@ -1069,10 +1222,28 @@ export function getResourceDiff(agent, version) {
|
|
|
1069
1222
|
return 'none';
|
|
1070
1223
|
}
|
|
1071
1224
|
};
|
|
1072
|
-
// Commands: check directory symlink (or individual files for Gemini)
|
|
1225
|
+
// Commands: check directory symlink (or individual files for Gemini / generated skills for newer Codex)
|
|
1073
1226
|
const centralCommands = getCommandsDir();
|
|
1074
1227
|
const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
|
|
1075
|
-
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') {
|
|
1076
1247
|
// Gemini: compare .md files in central vs .toml files in version
|
|
1077
1248
|
if (fs.existsSync(centralCommands)) {
|
|
1078
1249
|
const centralFiles = fs.readdirSync(centralCommands).filter(f => f.endsWith('.md'));
|
|
@@ -1136,10 +1307,10 @@ export function getResourceDiff(agent, version) {
|
|
|
1136
1307
|
diff.hooks.dangling = ['hooks/'];
|
|
1137
1308
|
}
|
|
1138
1309
|
}
|
|
1139
|
-
//
|
|
1140
|
-
const centralMemory =
|
|
1310
|
+
// Rules: check individual file symlinks
|
|
1311
|
+
const centralMemory = getResolvedRulesDir();
|
|
1141
1312
|
if (fs.existsSync(centralMemory)) {
|
|
1142
|
-
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);
|
|
1143
1314
|
for (const file of memoryFiles) {
|
|
1144
1315
|
const targetName = file === 'AGENTS.md' ? agentConfig.instructionsFile : file;
|
|
1145
1316
|
const targetPath = path.join(agentDir, targetName);
|
|
@@ -1177,7 +1348,20 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1177
1348
|
const result = { commands: false, skills: false, hooks: false, memory: [], permissions: false, mcp: [], subagents: [], plugins: [] };
|
|
1178
1349
|
const cwd = options.cwd || process.cwd();
|
|
1179
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();
|
|
1180
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
|
+
}
|
|
1181
1365
|
// Helper: remove a path (symlink or real) if it exists
|
|
1182
1366
|
const removePath = (p) => {
|
|
1183
1367
|
try {
|
|
@@ -1196,12 +1380,16 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1196
1380
|
fs.mkdirSync(dest, { recursive: true });
|
|
1197
1381
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
1198
1382
|
for (const entry of entries) {
|
|
1199
|
-
|
|
1200
|
-
|
|
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);
|
|
1201
1389
|
if (entry.isDirectory()) {
|
|
1202
1390
|
copyDir(srcPath, destPath);
|
|
1203
1391
|
}
|
|
1204
|
-
else {
|
|
1392
|
+
else if (entry.isFile()) {
|
|
1205
1393
|
fs.copyFileSync(srcPath, destPath);
|
|
1206
1394
|
}
|
|
1207
1395
|
}
|
|
@@ -1210,8 +1398,10 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1210
1398
|
const resolveSelection = (sel, available) => {
|
|
1211
1399
|
if (sel === 'all')
|
|
1212
1400
|
return available;
|
|
1213
|
-
if (Array.isArray(sel))
|
|
1214
|
-
|
|
1401
|
+
if (Array.isArray(sel)) {
|
|
1402
|
+
const availableSet = new Set(available);
|
|
1403
|
+
return sel.filter((item) => availableSet.has(item));
|
|
1404
|
+
}
|
|
1215
1405
|
return [];
|
|
1216
1406
|
};
|
|
1217
1407
|
// Sync commands
|
|
@@ -1219,24 +1409,38 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1219
1409
|
? resolveSelection(selection.commands, available.commands)
|
|
1220
1410
|
: available.commands; // No selection = sync all
|
|
1221
1411
|
if (commandsToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
1222
|
-
const centralCommands = getCommandsDir();
|
|
1223
|
-
const projectCommandsDir = projectAgentsDir ? path.join(projectAgentsDir, 'commands') : null;
|
|
1224
1412
|
const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
|
|
1225
|
-
|
|
1413
|
+
const commandsAsSkills = shouldInstallCommandAsSkill(agent, version);
|
|
1414
|
+
if (commandsAsSkills) {
|
|
1415
|
+
removePath(commandsTarget);
|
|
1416
|
+
}
|
|
1417
|
+
else {
|
|
1418
|
+
fs.mkdirSync(commandsTarget, { recursive: true });
|
|
1419
|
+
}
|
|
1226
1420
|
const syncedCommands = [];
|
|
1227
1421
|
for (const cmd of commandsToSync) {
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
const srcFile = projectSource && fs.existsSync(projectSource) ? projectSource : userSource;
|
|
1231
|
-
if (!fs.existsSync(srcFile))
|
|
1422
|
+
const resolved = resolveResource('commands', `${cmd}.md`, cwd);
|
|
1423
|
+
if (!resolved || fs.lstatSync(resolved.path).isSymbolicLink())
|
|
1232
1424
|
continue;
|
|
1233
|
-
|
|
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') {
|
|
1234
1438
|
const content = fs.readFileSync(srcFile, 'utf-8');
|
|
1235
1439
|
const tomlContent = markdownToToml(cmd, content);
|
|
1236
|
-
fs.writeFileSync(
|
|
1440
|
+
fs.writeFileSync(safeJoin(commandsTarget, `${cmd}.toml`), tomlContent);
|
|
1237
1441
|
}
|
|
1238
1442
|
else {
|
|
1239
|
-
fs.copyFileSync(srcFile,
|
|
1443
|
+
fs.copyFileSync(srcFile, safeJoin(commandsTarget, `${cmd}.md`));
|
|
1240
1444
|
}
|
|
1241
1445
|
syncedCommands.push(cmd);
|
|
1242
1446
|
}
|
|
@@ -1256,17 +1460,17 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1256
1460
|
? resolveSelection(selection.skills, available.skills)
|
|
1257
1461
|
: available.skills;
|
|
1258
1462
|
if (skillsToSync.length > 0) {
|
|
1259
|
-
const centralSkills = getSkillsDir();
|
|
1260
|
-
const projectSkills = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
|
|
1261
1463
|
const skillsTarget = path.join(agentDir, 'skills');
|
|
1262
1464
|
fs.mkdirSync(skillsTarget, { recursive: true });
|
|
1263
1465
|
const syncedSkills = [];
|
|
1264
1466
|
for (const skill of skillsToSync) {
|
|
1265
|
-
const
|
|
1266
|
-
const srcDir =
|
|
1267
|
-
|
|
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)
|
|
1268
1472
|
continue;
|
|
1269
|
-
const destDir =
|
|
1473
|
+
const destDir = safeJoin(skillsTarget, skill);
|
|
1270
1474
|
removePath(destDir);
|
|
1271
1475
|
copyDir(srcDir, destDir);
|
|
1272
1476
|
syncedSkills.push(skill);
|
|
@@ -1277,11 +1481,11 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1277
1481
|
}
|
|
1278
1482
|
}
|
|
1279
1483
|
}
|
|
1280
|
-
// Sync hooks (if agent supports them)
|
|
1484
|
+
// Sync hooks (if agent supports them at this version)
|
|
1485
|
+
const hooksGate = supports(agent, 'hooks', version);
|
|
1281
1486
|
if (agentConfig.supportsHooks) {
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
console.warn(`hooks skipped: codex@${version} < ${CODEX_HOOKS_MIN_VERSION}`);
|
|
1487
|
+
if (!hooksGate.ok) {
|
|
1488
|
+
console.warn(explainSkip(agent, 'hooks', hooksGate, version) + ' -- skipped');
|
|
1285
1489
|
}
|
|
1286
1490
|
else {
|
|
1287
1491
|
const hooksToSync = selection
|
|
@@ -1289,28 +1493,53 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1289
1493
|
: available.hooks;
|
|
1290
1494
|
if (hooksToSync.length > 0) {
|
|
1291
1495
|
const centralHooks = getHooksDir();
|
|
1292
|
-
const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
|
|
1293
1496
|
const hooksTarget = path.join(agentDir, 'hooks');
|
|
1294
1497
|
fs.mkdirSync(hooksTarget, { recursive: true });
|
|
1295
1498
|
const syncedHooks = [];
|
|
1296
1499
|
for (const hook of hooksToSync) {
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
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)
|
|
1300
1513
|
continue;
|
|
1301
|
-
const destFile =
|
|
1514
|
+
const destFile = safeJoin(hooksTarget, hook);
|
|
1302
1515
|
fs.copyFileSync(srcFile, destFile);
|
|
1303
1516
|
fs.chmodSync(destFile, 0o755);
|
|
1304
1517
|
syncedHooks.push(hook);
|
|
1305
1518
|
}
|
|
1306
|
-
// Remove orphan hook files that exist in version home but not in
|
|
1519
|
+
// Remove orphan hook files that exist in version home but not in any trusted source.
|
|
1307
1520
|
const centralHookNames = new Set(fs.existsSync(getHooksDir())
|
|
1308
1521
|
? fs.readdirSync(getHooksDir()).filter(f => !f.startsWith('.'))
|
|
1309
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
|
+
}
|
|
1310
1539
|
if (fs.existsSync(hooksTarget)) {
|
|
1311
1540
|
for (const file of fs.readdirSync(hooksTarget).filter(f => !f.startsWith('.'))) {
|
|
1312
1541
|
if (!centralHookNames.has(file)) {
|
|
1313
|
-
removePath(
|
|
1542
|
+
removePath(safeJoin(hooksTarget, file));
|
|
1314
1543
|
}
|
|
1315
1544
|
}
|
|
1316
1545
|
}
|
|
@@ -1318,7 +1547,9 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1318
1547
|
if (syncedHooks.length > 0) {
|
|
1319
1548
|
recordVersionResources(agent, version, 'hooks', syncedHooks);
|
|
1320
1549
|
}
|
|
1321
|
-
|
|
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') {
|
|
1322
1553
|
registerHooksToSettings(agent, versionHome);
|
|
1323
1554
|
}
|
|
1324
1555
|
}
|
|
@@ -1329,19 +1560,23 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1329
1560
|
? resolveSelection(selection.memory, available.memory)
|
|
1330
1561
|
: available.memory;
|
|
1331
1562
|
if (memoryToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
1332
|
-
const centralMemory =
|
|
1333
|
-
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, '
|
|
1563
|
+
const centralMemory = getResolvedRulesDir();
|
|
1564
|
+
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, 'rules') : null;
|
|
1565
|
+
const userMemoryDir = getUserRulesDir();
|
|
1334
1566
|
const syncedMemory = [];
|
|
1335
1567
|
const agentSupportsImports = !!agentConfig.capabilities.memoryImports;
|
|
1336
1568
|
for (const mem of memoryToSync) {
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
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)
|
|
1342
1577
|
continue;
|
|
1343
1578
|
const targetName = mem === 'AGENTS' ? agentConfig.instructionsFile : `${mem}.md`;
|
|
1344
|
-
const destFile =
|
|
1579
|
+
const destFile = safeJoin(agentDir, targetName);
|
|
1345
1580
|
removePath(destFile);
|
|
1346
1581
|
// For the primary memory file (AGENTS.md), agents that don't natively
|
|
1347
1582
|
// resolve @-imports get a compiled (inlined) copy + sidecar manifest.
|
|
@@ -1360,13 +1595,43 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1360
1595
|
recordVersionResources(agent, version, 'memory', syncedMemory);
|
|
1361
1596
|
}
|
|
1362
1597
|
}
|
|
1363
|
-
// Apply permissions (if agent supports them)
|
|
1364
|
-
//
|
|
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).
|
|
1365
1603
|
const permissionGroups = discoverPermissionGroups();
|
|
1366
1604
|
const allGroupNames = permissionGroups.map(g => g.name);
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1369
|
-
|
|
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
|
+
}
|
|
1370
1635
|
if (permsToSync.length > 0 && PERMISSIONS_CAPABLE_AGENTS.includes(agent)) {
|
|
1371
1636
|
// Build permissions from selected groups
|
|
1372
1637
|
const builtPerms = buildPermissionsFromGroups(permsToSync);
|
|
@@ -1408,12 +1673,12 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1408
1673
|
const agentsDir = path.join(agentDir, 'agents');
|
|
1409
1674
|
fs.mkdirSync(agentsDir, { recursive: true });
|
|
1410
1675
|
const transformed = transformSubagentForClaude(subagent.path);
|
|
1411
|
-
fs.writeFileSync(
|
|
1676
|
+
fs.writeFileSync(safeJoin(agentsDir, `${subagent.name}.md`), transformed);
|
|
1412
1677
|
result.subagents.push(subagent.name);
|
|
1413
1678
|
}
|
|
1414
1679
|
else if (agent === 'openclaw') {
|
|
1415
1680
|
// OpenClaw: copy full directory, rename AGENT.md -> AGENTS.md
|
|
1416
|
-
const targetDir = path.join(versionHome, '.openclaw', subagent.name);
|
|
1681
|
+
const targetDir = safeJoin(path.join(versionHome, '.openclaw'), subagent.name);
|
|
1417
1682
|
const syncResult = syncSubagentToOpenclaw(subagent.path, targetDir);
|
|
1418
1683
|
if (syncResult.success) {
|
|
1419
1684
|
result.subagents.push(subagent.name);
|
|
@@ -1449,6 +1714,10 @@ export function syncResourcesToVersion(agent, version, selection, options = {})
|
|
|
1449
1714
|
recordVersionResources(agent, version, 'plugins', result.plugins);
|
|
1450
1715
|
}
|
|
1451
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
|
+
}
|
|
1452
1721
|
return result;
|
|
1453
1722
|
}
|
|
1454
1723
|
/**
|
|
@@ -1734,4 +2003,3 @@ export async function promptAgentVersionSelection(availableAgents, options = {})
|
|
|
1734
2003
|
}
|
|
1735
2004
|
return { selectedAgents, versionSelections };
|
|
1736
2005
|
}
|
|
1737
|
-
//# sourceMappingURL=versions.js.map
|