@phnx-labs/agents-cli 1.20.4 → 1.20.5
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 +19 -0
- package/README.md +48 -17
- package/dist/commands/cli.js +1 -1
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +2 -0
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/exec.js +52 -16
- package/dist/commands/hooks.js +6 -6
- package/dist/commands/inspect.d.ts +26 -0
- package/dist/commands/inspect.js +590 -0
- package/dist/commands/mcp.js +17 -16
- package/dist/commands/models.js +1 -1
- package/dist/commands/packages.js +6 -4
- package/dist/commands/permissions.js +13 -12
- package/dist/commands/plugins.d.ts +13 -0
- package/dist/commands/plugins.js +100 -11
- package/dist/commands/prune.js +3 -2
- package/dist/commands/pull.d.ts +12 -5
- package/dist/commands/pull.js +26 -422
- package/dist/commands/push.d.ts +14 -0
- package/dist/commands/push.js +30 -0
- package/dist/commands/repo.d.ts +1 -1
- package/dist/commands/repo.js +155 -112
- package/dist/commands/resource-view.d.ts +2 -0
- package/dist/commands/resource-view.js +12 -3
- package/dist/commands/routines.js +32 -7
- package/dist/commands/rules.js +1 -1
- package/dist/commands/sessions.js +1 -0
- package/dist/commands/setup.d.ts +3 -3
- package/dist/commands/setup.js +15 -15
- package/dist/commands/skills.js +6 -5
- package/dist/commands/subagents.js +5 -4
- package/dist/commands/sync.d.ts +18 -5
- package/dist/commands/sync.js +251 -65
- package/dist/commands/teams.js +1 -0
- package/dist/commands/tmux.d.ts +25 -0
- package/dist/commands/tmux.js +415 -0
- package/dist/commands/trash.d.ts +2 -2
- package/dist/commands/trash.js +1 -1
- package/dist/commands/versions.js +2 -2
- package/dist/commands/view.js +9 -4
- package/dist/commands/workflows.js +4 -3
- package/dist/commands/worktree.d.ts +4 -5
- package/dist/commands/worktree.js +4 -4
- package/dist/index.js +68 -20
- package/dist/lib/agents.d.ts +19 -10
- package/dist/lib/agents.js +79 -25
- package/dist/lib/auto-pull-worker.d.ts +1 -1
- package/dist/lib/auto-pull-worker.js +2 -2
- package/dist/lib/auto-pull.d.ts +1 -1
- package/dist/lib/auto-pull.js +1 -1
- package/dist/lib/beta.d.ts +1 -1
- package/dist/lib/beta.js +1 -1
- package/dist/lib/capabilities.js +2 -0
- package/dist/lib/commands.d.ts +28 -1
- package/dist/lib/commands.js +125 -20
- package/dist/lib/doctor-diff.js +2 -2
- package/dist/lib/exec.d.ts +14 -0
- package/dist/lib/exec.js +39 -5
- package/dist/lib/fuzzy.d.ts +12 -2
- package/dist/lib/fuzzy.js +29 -4
- package/dist/lib/git.js +8 -1
- package/dist/lib/hooks.d.ts +2 -2
- package/dist/lib/hooks.js +97 -10
- package/dist/lib/mcp.js +32 -2
- package/dist/lib/migrate.d.ts +51 -0
- package/dist/lib/migrate.js +227 -1
- package/dist/lib/models.js +62 -15
- package/dist/lib/permissions.d.ts +36 -2
- package/dist/lib/permissions.js +217 -7
- package/dist/lib/plugin-marketplace.d.ts +98 -40
- package/dist/lib/plugin-marketplace.js +196 -93
- package/dist/lib/plugins.d.ts +21 -4
- package/dist/lib/plugins.js +130 -49
- package/dist/lib/profiles-presets.js +12 -12
- package/dist/lib/project-launch.d.ts +65 -0
- package/dist/lib/project-launch.js +367 -0
- package/dist/lib/pty-client.js +1 -1
- package/dist/lib/pty-server.d.ts +1 -1
- package/dist/lib/pty-server.js +1 -1
- package/dist/lib/refresh.d.ts +26 -0
- package/dist/lib/refresh.js +315 -0
- package/dist/lib/resource-patterns.d.ts +1 -1
- package/dist/lib/resource-patterns.js +1 -1
- package/dist/lib/resources/commands.js +2 -2
- package/dist/lib/resources/hooks.d.ts +1 -1
- package/dist/lib/resources/hooks.js +1 -1
- package/dist/lib/resources/mcp.d.ts +1 -1
- package/dist/lib/resources/mcp.js +5 -6
- package/dist/lib/resources/permissions.js +5 -2
- package/dist/lib/resources/rules.js +3 -2
- package/dist/lib/resources/skills.js +3 -2
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/resources.js +2 -2
- package/dist/lib/rotate.d.ts +1 -1
- package/dist/lib/rotate.js +1 -1
- package/dist/lib/routines.d.ts +16 -4
- package/dist/lib/routines.js +67 -17
- package/dist/lib/rules/compile.js +22 -10
- package/dist/lib/rules/rules.js +3 -3
- package/dist/lib/runner.js +16 -3
- package/dist/lib/scheduler.js +15 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +9 -1
- package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
- package/dist/lib/secrets/linux.d.ts +44 -9
- package/dist/lib/secrets/linux.js +302 -48
- package/dist/lib/session/db.js +15 -2
- package/dist/lib/session/discover.js +118 -3
- package/dist/lib/session/parse.js +3 -0
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/session/types.js +1 -1
- package/dist/lib/shims.d.ts +10 -9
- package/dist/lib/shims.js +101 -50
- package/dist/lib/skills.d.ts +1 -1
- package/dist/lib/skills.js +10 -9
- package/dist/lib/staleness/detectors/commands.d.ts +3 -0
- package/dist/lib/staleness/detectors/commands.js +46 -0
- package/dist/lib/staleness/detectors/hooks.d.ts +3 -0
- package/dist/lib/staleness/detectors/hooks.js +44 -0
- package/dist/lib/staleness/detectors/mcp.d.ts +3 -0
- package/dist/lib/staleness/detectors/mcp.js +31 -0
- package/dist/lib/staleness/detectors/permissions.d.ts +3 -0
- package/dist/lib/staleness/detectors/permissions.js +201 -0
- package/dist/lib/staleness/detectors/plugins.d.ts +8 -0
- package/dist/lib/staleness/detectors/plugins.js +23 -0
- package/dist/lib/staleness/detectors/rules.d.ts +3 -0
- package/dist/lib/staleness/detectors/rules.js +34 -0
- package/dist/lib/staleness/detectors/skills.d.ts +3 -0
- package/dist/lib/staleness/detectors/skills.js +71 -0
- package/dist/lib/staleness/detectors/subagents.d.ts +3 -0
- package/dist/lib/staleness/detectors/subagents.js +50 -0
- package/dist/lib/staleness/detectors/types.d.ts +22 -0
- package/dist/lib/staleness/detectors/types.js +1 -0
- package/dist/lib/staleness/detectors/workflows.d.ts +3 -0
- package/dist/lib/staleness/detectors/workflows.js +28 -0
- package/dist/lib/staleness/registry.d.ts +26 -0
- package/dist/lib/staleness/registry.js +123 -0
- package/dist/lib/staleness/writers/commands.d.ts +3 -0
- package/dist/lib/staleness/writers/commands.js +111 -0
- package/dist/lib/staleness/writers/hooks.d.ts +3 -0
- package/dist/lib/staleness/writers/hooks.js +47 -0
- package/dist/lib/staleness/writers/kinds.d.ts +10 -0
- package/dist/lib/staleness/writers/kinds.js +15 -0
- package/dist/lib/staleness/writers/lazy-map.d.ts +13 -0
- package/dist/lib/staleness/writers/lazy-map.js +19 -0
- package/dist/lib/staleness/writers/mcp.d.ts +10 -0
- package/dist/lib/staleness/writers/mcp.js +19 -0
- package/dist/lib/staleness/writers/permissions.d.ts +13 -0
- package/dist/lib/staleness/writers/permissions.js +26 -0
- package/dist/lib/staleness/writers/plugins.d.ts +7 -0
- package/dist/lib/staleness/writers/plugins.js +31 -0
- package/dist/lib/staleness/writers/rules.d.ts +7 -0
- package/dist/lib/staleness/writers/rules.js +55 -0
- package/dist/lib/staleness/writers/skills.d.ts +3 -0
- package/dist/lib/staleness/writers/skills.js +81 -0
- package/dist/lib/staleness/writers/sources.d.ts +16 -0
- package/dist/lib/staleness/writers/sources.js +72 -0
- package/dist/lib/staleness/writers/subagents.d.ts +3 -0
- package/dist/lib/staleness/writers/subagents.js +53 -0
- package/dist/lib/staleness/writers/types.d.ts +36 -0
- package/dist/lib/staleness/writers/types.js +1 -0
- package/dist/lib/staleness/writers/workflows.d.ts +7 -0
- package/dist/lib/staleness/writers/workflows.js +31 -0
- package/dist/lib/state.d.ts +34 -11
- package/dist/lib/state.js +58 -13
- package/dist/lib/subagents.d.ts +0 -2
- package/dist/lib/subagents.js +6 -6
- package/dist/lib/teams/agents.js +1 -1
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/tmux/binary.d.ts +67 -0
- package/dist/lib/tmux/binary.js +141 -0
- package/dist/lib/tmux/index.d.ts +8 -0
- package/dist/lib/tmux/index.js +8 -0
- package/dist/lib/tmux/paths.d.ts +17 -0
- package/dist/lib/tmux/paths.js +30 -0
- package/dist/lib/tmux/session.d.ts +122 -0
- package/dist/lib/tmux/session.js +305 -0
- package/dist/lib/types.d.ts +58 -7
- package/dist/lib/types.js +1 -1
- package/dist/lib/usage.js +1 -1
- package/dist/lib/versions.d.ts +4 -4
- package/dist/lib/versions.js +135 -493
- package/dist/lib/workflows.d.ts +2 -4
- package/dist/lib/workflows.js +3 -4
- package/package.json +2 -2
- package/scripts/postinstall.js +16 -63
- package/dist/commands/status.d.ts +0 -9
- package/dist/commands/status.js +0 -25
|
@@ -1,45 +1,134 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Native plugin
|
|
2
|
+
* Native plugin marketplaces for Claude / OpenClaw — one per DotAgents repo.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Every DotAgents repo that holds plugins synthesizes its OWN synthetic local
|
|
5
|
+
* marketplace under each version's plugin directory, named after the repo:
|
|
6
6
|
*
|
|
7
7
|
* <versionHome>/.{claude,openclaw}/plugins/
|
|
8
|
-
* known_marketplaces.json
|
|
9
|
-
* marketplaces/agents-cli/
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* known_marketplaces.json # registers each repo's marketplace
|
|
9
|
+
* marketplaces/agents-cli/ # ~/.agents/plugins/* (user repo)
|
|
10
|
+
* marketplaces/agents-<alias>/ # ~/.agents-<alias>/plugins/* (extra repo)
|
|
11
|
+
* marketplaces/agents-project/ # <cwd>/.agents/plugins/* (project repo)
|
|
12
|
+
* .claude-plugin/marketplace.json # synthesized catalog
|
|
13
|
+
* plugins/<plugin>/ # copied plugin source
|
|
12
14
|
*
|
|
13
|
-
* Plus the version's settings.json gets
|
|
15
|
+
* Plus the version's settings.json gets
|
|
16
|
+
* `enabledPlugins["<plugin>@<marketplace>"] = true`.
|
|
14
17
|
*
|
|
15
|
-
* This produces native `/plugin:skill` slash namespacing, visibility in
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
+
* This produces native `/plugin:skill` slash namespacing, visibility in
|
|
19
|
+
* `/plugins`, `/plugin enable|disable` support, AND honest attribution (the
|
|
20
|
+
* user can see which repo each plugin came from) — matching the Claude Code
|
|
21
|
+
* spec at https://code.claude.com/docs/en/plugins and /plugin-marketplaces.
|
|
22
|
+
*
|
|
23
|
+
* The naming policy lives in one place — marketplaceNameFor(). Source-side
|
|
24
|
+
* discovery (discoverMarketplaces) and per-version synthesis (syncMarketplaceManifest
|
|
25
|
+
* / registerMarketplace / syncAllMarketplaces) all key off a MarketplaceSpec so
|
|
26
|
+
* the catalog name and on-disk layout are derived, never hard-coded per call.
|
|
18
27
|
*/
|
|
19
28
|
import * as fs from 'fs';
|
|
20
29
|
import * as path from 'path';
|
|
30
|
+
import { getPluginsDir, getEnabledExtraRepos, getProjectPluginsDir } from './state.js';
|
|
31
|
+
import { agentConfigDirName } from './agents.js';
|
|
32
|
+
/**
|
|
33
|
+
* Canonical name for the user-repo marketplace (~/.agents/plugins/). Kept as an
|
|
34
|
+
* exported constant for callers that operate on the user repo directly and for
|
|
35
|
+
* the `marketplaces/agents-cli/` on-disk path that existing installs already have.
|
|
36
|
+
*/
|
|
21
37
|
export const MARKETPLACE_NAME = 'agents-cli';
|
|
38
|
+
export const SYSTEM_MARKETPLACE_NAME = 'agents-system';
|
|
39
|
+
/** Marketplace name for <cwd>/.agents/plugins/*. */
|
|
40
|
+
export const PROJECT_MARKETPLACE_NAME = 'agents-project';
|
|
41
|
+
// ─── Naming policy (single source of truth) ──────────────────────────────────
|
|
42
|
+
/**
|
|
43
|
+
* Map a MarketplaceSpec to its catalog name. This is the ONLY place that
|
|
44
|
+
* encodes the repo → name policy; every other function derives the name here.
|
|
45
|
+
*/
|
|
46
|
+
export function marketplaceNameFor(spec) {
|
|
47
|
+
switch (spec.kind) {
|
|
48
|
+
case 'user': return MARKETPLACE_NAME; // "agents-cli"
|
|
49
|
+
case 'extra': return `agents-${spec.alias}`; // e.g. "agents-extras"
|
|
50
|
+
case 'project': return PROJECT_MARKETPLACE_NAME; // "agents-project"
|
|
51
|
+
case 'system': return SYSTEM_MARKETPLACE_NAME; // "agents-system"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** Resolve a spec-or-name argument to the bare marketplace name string. */
|
|
55
|
+
function nameOf(specOrName) {
|
|
56
|
+
return typeof specOrName === 'string' ? specOrName : marketplaceNameFor(specOrName);
|
|
57
|
+
}
|
|
58
|
+
function descriptionFor(spec) {
|
|
59
|
+
switch (spec.kind) {
|
|
60
|
+
case 'user': return 'Plugins from the user repo (~/.agents/plugins/)';
|
|
61
|
+
case 'extra': return `Plugins from extra repo "${spec.alias}" (~/.agents-${spec.alias}/plugins/)`;
|
|
62
|
+
case 'project': return 'Project-scoped plugins from <cwd>/.agents/plugins/';
|
|
63
|
+
case 'system': return 'Plugins from the system repo (~/.agents/.system/plugins/)';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// ─── Source-side discovery ────────────────────────────────────────────────────
|
|
67
|
+
/**
|
|
68
|
+
* Discover every DotAgents repo that contributes plugins, in precedence order
|
|
69
|
+
* (user, then each enabled extra repo, then the project repo when cwd has one).
|
|
70
|
+
* No agent / version is involved — this walks source-side plugin roots only.
|
|
71
|
+
*
|
|
72
|
+
* A repo is included when its plugins/ directory exists on disk. The user repo
|
|
73
|
+
* is always probed; extras come from getEnabledExtraRepos() (already filtered to
|
|
74
|
+
* enabled + on-disk repos); the project repo is included only when
|
|
75
|
+
* <cwd>/.agents/plugins/ exists.
|
|
76
|
+
*/
|
|
77
|
+
export function discoverMarketplaces(opts = {}) {
|
|
78
|
+
const out = [];
|
|
79
|
+
// User repo — always the canonical "agents-cli" marketplace.
|
|
80
|
+
const userRoot = getPluginsDir();
|
|
81
|
+
if (dirExists(userRoot)) {
|
|
82
|
+
const spec = { kind: 'user' };
|
|
83
|
+
out.push({ spec, name: marketplaceNameFor(spec), pluginsRoot: userRoot, description: descriptionFor(spec) });
|
|
84
|
+
}
|
|
85
|
+
// Extra repos — peer ~/.agents-<alias>/ clones (and user-owned path:-repos).
|
|
86
|
+
for (const extra of getEnabledExtraRepos()) {
|
|
87
|
+
const pluginsRoot = path.join(extra.dir, 'plugins');
|
|
88
|
+
if (!dirExists(pluginsRoot))
|
|
89
|
+
continue;
|
|
90
|
+
const spec = { kind: 'extra', alias: extra.alias, root: pluginsRoot };
|
|
91
|
+
out.push({ spec, name: marketplaceNameFor(spec), pluginsRoot, description: descriptionFor(spec) });
|
|
92
|
+
}
|
|
93
|
+
// Project repo — <cwd>/.agents/plugins/.
|
|
94
|
+
const projectRoot = getProjectPluginsDir(opts.cwd ?? process.cwd());
|
|
95
|
+
if (projectRoot && dirExists(projectRoot)) {
|
|
96
|
+
const spec = { kind: 'project', root: projectRoot };
|
|
97
|
+
out.push({ spec, name: marketplaceNameFor(spec), pluginsRoot: projectRoot, description: descriptionFor(spec) });
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
function dirExists(p) {
|
|
102
|
+
try {
|
|
103
|
+
return fs.statSync(p).isDirectory();
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// ─── Per-version paths ────────────────────────────────────────────────────────
|
|
22
110
|
function pluginsRootForVersion(agent, versionHome) {
|
|
23
|
-
return path.join(versionHome,
|
|
111
|
+
return path.join(versionHome, agentConfigDirName(agent), 'plugins');
|
|
24
112
|
}
|
|
25
|
-
export function marketplaceRoot(agent, versionHome) {
|
|
26
|
-
return path.join(pluginsRootForVersion(agent, versionHome), 'marketplaces',
|
|
113
|
+
export function marketplaceRoot(specOrName, agent, versionHome) {
|
|
114
|
+
return path.join(pluginsRootForVersion(agent, versionHome), 'marketplaces', nameOf(specOrName));
|
|
27
115
|
}
|
|
28
|
-
export function marketplaceManifestPath(agent, versionHome) {
|
|
29
|
-
return path.join(marketplaceRoot(agent, versionHome), '.claude-plugin', 'marketplace.json');
|
|
116
|
+
export function marketplaceManifestPath(specOrName, agent, versionHome) {
|
|
117
|
+
return path.join(marketplaceRoot(specOrName, agent, versionHome), '.claude-plugin', 'marketplace.json');
|
|
30
118
|
}
|
|
31
|
-
export function pluginInstallDir(plugin, agent, versionHome) {
|
|
32
|
-
return path.join(marketplaceRoot(agent, versionHome), 'plugins', plugin.name);
|
|
119
|
+
export function pluginInstallDir(plugin, specOrName, agent, versionHome) {
|
|
120
|
+
return path.join(marketplaceRoot(specOrName, agent, versionHome), 'plugins', plugin.name);
|
|
33
121
|
}
|
|
34
122
|
export function knownMarketplacesPath(agent, versionHome) {
|
|
35
123
|
return path.join(pluginsRootForVersion(agent, versionHome), 'known_marketplaces.json');
|
|
36
124
|
}
|
|
37
125
|
function settingsPath(agent, versionHome) {
|
|
38
|
-
return path.join(versionHome,
|
|
126
|
+
return path.join(versionHome, agentConfigDirName(agent), 'settings.json');
|
|
39
127
|
}
|
|
128
|
+
// ─── Copy plugin source into a marketplace ────────────────────────────────────
|
|
40
129
|
/**
|
|
41
|
-
* Copy plugin source into marketplace install dir.
|
|
42
|
-
* Source of truth remains
|
|
130
|
+
* Copy plugin source into the marketplace install dir for the given spec.
|
|
131
|
+
* Source of truth remains the plugin's source dir — this is a per-version snapshot.
|
|
43
132
|
*
|
|
44
133
|
* Symlinks pointing OUTSIDE the plugin source root are dropped. They show up
|
|
45
134
|
* when plugin authors (legitimately) link prompt-side references to sibling
|
|
@@ -51,8 +140,8 @@ function settingsPath(agent, versionHome) {
|
|
|
51
140
|
*
|
|
52
141
|
* Internal symlinks (target stays inside the plugin root) are preserved.
|
|
53
142
|
*/
|
|
54
|
-
export function copyPluginToMarketplace(plugin, agent, versionHome) {
|
|
55
|
-
const dest = pluginInstallDir(plugin, agent, versionHome);
|
|
143
|
+
export function copyPluginToMarketplace(plugin, spec, agent, versionHome) {
|
|
144
|
+
const dest = pluginInstallDir(plugin, spec, agent, versionHome);
|
|
56
145
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
57
146
|
if (fs.existsSync(dest)) {
|
|
58
147
|
fs.rmSync(dest, { recursive: true, force: true });
|
|
@@ -96,21 +185,38 @@ export function copyPluginToMarketplace(plugin, agent, versionHome) {
|
|
|
96
185
|
}
|
|
97
186
|
return dest;
|
|
98
187
|
}
|
|
188
|
+
// ─── Catalog synthesis ──────────────────────────────────────────────────────
|
|
99
189
|
/**
|
|
100
|
-
* Re-synthesize <marketplace>/.claude-plugin/marketplace.json from the
|
|
101
|
-
*
|
|
102
|
-
* so the manifest stays in lockstep with on-disk contents.
|
|
190
|
+
* Re-synthesize <marketplace>/.claude-plugin/marketplace.json from the plugins
|
|
191
|
+
* already installed under <marketplace>/plugins/. Always run after add or remove
|
|
192
|
+
* so the manifest stays in lockstep with on-disk contents. Returns the manifest
|
|
193
|
+
* it wrote, or null when the marketplace has no plugins dir yet.
|
|
103
194
|
*/
|
|
104
|
-
export function syncMarketplaceManifest(agent, versionHome) {
|
|
105
|
-
const
|
|
195
|
+
export function syncMarketplaceManifest(spec, agent, versionHome) {
|
|
196
|
+
const name = marketplaceNameFor(spec);
|
|
197
|
+
const root = marketplaceRoot(spec, agent, versionHome);
|
|
106
198
|
const pluginsDir = path.join(root, 'plugins');
|
|
107
199
|
if (!fs.existsSync(pluginsDir))
|
|
108
200
|
return null;
|
|
109
201
|
const entries = [];
|
|
110
202
|
for (const entry of fs.readdirSync(pluginsDir, { withFileTypes: true })) {
|
|
111
|
-
if (
|
|
203
|
+
if (entry.name.startsWith('.'))
|
|
112
204
|
continue;
|
|
113
|
-
|
|
205
|
+
// Follow symlinks: Dirent.isDirectory() is false for a symlink even when the
|
|
206
|
+
// target is a directory. statSync follows the link.
|
|
207
|
+
const entryPath = path.join(pluginsDir, entry.name);
|
|
208
|
+
let isDir = entry.isDirectory();
|
|
209
|
+
if (!isDir && entry.isSymbolicLink()) {
|
|
210
|
+
try {
|
|
211
|
+
isDir = fs.statSync(entryPath).isDirectory();
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
isDir = false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (!isDir)
|
|
218
|
+
continue;
|
|
219
|
+
const manifestFile = path.join(entryPath, '.claude-plugin', 'plugin.json');
|
|
114
220
|
if (!fs.existsSync(manifestFile))
|
|
115
221
|
continue;
|
|
116
222
|
let manifest;
|
|
@@ -130,22 +236,25 @@ export function syncMarketplaceManifest(agent, versionHome) {
|
|
|
130
236
|
}
|
|
131
237
|
const manifest = {
|
|
132
238
|
$schema: 'https://anthropic.com/claude-code/marketplace.schema.json',
|
|
133
|
-
name
|
|
134
|
-
description:
|
|
239
|
+
name,
|
|
240
|
+
description: descriptionFor(spec),
|
|
135
241
|
owner: { name: 'agents-cli' },
|
|
136
242
|
plugins: entries.sort((a, b) => a.name.localeCompare(b.name)),
|
|
137
243
|
};
|
|
138
|
-
const manifestPath = marketplaceManifestPath(agent, versionHome);
|
|
244
|
+
const manifestPath = marketplaceManifestPath(spec, agent, versionHome);
|
|
139
245
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
140
246
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
|
|
141
247
|
return manifest;
|
|
142
248
|
}
|
|
249
|
+
// ─── Registration in known_marketplaces.json ──────────────────────────────────
|
|
143
250
|
/**
|
|
144
|
-
* Register
|
|
145
|
-
*
|
|
251
|
+
* Register a marketplace in known_marketplaces.json so Claude Code discovers
|
|
252
|
+
* it on startup. Idempotent: re-running just refreshes lastUpdated. Other
|
|
253
|
+
* marketplaces' entries are preserved untouched.
|
|
146
254
|
*/
|
|
147
|
-
export function registerMarketplace(agent, versionHome) {
|
|
148
|
-
const
|
|
255
|
+
export function registerMarketplace(spec, agent, versionHome) {
|
|
256
|
+
const name = marketplaceNameFor(spec);
|
|
257
|
+
const root = marketplaceRoot(spec, agent, versionHome);
|
|
149
258
|
const knownPath = knownMarketplacesPath(agent, versionHome);
|
|
150
259
|
let known = {};
|
|
151
260
|
if (fs.existsSync(knownPath)) {
|
|
@@ -156,7 +265,7 @@ export function registerMarketplace(agent, versionHome) {
|
|
|
156
265
|
known = {};
|
|
157
266
|
}
|
|
158
267
|
}
|
|
159
|
-
known[
|
|
268
|
+
known[name] = {
|
|
160
269
|
source: { source: 'directory', path: root },
|
|
161
270
|
installLocation: root,
|
|
162
271
|
lastUpdated: new Date().toISOString(),
|
|
@@ -165,10 +274,12 @@ export function registerMarketplace(agent, versionHome) {
|
|
|
165
274
|
fs.writeFileSync(knownPath, JSON.stringify(known, null, 2) + '\n', 'utf-8');
|
|
166
275
|
}
|
|
167
276
|
/**
|
|
168
|
-
* Drop
|
|
169
|
-
*
|
|
277
|
+
* Drop a marketplace entry from known_marketplaces.json. Called when the last
|
|
278
|
+
* plugin under it is removed. Removes only its own entry; deletes the file only
|
|
279
|
+
* when no entries remain.
|
|
170
280
|
*/
|
|
171
|
-
export function unregisterMarketplace(agent, versionHome) {
|
|
281
|
+
export function unregisterMarketplace(specOrName, agent, versionHome) {
|
|
282
|
+
const name = nameOf(specOrName);
|
|
172
283
|
const knownPath = knownMarketplacesPath(agent, versionHome);
|
|
173
284
|
if (!fs.existsSync(knownPath))
|
|
174
285
|
return;
|
|
@@ -179,9 +290,9 @@ export function unregisterMarketplace(agent, versionHome) {
|
|
|
179
290
|
catch {
|
|
180
291
|
return;
|
|
181
292
|
}
|
|
182
|
-
if (!(
|
|
293
|
+
if (!(name in known))
|
|
183
294
|
return;
|
|
184
|
-
delete known[
|
|
295
|
+
delete known[name];
|
|
185
296
|
if (Object.keys(known).length === 0) {
|
|
186
297
|
try {
|
|
187
298
|
fs.unlinkSync(knownPath);
|
|
@@ -192,15 +303,38 @@ export function unregisterMarketplace(agent, versionHome) {
|
|
|
192
303
|
fs.writeFileSync(knownPath, JSON.stringify(known, null, 2) + '\n', 'utf-8');
|
|
193
304
|
}
|
|
194
305
|
}
|
|
306
|
+
// ─── Top-level orchestration ──────────────────────────────────────────────────
|
|
195
307
|
/**
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
308
|
+
* Discover every source-side marketplace, then for each one re-synthesize its
|
|
309
|
+
* catalog from the plugins already copied under the version home and register
|
|
310
|
+
* it in known_marketplaces.json. Returns one result per marketplace that has at
|
|
311
|
+
* least one plugin installed.
|
|
312
|
+
*
|
|
313
|
+
* Copying plugin source into a marketplace is the caller's responsibility
|
|
314
|
+
* (copyPluginToMarketplace / syncPluginToVersion) — this reconciles catalogs +
|
|
315
|
+
* registrations across all repos once the copies are in place. Marketplaces
|
|
316
|
+
* whose version-home plugins dir is empty or absent are skipped, so we never
|
|
317
|
+
* register a known_marketplace pointing at a directory with no catalog.
|
|
199
318
|
*/
|
|
200
|
-
export function
|
|
201
|
-
|
|
202
|
-
|
|
319
|
+
export function syncAllMarketplaces(agent, versionHome, opts = {}) {
|
|
320
|
+
const results = [];
|
|
321
|
+
for (const dm of discoverMarketplaces(opts)) {
|
|
322
|
+
const manifest = syncMarketplaceManifest(dm.spec, agent, versionHome);
|
|
323
|
+
if (!manifest || manifest.plugins.length === 0)
|
|
324
|
+
continue;
|
|
325
|
+
registerMarketplace(dm.spec, agent, versionHome);
|
|
326
|
+
results.push({ spec: dm.spec, name: dm.name, plugins: manifest.plugins.length });
|
|
203
327
|
}
|
|
328
|
+
return results;
|
|
329
|
+
}
|
|
330
|
+
// ─── Per-plugin settings ops ──────────────────────────────────────────────────
|
|
331
|
+
/**
|
|
332
|
+
* Mark a plugin as enabled in <versionHome>/.{agent}/settings.json under
|
|
333
|
+
* enabledPlugins["<plugin>@<marketplace>"]: true. Reads, mutates, writes —
|
|
334
|
+
* preserving every other key. Trust/exec-surface gating is the caller's
|
|
335
|
+
* responsibility (plugins.ts owns plugin capability inspection).
|
|
336
|
+
*/
|
|
337
|
+
export function addPluginToSettings(pluginName, marketplaceName, agent, versionHome) {
|
|
204
338
|
const sPath = settingsPath(agent, versionHome);
|
|
205
339
|
let settings = {};
|
|
206
340
|
if (fs.existsSync(sPath)) {
|
|
@@ -215,49 +349,17 @@ export function enablePluginInSettings(pluginName, agent, versionHome, options =
|
|
|
215
349
|
settings.enabledPlugins = {};
|
|
216
350
|
}
|
|
217
351
|
const enabled = settings.enabledPlugins;
|
|
218
|
-
const key = `${pluginName}@${
|
|
352
|
+
const key = `${pluginName}@${marketplaceName}`;
|
|
219
353
|
if (enabled[key] === true)
|
|
220
354
|
return;
|
|
221
355
|
enabled[key] = true;
|
|
222
356
|
fs.mkdirSync(path.dirname(sPath), { recursive: true });
|
|
223
357
|
fs.writeFileSync(sPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
224
358
|
}
|
|
225
|
-
function marketplacePluginHasExecSurfaces(pluginName, agent, versionHome) {
|
|
226
|
-
const root = path.join(marketplaceRoot(agent, versionHome), 'plugins', pluginName);
|
|
227
|
-
if (fs.existsSync(path.join(root, '.mcp.json')))
|
|
228
|
-
return true;
|
|
229
|
-
for (const dir of ['bin', 'scripts', 'permissions']) {
|
|
230
|
-
if (fs.existsSync(path.join(root, dir)))
|
|
231
|
-
return true;
|
|
232
|
-
}
|
|
233
|
-
const hooksFile = path.join(root, 'hooks', 'hooks.json');
|
|
234
|
-
if (fs.existsSync(hooksFile))
|
|
235
|
-
return true;
|
|
236
|
-
const hooksDir = path.join(root, 'hooks');
|
|
237
|
-
if (fs.existsSync(hooksDir)) {
|
|
238
|
-
try {
|
|
239
|
-
if (fs.readdirSync(hooksDir).some((entry) => !entry.startsWith('.')))
|
|
240
|
-
return true;
|
|
241
|
-
}
|
|
242
|
-
catch {
|
|
243
|
-
return true;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
const settingsFile = path.join(root, 'settings.json');
|
|
247
|
-
if (!fs.existsSync(settingsFile))
|
|
248
|
-
return false;
|
|
249
|
-
try {
|
|
250
|
-
const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf-8'));
|
|
251
|
-
return Object.keys(settings).some((key) => key !== 'permissions') || 'permissions' in settings;
|
|
252
|
-
}
|
|
253
|
-
catch {
|
|
254
|
-
return true;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
359
|
/**
|
|
258
|
-
* Remove the enabledPlugins key for this plugin. Inverse of
|
|
360
|
+
* Remove the enabledPlugins key for this plugin. Inverse of addPluginToSettings.
|
|
259
361
|
*/
|
|
260
|
-
export function
|
|
362
|
+
export function removePluginFromSettings(pluginName, marketplaceName, agent, versionHome) {
|
|
261
363
|
const sPath = settingsPath(agent, versionHome);
|
|
262
364
|
if (!fs.existsSync(sPath))
|
|
263
365
|
return;
|
|
@@ -271,7 +373,7 @@ export function disablePluginInSettings(pluginName, agent, versionHome) {
|
|
|
271
373
|
const enabled = settings.enabledPlugins;
|
|
272
374
|
if (!enabled)
|
|
273
375
|
return;
|
|
274
|
-
const key = `${pluginName}@${
|
|
376
|
+
const key = `${pluginName}@${marketplaceName}`;
|
|
275
377
|
if (!(key in enabled))
|
|
276
378
|
return;
|
|
277
379
|
delete enabled[key];
|
|
@@ -280,12 +382,13 @@ export function disablePluginInSettings(pluginName, agent, versionHome) {
|
|
|
280
382
|
}
|
|
281
383
|
fs.writeFileSync(sPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
282
384
|
}
|
|
385
|
+
// ─── Marketplace teardown helpers ─────────────────────────────────────────────
|
|
283
386
|
/**
|
|
284
387
|
* Remove a plugin's installed marketplace directory. Returns true if the dir
|
|
285
388
|
* existed and was removed.
|
|
286
389
|
*/
|
|
287
|
-
export function removePluginFromMarketplace(pluginName, agent, versionHome) {
|
|
288
|
-
const installed = path.join(marketplaceRoot(agent, versionHome), 'plugins', pluginName);
|
|
390
|
+
export function removePluginFromMarketplace(pluginName, specOrName, agent, versionHome) {
|
|
391
|
+
const installed = path.join(marketplaceRoot(specOrName, agent, versionHome), 'plugins', pluginName);
|
|
289
392
|
if (!fs.existsSync(installed))
|
|
290
393
|
return false;
|
|
291
394
|
fs.rmSync(installed, { recursive: true, force: true });
|
|
@@ -294,8 +397,8 @@ export function removePluginFromMarketplace(pluginName, agent, versionHome) {
|
|
|
294
397
|
/**
|
|
295
398
|
* Return true if the marketplace has no plugins left under it.
|
|
296
399
|
*/
|
|
297
|
-
export function marketplaceIsEmpty(agent, versionHome) {
|
|
298
|
-
const pluginsDir = path.join(marketplaceRoot(agent, versionHome), 'plugins');
|
|
400
|
+
export function marketplaceIsEmpty(specOrName, agent, versionHome) {
|
|
401
|
+
const pluginsDir = path.join(marketplaceRoot(specOrName, agent, versionHome), 'plugins');
|
|
299
402
|
if (!fs.existsSync(pluginsDir))
|
|
300
403
|
return true;
|
|
301
404
|
const remaining = fs.readdirSync(pluginsDir, { withFileTypes: true })
|
|
@@ -305,8 +408,8 @@ export function marketplaceIsEmpty(agent, versionHome) {
|
|
|
305
408
|
/**
|
|
306
409
|
* Drop the entire marketplace directory. Called after the last plugin removal.
|
|
307
410
|
*/
|
|
308
|
-
export function removeEmptyMarketplaceDir(agent, versionHome) {
|
|
309
|
-
const root = marketplaceRoot(agent, versionHome);
|
|
411
|
+
export function removeEmptyMarketplaceDir(specOrName, agent, versionHome) {
|
|
412
|
+
const root = marketplaceRoot(specOrName, agent, versionHome);
|
|
310
413
|
if (!fs.existsSync(root))
|
|
311
414
|
return;
|
|
312
415
|
fs.rmSync(root, { recursive: true, force: true });
|
|
@@ -314,7 +417,7 @@ export function removeEmptyMarketplaceDir(agent, versionHome) {
|
|
|
314
417
|
/**
|
|
315
418
|
* Detect whether a plugin is installed via the native marketplace path.
|
|
316
419
|
*/
|
|
317
|
-
export function isInstalledInMarketplace(pluginName, agent, versionHome) {
|
|
318
|
-
const installed = path.join(marketplaceRoot(agent, versionHome), 'plugins', pluginName);
|
|
420
|
+
export function isInstalledInMarketplace(pluginName, specOrName, agent, versionHome) {
|
|
421
|
+
const installed = path.join(marketplaceRoot(specOrName, agent, versionHome), 'plugins', pluginName);
|
|
319
422
|
return fs.existsSync(path.join(installed, '.claude-plugin', 'plugin.json'));
|
|
320
423
|
}
|
package/dist/lib/plugins.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* module discovers plugins, validates their manifests, and syncs their
|
|
9
9
|
* contents into agent version homes.
|
|
10
10
|
*/
|
|
11
|
-
import type { AgentId, DiscoveredPlugin, PluginManifest } from './types.js';
|
|
11
|
+
import type { AgentId, DiscoveredPlugin, PluginManifest, MarketplaceSpec } from './types.js';
|
|
12
12
|
export interface PluginCapabilities {
|
|
13
13
|
hasHooks: boolean;
|
|
14
14
|
hasMcp: boolean;
|
|
@@ -19,11 +19,28 @@ export interface PluginCapabilities {
|
|
|
19
19
|
}
|
|
20
20
|
export declare const PLUGIN_EXEC_SURFACE_LABELS: Record<keyof PluginCapabilities, string>;
|
|
21
21
|
/**
|
|
22
|
-
* Discover all plugins in ~/.agents/plugins
|
|
22
|
+
* Discover all plugins in a given plugins directory (e.g. ~/.agents/plugins/,
|
|
23
|
+
* ~/.agents/.system/plugins/, <cwd>/.agents/plugins/, ~/.agents-<alias>/plugins/).
|
|
23
24
|
* A valid plugin has a .claude-plugin/plugin.json manifest.
|
|
25
|
+
*
|
|
26
|
+
* `spec` stamps marketplace provenance onto each discovered plugin. Callers that
|
|
27
|
+
* scan a single source dir without a marketplace identity (e.g. project-launch)
|
|
28
|
+
* may omit it; those plugins default to the user marketplace.
|
|
24
29
|
*/
|
|
25
|
-
export declare function
|
|
26
|
-
|
|
30
|
+
export declare function discoverPluginsInDir(pluginsDir: string, spec?: MarketplaceSpec): DiscoveredPlugin[];
|
|
31
|
+
/**
|
|
32
|
+
* Discover every plugin across ALL marketplaces — the user repo (~/.agents/),
|
|
33
|
+
* each enabled extra repo (~/.agents-<alias>/), and the project repo
|
|
34
|
+
* (<cwd>/.agents/) — stamping marketplace provenance onto each.
|
|
35
|
+
*
|
|
36
|
+
* Plugin names are NOT deduplicated across marketplaces: a `code` plugin in both
|
|
37
|
+
* the user repo and an extra repo yields two entries (`code@agents-cli` and
|
|
38
|
+
* `code@agents-<alias>`), each installing into its own marketplace directory.
|
|
39
|
+
*/
|
|
40
|
+
export declare function discoverPlugins(opts?: {
|
|
41
|
+
cwd?: string;
|
|
42
|
+
}): DiscoveredPlugin[];
|
|
43
|
+
export declare function buildDiscoveredPlugin(pluginRoot: string, manifest: PluginManifest, spec?: MarketplaceSpec): DiscoveredPlugin;
|
|
27
44
|
export declare function inspectPluginCapabilities(pluginRoot: string): PluginCapabilities;
|
|
28
45
|
export declare function hasPluginExecSurfaces(capabilities: PluginCapabilities): boolean;
|
|
29
46
|
export declare function pluginCapabilityLabels(capabilities: PluginCapabilities): string[];
|