@phnx-labs/agents-cli 1.20.4 → 1.20.6
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 +49 -18
- package/dist/commands/browser.js +31 -4
- package/dist/commands/cli.js +1 -1
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +2 -0
- package/dist/commands/computer.js +10 -2
- package/dist/commands/defaults.d.ts +7 -0
- package/dist/commands/defaults.js +89 -0
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/exec.js +73 -19
- 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 +4 -4
- package/dist/commands/secrets.js +46 -9
- package/dist/commands/sessions.js +1 -0
- package/dist/commands/setup.d.ts +3 -3
- package/dist/commands/setup.js +17 -17
- 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 +109 -11
- 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.d.ts +12 -1
- package/dist/commands/view.js +128 -40
- 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 +106 -41
- package/dist/lib/agents.d.ts +23 -10
- package/dist/lib/agents.js +88 -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/browser/chrome.d.ts +10 -0
- package/dist/lib/browser/chrome.js +84 -3
- 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 +59 -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 +233 -5
- package/dist/lib/models.js +62 -15
- package/dist/lib/permissions.d.ts +59 -2
- package/dist/lib/permissions.js +299 -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 +70 -0
- package/dist/lib/project-launch.js +404 -0
- package/dist/lib/pty-client.js +1 -1
- package/dist/lib/pty-server.d.ts +1 -1
- package/dist/lib/pty-server.js +8 -5
- 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.d.ts +2 -0
- package/dist/lib/resources.js +4 -3
- package/dist/lib/rotate.d.ts +1 -1
- package/dist/lib/rotate.js +7 -19
- 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/run-config.d.ts +9 -0
- package/dist/lib/run-config.js +35 -0
- package/dist/lib/run-defaults.d.ts +42 -0
- package/dist/lib/run-defaults.js +180 -0
- 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/install-helper.d.ts +11 -3
- package/dist/lib/secrets/install-helper.js +48 -6
- package/dist/lib/secrets/linux.d.ts +56 -9
- package/dist/lib/secrets/linux.js +327 -59
- 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 +18 -9
- package/dist/lib/shims.js +133 -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/api.d.ts +67 -0
- package/dist/lib/teams/api.js +78 -0
- 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 +73 -13
- 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 +138 -496
- package/dist/lib/workflows.d.ts +2 -4
- package/dist/lib/workflows.js +3 -4
- package/package.json +6 -3
- package/scripts/postinstall.js +16 -63
- package/dist/commands/status.d.ts +0 -9
- package/dist/commands/status.js +0 -25
package/dist/lib/plugins.js
CHANGED
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
import * as fs from 'fs';
|
|
12
12
|
import * as path from 'path';
|
|
13
13
|
import { execFileSync } from 'child_process';
|
|
14
|
-
import { getPluginsDir, getTrashPluginsDir } from './state.js';
|
|
14
|
+
import { getPluginsDir, getTrashPluginsDir, getExtraPluginsDir, getProjectPluginsDir } from './state.js';
|
|
15
15
|
import { listInstalledVersions, getVersionHomePath } from './versions.js';
|
|
16
|
-
import { AGENTS,
|
|
16
|
+
import { AGENTS, agentConfigDirName } from './agents.js';
|
|
17
|
+
import { capableAgents, isCapable } from './capabilities.js';
|
|
17
18
|
import { shouldInstallCommandAsSkill, installCommandSkillToVersion } from './command-skills.js';
|
|
18
|
-
import { copyPluginToMarketplace, syncMarketplaceManifest, registerMarketplace, unregisterMarketplace,
|
|
19
|
+
import { copyPluginToMarketplace, syncMarketplaceManifest, registerMarketplace, unregisterMarketplace, addPluginToSettings, removePluginFromSettings, removePluginFromMarketplace, marketplaceIsEmpty, removeEmptyMarketplaceDir, isInstalledInMarketplace, marketplaceRoot, discoverMarketplaces, marketplaceNameFor, MARKETPLACE_NAME, PROJECT_MARKETPLACE_NAME, } from './plugin-marketplace.js';
|
|
19
20
|
const PLUGIN_MANIFEST_DIR = '.claude-plugin';
|
|
20
21
|
const PLUGIN_MANIFEST_FILE = 'plugin.json';
|
|
21
22
|
const USER_CONFIG_FILE = '.user-config.json';
|
|
@@ -43,11 +44,15 @@ function isPluginRootEntry(pluginsDir, entry) {
|
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
46
|
-
* Discover all plugins in ~/.agents/plugins
|
|
47
|
+
* Discover all plugins in a given plugins directory (e.g. ~/.agents/plugins/,
|
|
48
|
+
* ~/.agents/.system/plugins/, <cwd>/.agents/plugins/, ~/.agents-<alias>/plugins/).
|
|
47
49
|
* A valid plugin has a .claude-plugin/plugin.json manifest.
|
|
50
|
+
*
|
|
51
|
+
* `spec` stamps marketplace provenance onto each discovered plugin. Callers that
|
|
52
|
+
* scan a single source dir without a marketplace identity (e.g. project-launch)
|
|
53
|
+
* may omit it; those plugins default to the user marketplace.
|
|
48
54
|
*/
|
|
49
|
-
export function
|
|
50
|
-
const pluginsDir = getPluginsDir();
|
|
55
|
+
export function discoverPluginsInDir(pluginsDir, spec = { kind: 'user' }) {
|
|
51
56
|
if (!fs.existsSync(pluginsDir)) {
|
|
52
57
|
return [];
|
|
53
58
|
}
|
|
@@ -60,15 +65,32 @@ export function discoverPlugins() {
|
|
|
60
65
|
const manifest = loadPluginManifest(pluginRoot);
|
|
61
66
|
if (!manifest)
|
|
62
67
|
continue;
|
|
63
|
-
plugins.push(buildDiscoveredPlugin(pluginRoot, manifest));
|
|
68
|
+
plugins.push(buildDiscoveredPlugin(pluginRoot, manifest, spec));
|
|
64
69
|
}
|
|
65
70
|
return plugins;
|
|
66
71
|
}
|
|
67
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Discover every plugin across ALL marketplaces — the user repo (~/.agents/),
|
|
74
|
+
* each enabled extra repo (~/.agents-<alias>/), and the project repo
|
|
75
|
+
* (<cwd>/.agents/) — stamping marketplace provenance onto each.
|
|
76
|
+
*
|
|
77
|
+
* Plugin names are NOT deduplicated across marketplaces: a `code` plugin in both
|
|
78
|
+
* the user repo and an extra repo yields two entries (`code@agents-cli` and
|
|
79
|
+
* `code@agents-<alias>`), each installing into its own marketplace directory.
|
|
80
|
+
*/
|
|
81
|
+
export function discoverPlugins(opts = {}) {
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const dm of discoverMarketplaces(opts)) {
|
|
84
|
+
out.push(...discoverPluginsInDir(dm.pluginsRoot, dm.spec));
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
export function buildDiscoveredPlugin(pluginRoot, manifest, spec = { kind: 'user' }) {
|
|
68
89
|
return {
|
|
69
90
|
name: manifest.name,
|
|
70
91
|
root: pluginRoot,
|
|
71
92
|
manifest,
|
|
93
|
+
marketplace: marketplaceNameFor(spec),
|
|
72
94
|
skills: discoverPluginSkills(pluginRoot),
|
|
73
95
|
hooks: discoverPluginHooks(pluginRoot),
|
|
74
96
|
scripts: discoverPluginScripts(pluginRoot),
|
|
@@ -151,7 +173,7 @@ export function getPlugin(name) {
|
|
|
151
173
|
* Otherwise defaults to all plugin-capable agents.
|
|
152
174
|
*/
|
|
153
175
|
export function pluginSupportsAgent(plugin, agent) {
|
|
154
|
-
if (!
|
|
176
|
+
if (!isCapable(agent, 'plugins'))
|
|
155
177
|
return false;
|
|
156
178
|
if (plugin.manifest.agents && plugin.manifest.agents.length > 0) {
|
|
157
179
|
return plugin.manifest.agents.includes(agent);
|
|
@@ -274,7 +296,7 @@ function pluginHasNonPermissionSettings(pluginRoot) {
|
|
|
274
296
|
* ${user_config.<key>} -> value from plugin's .user-config.json
|
|
275
297
|
*/
|
|
276
298
|
export function expandPluginVars(str, pluginRoot, pluginName, agentId, versionHome, userConfig) {
|
|
277
|
-
const dataDir = path.join(versionHome,
|
|
299
|
+
const dataDir = path.join(versionHome, agentConfigDirName(agentId), 'plugin-data', pluginName);
|
|
278
300
|
let result = str
|
|
279
301
|
.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, pluginRoot)
|
|
280
302
|
.replace(/\$\{CLAUDE_PLUGIN_DATA\}/g, dataDir);
|
|
@@ -319,6 +341,42 @@ export function checkPluginDependencies(manifest) {
|
|
|
319
341
|
const installed = new Set(discoverPlugins().map(p => p.name));
|
|
320
342
|
return manifest.dependencies.filter(dep => !installed.has(dep));
|
|
321
343
|
}
|
|
344
|
+
// ─── Marketplace routing ──────────────────────────────────────────────────────
|
|
345
|
+
/**
|
|
346
|
+
* Reconstruct a MarketplaceSpec from a marketplace name. The inverse of
|
|
347
|
+
* marketplaceNameFor(): "agents-cli" → user, "agents-project" → project,
|
|
348
|
+
* "agents-<alias>" → extra. The per-version marketplace operations only key off
|
|
349
|
+
* the name (never spec.root), but we resolve the real source root anyway so the
|
|
350
|
+
* spec is honest for any caller that inspects it.
|
|
351
|
+
*/
|
|
352
|
+
function marketplaceSpecForName(name, cwd = process.cwd()) {
|
|
353
|
+
if (!name || name === MARKETPLACE_NAME)
|
|
354
|
+
return { kind: 'user' };
|
|
355
|
+
if (name === PROJECT_MARKETPLACE_NAME) {
|
|
356
|
+
return { kind: 'project', root: getProjectPluginsDir(cwd) ?? '' };
|
|
357
|
+
}
|
|
358
|
+
const alias = name.slice('agents-'.length);
|
|
359
|
+
return { kind: 'extra', alias, root: getExtraPluginsDir(alias) };
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* List the marketplace names that have been synthesized under a version home
|
|
363
|
+
* (i.e. the directories beneath .{agent}/plugins/marketplaces/). Used by
|
|
364
|
+
* removal/orphan/diff passes that must touch every marketplace a version
|
|
365
|
+
* carries, not just the user one.
|
|
366
|
+
*/
|
|
367
|
+
function listVersionMarketplaceNames(agent, versionHome) {
|
|
368
|
+
const dir = path.join(versionHome, `.${agent}`, 'plugins', 'marketplaces');
|
|
369
|
+
if (!fs.existsSync(dir))
|
|
370
|
+
return [];
|
|
371
|
+
try {
|
|
372
|
+
return fs.readdirSync(dir, { withFileTypes: true })
|
|
373
|
+
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
374
|
+
.map(d => d.name);
|
|
375
|
+
}
|
|
376
|
+
catch {
|
|
377
|
+
return [];
|
|
378
|
+
}
|
|
379
|
+
}
|
|
322
380
|
// ─── Main sync entry point ────────────────────────────────────────────────────
|
|
323
381
|
/**
|
|
324
382
|
* Sync a plugin to a specific agent version's home directory.
|
|
@@ -352,8 +410,13 @@ export function syncPluginToVersion(plugin, agent, versionHome, options = {}) {
|
|
|
352
410
|
return result;
|
|
353
411
|
}
|
|
354
412
|
const userConfig = loadUserConfig(plugin.name);
|
|
413
|
+
// Route every marketplace op through the plugin's own marketplace, so a plugin
|
|
414
|
+
// discovered in an extra/project repo installs under its own
|
|
415
|
+
// marketplaces/<name>/ tree — never the user marketplace.
|
|
416
|
+
const spec = marketplaceSpecForName(plugin.marketplace);
|
|
417
|
+
const marketplaceName = marketplaceNameFor(spec);
|
|
355
418
|
// 1. Copy plugin to native marketplace install dir.
|
|
356
|
-
const installDir = copyPluginToMarketplace(plugin, agent, versionHome);
|
|
419
|
+
const installDir = copyPluginToMarketplace(plugin, spec, agent, versionHome);
|
|
357
420
|
// 2. Pre-expand ${user_config.*} in the copy. Leave ${CLAUDE_PLUGIN_ROOT} /
|
|
358
421
|
// ${CLAUDE_PLUGIN_DATA} alone — Claude expands those natively at runtime.
|
|
359
422
|
if (Object.keys(userConfig).length > 0) {
|
|
@@ -371,16 +434,20 @@ export function syncPluginToVersion(plugin, agent, versionHome, options = {}) {
|
|
|
371
434
|
}
|
|
372
435
|
}
|
|
373
436
|
// 3-5. Synthesize manifest, register marketplace, enable plugin.
|
|
374
|
-
syncMarketplaceManifest(agent, versionHome);
|
|
375
|
-
registerMarketplace(agent, versionHome);
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
437
|
+
syncMarketplaceManifest(spec, agent, versionHome);
|
|
438
|
+
registerMarketplace(spec, agent, versionHome);
|
|
439
|
+
// Trust gate: plugins with executable surfaces (hooks/, bin/, scripts/,
|
|
440
|
+
// .mcp.json, settings.json, permissions/) are only auto-enabled when the
|
|
441
|
+
// caller explicitly opts in. addPluginToSettings does no gating — that moved
|
|
442
|
+
// here, where plugin capabilities are inspected.
|
|
443
|
+
if (options.allowExecSurfaces === true || !hasPluginExecSurfaces(inspectPluginCapabilities(plugin.root))) {
|
|
444
|
+
addPluginToSettings(plugin.name, marketplaceName, agent, versionHome);
|
|
445
|
+
}
|
|
379
446
|
// 5b. Convert plugin commands/ to skills for agents that dropped command support
|
|
380
447
|
// (Codex >= 0.117.0). Skill name is prefixed with plugin name to avoid
|
|
381
448
|
// collision with standalone command skills.
|
|
382
449
|
if (options.version && shouldInstallCommandAsSkill(agent, options.version) && plugin.commands.length > 0) {
|
|
383
|
-
const agentDir = path.join(versionHome,
|
|
450
|
+
const agentDir = path.join(versionHome, agentConfigDirName(agent));
|
|
384
451
|
const skillSourceDirs = [path.join(agentDir, 'skills')];
|
|
385
452
|
for (const cmd of plugin.commands) {
|
|
386
453
|
const srcPath = path.join(plugin.root, 'commands', `${cmd}.md`);
|
|
@@ -465,7 +532,7 @@ function expandUserConfigInDir(dir, userConfig) {
|
|
|
465
532
|
*/
|
|
466
533
|
function migrateLegacyFlatLayout(plugin, agent, versionHome) {
|
|
467
534
|
const prefix = `${plugin.name}--`;
|
|
468
|
-
const agentRoot = path.join(versionHome,
|
|
535
|
+
const agentRoot = path.join(versionHome, agentConfigDirName(agent));
|
|
469
536
|
// 1. skills
|
|
470
537
|
const skillsDir = path.join(agentRoot, 'skills');
|
|
471
538
|
if (fs.existsSync(skillsDir)) {
|
|
@@ -598,9 +665,9 @@ function migrateLegacyFlatLayout(plugin, agent, versionHome) {
|
|
|
598
665
|
* they're treated as stale and migrated away on the next sync.
|
|
599
666
|
*/
|
|
600
667
|
export function isPluginSynced(plugin, agent, versionHome) {
|
|
601
|
-
if (!
|
|
668
|
+
if (!isCapable(agent, 'plugins'))
|
|
602
669
|
return false;
|
|
603
|
-
return isInstalledInMarketplace(plugin.name, agent, versionHome);
|
|
670
|
+
return isInstalledInMarketplace(plugin.name, marketplaceSpecForName(plugin.marketplace), agent, versionHome);
|
|
604
671
|
}
|
|
605
672
|
// ─── Removal ─────────────────────────────────────────────────────────────────
|
|
606
673
|
/**
|
|
@@ -619,19 +686,26 @@ export function removePluginFromVersion(pluginName, pluginRoot, agent, versionHo
|
|
|
619
686
|
permissions: 0,
|
|
620
687
|
mcp: 0,
|
|
621
688
|
};
|
|
622
|
-
// 1. Remove the plugin from
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
689
|
+
// 1. Remove the plugin from every marketplace it's installed under. A name can
|
|
690
|
+
// appear in more than one (collision across repos), so we sweep them all.
|
|
691
|
+
let removedAny = false;
|
|
692
|
+
for (const name of listVersionMarketplaceNames(agent, versionHome)) {
|
|
693
|
+
const spec = marketplaceSpecForName(name);
|
|
694
|
+
if (removePluginFromMarketplace(pluginName, name, agent, versionHome)) {
|
|
695
|
+
removedAny = true;
|
|
696
|
+
}
|
|
697
|
+
removePluginFromSettings(pluginName, name, agent, versionHome);
|
|
698
|
+
// Refresh marketplace.json so it reflects what's left under plugins/.
|
|
699
|
+
syncMarketplaceManifest(spec, agent, versionHome);
|
|
700
|
+
// If we just removed the last plugin, drop the marketplace dir and the
|
|
701
|
+
// known_marketplaces.json entry too.
|
|
702
|
+
if (marketplaceIsEmpty(name, agent, versionHome)) {
|
|
703
|
+
removeEmptyMarketplaceDir(name, agent, versionHome);
|
|
704
|
+
unregisterMarketplace(name, agent, versionHome);
|
|
705
|
+
}
|
|
626
706
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
syncMarketplaceManifest(agent, versionHome);
|
|
630
|
-
// 3. If we just removed the last plugin, drop the marketplace dir and the
|
|
631
|
-
// known_marketplaces.json entry too.
|
|
632
|
-
if (marketplaceIsEmpty(agent, versionHome)) {
|
|
633
|
-
removeEmptyMarketplaceDir(agent, versionHome);
|
|
634
|
-
unregisterMarketplace(agent, versionHome);
|
|
707
|
+
if (removedAny) {
|
|
708
|
+
result.skills.push(pluginName);
|
|
635
709
|
}
|
|
636
710
|
// 4. Strip any legacy dual-dash entries from prior agents-cli versions.
|
|
637
711
|
cleanLegacyFlatLayout(pluginName, pluginRoot, agent, versionHome, result);
|
|
@@ -643,7 +717,7 @@ export function removePluginFromVersion(pluginName, pluginRoot, agent, versionHo
|
|
|
643
717
|
*/
|
|
644
718
|
function cleanLegacyFlatLayout(pluginName, pluginRoot, agent, versionHome, result) {
|
|
645
719
|
const prefix = `${pluginName}--`;
|
|
646
|
-
const agentRoot = path.join(versionHome,
|
|
720
|
+
const agentRoot = path.join(versionHome, agentConfigDirName(agent));
|
|
647
721
|
const skillsDir = path.join(agentRoot, 'skills');
|
|
648
722
|
if (fs.existsSync(skillsDir)) {
|
|
649
723
|
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
@@ -786,9 +860,13 @@ function cleanLegacyFlatLayout(pluginName, pluginRoot, agent, versionHome, resul
|
|
|
786
860
|
*/
|
|
787
861
|
export function cleanOrphanedPluginSkills(agent, versionHome, activePluginNames, version) {
|
|
788
862
|
const removed = [];
|
|
789
|
-
// 1. Walk
|
|
790
|
-
const
|
|
791
|
-
|
|
863
|
+
// 1. Walk every marketplace's install dir and trash entries no longer active.
|
|
864
|
+
for (const name of listVersionMarketplaceNames(agent, versionHome)) {
|
|
865
|
+
const spec = marketplaceSpecForName(name);
|
|
866
|
+
const mktPluginsDir = path.join(marketplaceRoot(name, agent, versionHome), 'plugins');
|
|
867
|
+
if (!fs.existsSync(mktPluginsDir))
|
|
868
|
+
continue;
|
|
869
|
+
let trashedHere = false;
|
|
792
870
|
for (const entry of fs.readdirSync(mktPluginsDir, { withFileTypes: true })) {
|
|
793
871
|
if (!entry.isDirectory() || entry.name.startsWith('.'))
|
|
794
872
|
continue;
|
|
@@ -800,22 +878,23 @@ export function cleanOrphanedPluginSkills(agent, versionHome, activePluginNames,
|
|
|
800
878
|
const trashDest = path.join(trashDir, stamp);
|
|
801
879
|
fs.mkdirSync(trashDir, { recursive: true, mode: 0o700 });
|
|
802
880
|
fs.renameSync(path.join(mktPluginsDir, entry.name), trashDest);
|
|
803
|
-
|
|
881
|
+
removePluginFromSettings(entry.name, name, agent, versionHome);
|
|
804
882
|
removed.push(entry.name);
|
|
883
|
+
trashedHere = true;
|
|
805
884
|
}
|
|
806
885
|
catch { /* skip on error */ }
|
|
807
886
|
}
|
|
808
887
|
// Keep manifest in sync with on-disk state and drop the marketplace if empty.
|
|
809
|
-
if (
|
|
810
|
-
syncMarketplaceManifest(agent, versionHome);
|
|
811
|
-
if (marketplaceIsEmpty(agent, versionHome)) {
|
|
812
|
-
removeEmptyMarketplaceDir(agent, versionHome);
|
|
813
|
-
unregisterMarketplace(agent, versionHome);
|
|
888
|
+
if (trashedHere) {
|
|
889
|
+
syncMarketplaceManifest(spec, agent, versionHome);
|
|
890
|
+
if (marketplaceIsEmpty(name, agent, versionHome)) {
|
|
891
|
+
removeEmptyMarketplaceDir(name, agent, versionHome);
|
|
892
|
+
unregisterMarketplace(name, agent, versionHome);
|
|
814
893
|
}
|
|
815
894
|
}
|
|
816
895
|
}
|
|
817
896
|
// 2. Sweep legacy dual-dash skills directories from older agents-cli versions.
|
|
818
|
-
const skillsDir = path.join(versionHome,
|
|
897
|
+
const skillsDir = path.join(versionHome, agentConfigDirName(agent), 'skills');
|
|
819
898
|
if (fs.existsSync(skillsDir)) {
|
|
820
899
|
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
821
900
|
if (!entry.isDirectory())
|
|
@@ -843,8 +922,10 @@ export function diffVersionPlugins(agent, version) {
|
|
|
843
922
|
const versionHome = getVersionHomePath(agent, version);
|
|
844
923
|
const activePlugins = new Set(discoverPlugins().map(p => p.name));
|
|
845
924
|
const orphans = [];
|
|
846
|
-
const
|
|
847
|
-
|
|
925
|
+
for (const name of listVersionMarketplaceNames(agent, versionHome)) {
|
|
926
|
+
const mktPluginsDir = path.join(marketplaceRoot(name, agent, versionHome), 'plugins');
|
|
927
|
+
if (!fs.existsSync(mktPluginsDir))
|
|
928
|
+
continue;
|
|
848
929
|
for (const entry of fs.readdirSync(mktPluginsDir, { withFileTypes: true })) {
|
|
849
930
|
if (!entry.isDirectory() || entry.name.startsWith('.'))
|
|
850
931
|
continue;
|
|
@@ -854,7 +935,7 @@ export function diffVersionPlugins(agent, version) {
|
|
|
854
935
|
}
|
|
855
936
|
}
|
|
856
937
|
// Also surface legacy dual-dash skill dirs as orphans during migration period.
|
|
857
|
-
const skillsDir = path.join(versionHome,
|
|
938
|
+
const skillsDir = path.join(versionHome, agentConfigDirName(agent), 'skills');
|
|
858
939
|
if (fs.existsSync(skillsDir)) {
|
|
859
940
|
for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
|
|
860
941
|
if (!entry.isDirectory())
|
|
@@ -872,9 +953,9 @@ export function diffVersionPlugins(agent, version) {
|
|
|
872
953
|
}
|
|
873
954
|
export function iterPluginsCapableVersions(filter) {
|
|
874
955
|
const pairs = [];
|
|
875
|
-
const agents = filter?.agent ? [filter.agent] :
|
|
956
|
+
const agents = filter?.agent ? [filter.agent] : capableAgents('plugins');
|
|
876
957
|
for (const agent of agents) {
|
|
877
|
-
if (!
|
|
958
|
+
if (!isCapable(agent, 'plugins'))
|
|
878
959
|
continue;
|
|
879
960
|
const versions = listInstalledVersions(agent);
|
|
880
961
|
for (const version of versions) {
|
|
@@ -887,7 +968,7 @@ export function iterPluginsCapableVersions(filter) {
|
|
|
887
968
|
}
|
|
888
969
|
export function removePluginSkillFromVersion(agent, version, skillName) {
|
|
889
970
|
const versionHome = getVersionHomePath(agent, version);
|
|
890
|
-
const skillPath = path.join(versionHome,
|
|
971
|
+
const skillPath = path.join(versionHome, agentConfigDirName(agent), 'skills', skillName);
|
|
891
972
|
if (!fs.existsSync(skillPath)) {
|
|
892
973
|
return { success: true };
|
|
893
974
|
}
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
//
|
|
11
11
|
// Important limitation of Claude Code + non-Anthropic models via OpenRouter:
|
|
12
12
|
// Claude Code sends `thinking:{type:"enabled"}` in its Anthropic payload by
|
|
13
|
-
// default, and its
|
|
14
|
-
// contains thinking/redacted_thinking blocks — even when the model
|
|
15
|
-
// emits a text block. This means reasoning models work fine in
|
|
16
|
-
// `claude` mode (same env vars) but
|
|
17
|
-
// empty stdout.
|
|
13
|
+
// default, and its headless output consolidation returns empty text when a
|
|
14
|
+
// response contains thinking/redacted_thinking blocks — even when the model
|
|
15
|
+
// *also* emits a text block. This means reasoning models work fine in
|
|
16
|
+
// interactive `claude` mode (same env vars) but headless invocations
|
|
17
|
+
// (`agents run <profile> "<prompt>"`) see empty stdout.
|
|
18
18
|
//
|
|
19
|
-
// Presets flagged "
|
|
19
|
+
// Presets flagged "headless-safe" use non-reasoning variants that ignore
|
|
20
20
|
// thinking:enabled. Presets flagged "reasoning" are the leaderboard leaders
|
|
21
21
|
// but are best invoked interactively.
|
|
22
22
|
const OPENROUTER_BASE = 'https://openrouter.ai/api';
|
|
@@ -30,7 +30,7 @@ export const PRESETS = [
|
|
|
30
30
|
// ----- Top coding (via OpenRouter) -----
|
|
31
31
|
{
|
|
32
32
|
name: 'kimi',
|
|
33
|
-
description: 'Kimi K2.5 via OpenRouter (262K ctx, $0.38/$1.72 per 1M). Top Kimi: 99% HumanEval, 76.8% SWE-bench. REASONING — works interactively, but `agents run
|
|
33
|
+
description: 'Kimi K2.5 via OpenRouter (262K ctx, $0.38/$1.72 per 1M). Top Kimi: 99% HumanEval, 76.8% SWE-bench. REASONING — works interactively, but `agents run kimi "<prompt>"` (headless) returns empty stdout. Use `kimi-chat` preset for scripting.',
|
|
34
34
|
...OPENROUTER_AUTH,
|
|
35
35
|
env: {
|
|
36
36
|
ANTHROPIC_BASE_URL: OPENROUTER_BASE,
|
|
@@ -40,7 +40,7 @@ export const PRESETS = [
|
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
name: 'kimi-chat',
|
|
43
|
-
description: 'Kimi K2 0905 via OpenRouter (262K ctx, $0.40/$2.00 per 1M). Non-reasoning sibling of K2.5 — slightly older but
|
|
43
|
+
description: 'Kimi K2 0905 via OpenRouter (262K ctx, $0.40/$2.00 per 1M). Non-reasoning sibling of K2.5 — slightly older but HEADLESS-SAFE, works end-to-end with `agents run kimi-chat "<prompt>"` and in scripts/automation.',
|
|
44
44
|
...OPENROUTER_AUTH,
|
|
45
45
|
env: {
|
|
46
46
|
ANTHROPIC_BASE_URL: OPENROUTER_BASE,
|
|
@@ -50,7 +50,7 @@ export const PRESETS = [
|
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
name: 'minimax',
|
|
53
|
-
description: 'MiniMax M2.5 via OpenRouter (230B params). #1 SWE-bench Verified (80.2%) on Apr 2026 leaderboards. REASONING — works interactively,
|
|
53
|
+
description: 'MiniMax M2.5 via OpenRouter (230B params). #1 SWE-bench Verified (80.2%) on Apr 2026 leaderboards. REASONING — works interactively, headless `agents run` returns empty stdout.',
|
|
54
54
|
...OPENROUTER_AUTH,
|
|
55
55
|
env: {
|
|
56
56
|
ANTHROPIC_BASE_URL: OPENROUTER_BASE,
|
|
@@ -60,7 +60,7 @@ export const PRESETS = [
|
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
name: 'glm',
|
|
63
|
-
description: 'GLM 5 via OpenRouter (80K ctx, $0.72/$2.30 per 1M). #1 Chatbot Arena ELO (1451) among open-weight models on BenchLM.ai (Apr 2026). Prompt-complexity-dependent reasoning — Claude Code\'s 38K system prompt typically triggers thinking blocks, so
|
|
63
|
+
description: 'GLM 5 via OpenRouter (80K ctx, $0.72/$2.30 per 1M). #1 Chatbot Arena ELO (1451) among open-weight models on BenchLM.ai (Apr 2026). Prompt-complexity-dependent reasoning — Claude Code\'s 38K system prompt typically triggers thinking blocks, so headless invocations are unreliable. Interactive use is fine.',
|
|
64
64
|
...OPENROUTER_AUTH,
|
|
65
65
|
env: {
|
|
66
66
|
ANTHROPIC_BASE_URL: OPENROUTER_BASE,
|
|
@@ -70,7 +70,7 @@ export const PRESETS = [
|
|
|
70
70
|
},
|
|
71
71
|
{
|
|
72
72
|
name: 'qwen',
|
|
73
|
-
description: 'Qwen3 Coder Next via OpenRouter (256K ctx, $0.15/$0.80 per 1M, sparse MoE 80B/3B active). Latest coding-specific Qwen (Feb 2026).
|
|
73
|
+
description: 'Qwen3 Coder Next via OpenRouter (256K ctx, $0.15/$0.80 per 1M, sparse MoE 80B/3B active). Latest coding-specific Qwen (Feb 2026). HEADLESS-SAFE — works with `agents run qwen "<prompt>"`.',
|
|
74
74
|
...OPENROUTER_AUTH,
|
|
75
75
|
env: {
|
|
76
76
|
ANTHROPIC_BASE_URL: OPENROUTER_BASE,
|
|
@@ -80,7 +80,7 @@ export const PRESETS = [
|
|
|
80
80
|
},
|
|
81
81
|
{
|
|
82
82
|
name: 'deepseek',
|
|
83
|
-
description: 'DeepSeek Chat V3 (0324) via OpenRouter. Latest DeepSeek Chat variant that ignores thinking:enabled.
|
|
83
|
+
description: 'DeepSeek Chat V3 (0324) via OpenRouter. Latest DeepSeek Chat variant that ignores thinking:enabled. HEADLESS-SAFE. The newer V3.2 / V3.1-Terminus / V3.2-Speciale are reasoning variants — use `--model deepseek/deepseek-v3.2` to override if you want those for interactive use.',
|
|
84
84
|
...OPENROUTER_AUTH,
|
|
85
85
|
env: {
|
|
86
86
|
ANTHROPIC_BASE_URL: OPENROUTER_BASE,
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launch-time project compile. Invoked by the agent shim's hot path (via
|
|
3
|
+
* `agents sync --launch`) between version resolve and binary exec.
|
|
4
|
+
*
|
|
5
|
+
* Three responsibilities, all skip-fast when there's nothing to do:
|
|
6
|
+
*
|
|
7
|
+
* 1. Compile project rules from `<cwd>/.agents/rules/` into `<cwd>/AGENTS.md`
|
|
8
|
+
* (+ per-agent symlinks). Delegates to compileRulesForProject, which is
|
|
9
|
+
* the same helper management-side `agents sync` uses.
|
|
10
|
+
*
|
|
11
|
+
* 2. Mirror project resources from `<cwd>/.agents/{subagents,commands,skills}`
|
|
12
|
+
* into the agent's workspace-local discovery dirs (`<cwd>/.claude/agents/`,
|
|
13
|
+
* `<cwd>/.claude/commands/`, `<cwd>/.claude/skills/`). File-level symlinks
|
|
14
|
+
* so edits in the source dir are live without re-sync. Skips any dest
|
|
15
|
+
* entry that already exists and isn't one of our symlinks (don't-clobber).
|
|
16
|
+
* v1: claude-only. Other agents have varying workspace conventions (amp
|
|
17
|
+
* uses `~/.config/amp`, antigravity uses `~/.gemini/antigravity-cli`,
|
|
18
|
+
* codex/gemini/cursor lack subagent support entirely) — they're follow-up
|
|
19
|
+
* material. NOTE: `.mcp.json` is intentionally NOT auto-symlinked from
|
|
20
|
+
* the launch path — that's a supply-chain surface (cloning a hostile
|
|
21
|
+
* repo would auto-register an attacker MCP server). Belongs to full
|
|
22
|
+
* `agents sync` with explicit opt-in, not the hot path.
|
|
23
|
+
*
|
|
24
|
+
* 3. Synthesize four scope-grouped plugin marketplaces under the version's
|
|
25
|
+
* `<versionHome>/.{agent}/plugins/marketplaces/` (for plugin-capable agents):
|
|
26
|
+
* - agents-cli ← ~/.agents/plugins/* (user scope, legacy name)
|
|
27
|
+
* - agents-system ← ~/.agents/.system/plugins/*
|
|
28
|
+
* - extras-<alias> ← ~/.agents-<alias>/plugins/* (per enabled extra)
|
|
29
|
+
* - agents-project ← <cwd>/.agents/plugins/*
|
|
30
|
+
* Each plugin is copied in (skip-fast via mtime cache), the marketplace
|
|
31
|
+
* catalog is rewritten only when contents change, and the marketplace is
|
|
32
|
+
* registered in known_marketplaces.json. Upstream marketplaces like
|
|
33
|
+
* "claude-plugins-official" are left untouched. Project- and extras-
|
|
34
|
+
* scope plugins do NOT auto-enable exec surfaces (.mcp.json, hooks, bin/,
|
|
35
|
+
* scripts/) — user must explicitly `agents plugins enable` them.
|
|
36
|
+
*
|
|
37
|
+
* Heavy work (version-home reconciliation, hook registration, MCP merging)
|
|
38
|
+
* stays in `agents sync` without --launch and is NOT touched here. The
|
|
39
|
+
* launch path is filesystem-only and skip-fast: sub-50ms when no source
|
|
40
|
+
* has changed, scales linearly only with newly-modified plugins on the
|
|
41
|
+
* change path.
|
|
42
|
+
*/
|
|
43
|
+
import type { AgentId } from './types.js';
|
|
44
|
+
import { pluginInstallDir } from './plugin-marketplace.js';
|
|
45
|
+
export interface LaunchSyncOptions {
|
|
46
|
+
agent: AgentId;
|
|
47
|
+
version: string;
|
|
48
|
+
cwd: string;
|
|
49
|
+
}
|
|
50
|
+
export interface LaunchSyncResult {
|
|
51
|
+
/** Project rules were re-compiled into cwd/AGENTS.md (+ per-agent symlinks). */
|
|
52
|
+
rulesCompiled: boolean;
|
|
53
|
+
/** Number of workspace resource symlinks created or refreshed. */
|
|
54
|
+
workspaceLinks: number;
|
|
55
|
+
/** Workspace resource paths we left alone because they exist and aren't ours. */
|
|
56
|
+
workspaceSkipped: string[];
|
|
57
|
+
/** Map of marketplace name → plugin names installed under it. */
|
|
58
|
+
marketplaces: Record<string, string[]>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Run the launch-time project compile. Safe to call on every agent launch:
|
|
62
|
+
* each step is idempotent and skips when its inputs are missing.
|
|
63
|
+
*
|
|
64
|
+
* After a successful run, touches the shim-side skip-fast sentinel at
|
|
65
|
+
* `~/.agents/.cache/launch-sync/<agent>@<version>@<projectslug>` so the next
|
|
66
|
+
* shim invocation can skip the node spawn entirely when no source dir is
|
|
67
|
+
* newer than the sentinel (shim schema v17+).
|
|
68
|
+
*/
|
|
69
|
+
export declare function runLaunchSync(opts: LaunchSyncOptions): LaunchSyncResult;
|
|
70
|
+
export { pluginInstallDir };
|