@phnx-labs/agents-cli 1.18.1 → 1.18.3
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 +22 -0
- package/dist/commands/doctor.js +19 -5
- package/dist/commands/exec.js +9 -4
- package/dist/commands/plugins.js +58 -14
- package/dist/commands/view.js +16 -7
- package/dist/index.js +30 -0
- package/dist/lib/hooks.js +21 -3
- package/dist/lib/migrate.js +35 -12
- package/dist/lib/plugin-marketplace.d.ts +93 -0
- package/dist/lib/plugin-marketplace.js +239 -0
- package/dist/lib/plugins.d.ts +25 -13
- package/dist/lib/plugins.js +350 -566
- package/dist/lib/shims.d.ts +3 -1
- package/dist/lib/shims.js +81 -7
- package/dist/lib/staleness/checkers/commands.d.ts +7 -0
- package/dist/lib/staleness/checkers/commands.js +27 -0
- package/dist/lib/staleness/checkers/hooks.d.ts +13 -0
- package/dist/lib/staleness/checkers/hooks.js +63 -0
- package/dist/lib/staleness/checkers/mcp.d.ts +12 -0
- package/dist/lib/staleness/checkers/mcp.js +38 -0
- package/dist/lib/staleness/checkers/permissions.d.ts +17 -0
- package/dist/lib/staleness/checkers/permissions.js +73 -0
- package/dist/lib/staleness/checkers/plugins.d.ts +11 -0
- package/dist/lib/staleness/checkers/plugins.js +39 -0
- package/dist/lib/staleness/checkers/rules.d.ts +19 -0
- package/dist/lib/staleness/checkers/rules.js +86 -0
- package/dist/lib/staleness/checkers/skills.d.ts +7 -0
- package/dist/lib/staleness/checkers/skills.js +34 -0
- package/dist/lib/staleness/checkers/subagents.d.ts +12 -0
- package/dist/lib/staleness/checkers/subagents.js +39 -0
- package/dist/lib/staleness/checkers/types.d.ts +44 -0
- package/dist/lib/staleness/checkers/types.js +20 -0
- package/dist/lib/staleness/checkers/workflows.d.ts +10 -0
- package/dist/lib/staleness/checkers/workflows.js +37 -0
- package/dist/lib/staleness/fingerprint.d.ts +38 -0
- package/dist/lib/staleness/fingerprint.js +154 -0
- package/dist/lib/staleness/index.d.ts +26 -0
- package/dist/lib/staleness/index.js +122 -0
- package/dist/lib/staleness/layers.d.ts +37 -0
- package/dist/lib/staleness/layers.js +100 -0
- package/dist/lib/staleness/types.d.ts +56 -0
- package/dist/lib/staleness/types.js +6 -0
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +2 -0
- package/dist/lib/teams/agents.d.ts +11 -20
- package/dist/lib/teams/agents.js +55 -202
- package/dist/lib/teams/index.d.ts +3 -2
- package/dist/lib/teams/index.js +2 -2
- package/dist/lib/teams/persistence.d.ts +0 -38
- package/dist/lib/teams/persistence.js +7 -329
- package/dist/lib/teams/registry.js +7 -5
- package/dist/lib/types.d.ts +6 -0
- package/dist/lib/versions.js +34 -12
- package/package.json +1 -1
- package/dist/lib/sync-manifest.d.ts +0 -81
- package/dist/lib/sync-manifest.js +0 -450
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.18.3
|
|
4
|
+
|
|
5
|
+
**Plugins** ([#22](https://github.com/phnx-labs/agents-cli/issues/22))
|
|
6
|
+
|
|
7
|
+
- `agents plugins sync` now installs plugins via Claude Code's native marketplace path — `<versionHome>/.{claude,openclaw}/plugins/marketplaces/agents-cli/plugins/<name>/` — instead of flattening contents into `~/.claude/skills/<plugin>--<skill>/`. Skills resolve as `/plugin:skill` (the documented form) instead of `/plugin--skill`. Plugins appear in Claude's `/plugins` UI under Installed and respond to `/plugin enable`, `/plugin disable`.
|
|
8
|
+
- A synthetic `agents-cli` marketplace is materialized per version: `.claude-plugin/marketplace.json` is synthesized from discovered plugins, an entry is added to `<versionHome>/.claude/plugins/known_marketplaces.json`, and `settings.json#enabledPlugins["<plugin>@agents-cli"]` is flipped to `true`. Removal is symmetric — last plugin out drops the marketplace dir and the known_marketplaces entry.
|
|
9
|
+
- The sync now copies the whole plugin tree verbatim (single `fs.cpSync`) instead of re-implementing per-feature merges into `settings.json`. Every Claude plugin feature — skills, commands, subagents, hooks, `.mcp.json`, `.lsp.json`, `monitors/monitors.json`, `bin/`, `settings.json` — is preserved end-to-end. `${CLAUDE_PLUGIN_ROOT}` and `${CLAUDE_PLUGIN_DATA}` are left intact so Claude can expand them at runtime; only `${user_config.*}` (agents-cli-specific) is pre-expanded in copied text files.
|
|
10
|
+
- Legacy dual-dash layout from prior versions is auto-migrated at sync time — `~/.claude/skills/<plugin>--*`, `~/.claude/commands/<plugin>--*.md`, `~/.claude/agents/<plugin>--*.md`, `plugin-bin/<plugin>/`, and namespaced `mcpServers["<plugin>--*"]` entries are removed after the marketplace install succeeds.
|
|
11
|
+
- `agents plugins view <name>` surfaces every feature the plugin ships: Skills, Commands, Subagents, Hooks, MCP Servers, LSP Servers, Monitors, Bin, Scripts, Settings. The `agents view <agent>@<version>` Plugins section gains MCP/LSP/Monitor/Bin/Settings counts. New `discoverPluginMcpServers`, `discoverPluginLspServers`, `discoverPluginMonitors` helpers parse `.mcp.json`, `.lsp.json`, and `monitors/monitors.json`.
|
|
12
|
+
|
|
13
|
+
## 1.18.2
|
|
14
|
+
|
|
15
|
+
**Teams**
|
|
16
|
+
|
|
17
|
+
- Dropped `~/.agents/teams/config.json` entirely. It duplicated information agents-cli already has — agent commands, enabled flags, model defaults, provider endpoints — none of which the team runner was actually reading. Teams now discover agents via `listInstalledVersions()` (the same source `agents view` uses) and invoke them via the canonical `agents run` subcommand. One spawn path, one canonical exec module (`src/lib/exec.ts`). The deprecated `AGENT_COMMANDS`, `applyEditMode`, `applyFullMode`, `readConfig`, `writeConfig`, `setAgentEnabled`, `AgentConfig`, `SwarmConfig`, `ProviderConfig`, `ModelOverrides`, `ReadConfigResult`, and `EffortLevel` (the persistence-module copy) exports are removed from `@phnx-labs/agents-cli/teams`. Migration deletes both `~/.agents/teams/config.json` and the legacy `~/.agents/config.json`.
|
|
18
|
+
- `~/.agents/teams/registry.json` moves to `~/.agents/.history/teams/registry.json` — it's per-machine runtime state (timestamps + absolute worktree paths) and shouldn't be synced across machines via `agents repo push`.
|
|
19
|
+
- New `agents run --quiet` flag suppresses the rotation banner and `Running: …` preamble lines. Used by the team runner so stream-json events reach the parser without non-JSON preamble.
|
|
20
|
+
|
|
21
|
+
**Dev builds**
|
|
22
|
+
|
|
23
|
+
- The CLI auto-detects dev builds (version stamped `0.0.0-dev.<sha>` by `scripts/install.sh`, or invoked from a working tree where `<cli-dir>/../.git/` exists) and defaults `AGENTS_NO_AUTOPULL=1`, `AGENTS_SKIP_MIGRATION=1`, and `AGENTS_CLI_DISABLE_AUTO_UPDATE=1`. No more typing those three env vars on every iteration. Production installs (registry global, no `.git/` at package root) are unaffected.
|
|
24
|
+
|
|
3
25
|
## 1.18.1
|
|
4
26
|
|
|
5
27
|
**Fixes**
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { checkAllClis } from '../lib/teams/agents.js';
|
|
3
3
|
import { AGENTS, ALL_AGENT_IDS, resolveAgentName, formatAgentError } from '../lib/agents.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { getGlobalDefault, getVersionHomePath, isVersionInstalled, listInstalledVersions, parseAgentSpec, } from '../lib/versions.js';
|
|
5
|
+
import { loadManifest, isStale } from '../lib/staleness/index.js';
|
|
6
6
|
import { diffVersionCommands, iterCommandsCapableVersions } from '../lib/commands.js';
|
|
7
7
|
import { diffVersionSkills, iterSkillsCapableVersions } from '../lib/skills.js';
|
|
8
8
|
import { diffVersionHooks, iterHooksCapableVersions } from '../lib/hooks.js';
|
|
@@ -17,13 +17,12 @@ function checkSyncStatus(cwd) {
|
|
|
17
17
|
const version = getGlobalDefault(agent);
|
|
18
18
|
if (!version)
|
|
19
19
|
continue;
|
|
20
|
-
const manifest =
|
|
20
|
+
const manifest = loadManifest(agent, version);
|
|
21
21
|
if (!manifest) {
|
|
22
22
|
rows.push({ agent, version, status: 'never-synced' });
|
|
23
23
|
continue;
|
|
24
24
|
}
|
|
25
|
-
const
|
|
26
|
-
const stale = isSyncStale(manifest, available, agent, version, cwd);
|
|
25
|
+
const stale = isStale(manifest, agent, version, cwd);
|
|
27
26
|
rows.push({ agent, version, status: stale ? 'stale' : 'fresh' });
|
|
28
27
|
}
|
|
29
28
|
return rows;
|
|
@@ -270,6 +269,21 @@ function renderTargetText(report, options) {
|
|
|
270
269
|
: null,
|
|
271
270
|
].filter(Boolean).join(' ');
|
|
272
271
|
console.log(chalk.gray(` layers: ${layerStr}`));
|
|
272
|
+
// Staleness manifest verdict — single-line summary from the staleness
|
|
273
|
+
// library, sitting alongside the detailed per-resource diff below.
|
|
274
|
+
const manifest = loadManifest(report.agent, report.version);
|
|
275
|
+
if (!manifest) {
|
|
276
|
+
console.log(chalk.gray(` manifest: ${chalk.gray('cold')} (never synced)`));
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
const stale = isStale(manifest, report.agent, report.version, report.cwd);
|
|
280
|
+
if (stale) {
|
|
281
|
+
console.log(chalk.gray(' manifest: ') + chalk.yellow('stale') + chalk.gray(' (sources changed since last sync)'));
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
console.log(chalk.gray(' manifest: ') + chalk.green('fresh'));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
273
287
|
console.log();
|
|
274
288
|
for (const kind of DOCTOR_ALL_KINDS) {
|
|
275
289
|
const rows = report.kinds[kind];
|
package/dist/commands/exec.js
CHANGED
|
@@ -51,6 +51,7 @@ export function registerRunCommand(program) {
|
|
|
51
51
|
.option('--cwd <dir>', 'Working directory for the agent (defaults to current directory)')
|
|
52
52
|
.option('--add-dir <dir>', 'Grant access to an additional directory outside the project (Claude only, repeatable)', (val, prev) => [...prev, val], [])
|
|
53
53
|
.option('--json', 'Stream events as JSON lines (for parsing by other tools)')
|
|
54
|
+
.option('--quiet', 'Suppress preamble (rotation banner, "Running:" line). Useful when piping JSON events to a parser.', false)
|
|
54
55
|
.option('--headless', 'Non-interactive mode (auto-enabled when prompt provided)', false)
|
|
55
56
|
.option('-i, --interactive', 'Force interactive mode even when a prompt is provided')
|
|
56
57
|
.option('--session-id <id>', 'Resume a previous conversation (Claude only)')
|
|
@@ -249,17 +250,19 @@ Examples:
|
|
|
249
250
|
const resolved = await resolveRunVersion(agent, strategy, cwd);
|
|
250
251
|
if (resolved.version) {
|
|
251
252
|
version = resolved.version;
|
|
252
|
-
if (resolved.rotation) {
|
|
253
|
+
if (resolved.rotation && !options.quiet) {
|
|
253
254
|
const banner = formatRotationBanner(resolved.rotation, strategy);
|
|
254
255
|
process.stderr.write(chalk.gray(banner + '\n'));
|
|
255
256
|
}
|
|
256
257
|
}
|
|
257
|
-
else {
|
|
258
|
+
else if (!options.quiet) {
|
|
258
259
|
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} found no usable ${agent} version; falling back to defaults\n`));
|
|
259
260
|
}
|
|
260
261
|
}
|
|
261
262
|
catch (err) {
|
|
262
|
-
|
|
263
|
+
if (!options.quiet) {
|
|
264
|
+
process.stderr.write(chalk.yellow(`[agents] strategy ${strategy} skipped: ${err.message}\n`));
|
|
265
|
+
}
|
|
263
266
|
}
|
|
264
267
|
}
|
|
265
268
|
}
|
|
@@ -388,7 +391,9 @@ Examples:
|
|
|
388
391
|
}
|
|
389
392
|
}
|
|
390
393
|
const cmd = buildExecCommand(execOptions);
|
|
391
|
-
|
|
394
|
+
if (!options.quiet) {
|
|
395
|
+
process.stderr.write(chalk.gray(`Running: ${cmd.join(' ')}\n\n`));
|
|
396
|
+
}
|
|
392
397
|
try {
|
|
393
398
|
let exitCode;
|
|
394
399
|
if (fallback.length > 0) {
|
package/dist/commands/plugins.js
CHANGED
|
@@ -149,7 +149,19 @@ Examples:
|
|
|
149
149
|
if (plugin.skills.length > 0) {
|
|
150
150
|
console.log(chalk.bold('\n Skills'));
|
|
151
151
|
for (const skill of plugin.skills) {
|
|
152
|
-
console.log(` ${chalk.cyan(
|
|
152
|
+
console.log(` ${chalk.cyan(`/${plugin.name}:${skill}`)}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (plugin.commands.length > 0) {
|
|
156
|
+
console.log(chalk.bold('\n Commands'));
|
|
157
|
+
for (const cmd of plugin.commands) {
|
|
158
|
+
console.log(` ${chalk.cyan(`/${plugin.name}:${cmd}`)}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (plugin.agentDefs.length > 0) {
|
|
162
|
+
console.log(chalk.bold('\n Subagents'));
|
|
163
|
+
for (const a of plugin.agentDefs) {
|
|
164
|
+
console.log(` ${chalk.magenta(a)}`);
|
|
153
165
|
}
|
|
154
166
|
}
|
|
155
167
|
if (plugin.hooks.length > 0) {
|
|
@@ -158,12 +170,40 @@ Examples:
|
|
|
158
170
|
console.log(` ${chalk.yellow(hook)}`);
|
|
159
171
|
}
|
|
160
172
|
}
|
|
173
|
+
if (plugin.mcpServers.length > 0) {
|
|
174
|
+
console.log(chalk.bold('\n MCP Servers'));
|
|
175
|
+
for (const s of plugin.mcpServers) {
|
|
176
|
+
console.log(` ${chalk.green(s)}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (plugin.lspServers.length > 0) {
|
|
180
|
+
console.log(chalk.bold('\n LSP Servers'));
|
|
181
|
+
for (const s of plugin.lspServers) {
|
|
182
|
+
console.log(` ${chalk.green(s)}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (plugin.monitors.length > 0) {
|
|
186
|
+
console.log(chalk.bold('\n Monitors'));
|
|
187
|
+
for (const m of plugin.monitors) {
|
|
188
|
+
console.log(` ${chalk.blue(m)}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (plugin.bin.length > 0) {
|
|
192
|
+
console.log(chalk.bold('\n Bin'));
|
|
193
|
+
for (const b of plugin.bin) {
|
|
194
|
+
console.log(` ${chalk.white(b)}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
161
197
|
if (plugin.scripts.length > 0) {
|
|
162
198
|
console.log(chalk.bold('\n Scripts'));
|
|
163
199
|
for (const script of plugin.scripts) {
|
|
164
200
|
console.log(` ${chalk.gray(script)}`);
|
|
165
201
|
}
|
|
166
202
|
}
|
|
203
|
+
if (plugin.hasSettings) {
|
|
204
|
+
console.log(chalk.bold('\n Settings'));
|
|
205
|
+
console.log(` ${chalk.gray('settings.json')}`);
|
|
206
|
+
}
|
|
167
207
|
// Show installation status per agent version
|
|
168
208
|
console.log(chalk.bold('\n Installation Status'));
|
|
169
209
|
let anyInstalled = false;
|
|
@@ -572,20 +612,24 @@ function formatPluginDetail(plugin, targets) {
|
|
|
572
612
|
lines.push(' ' + chalk.gray('Supports: ') + supported.join(chalk.gray(' · ')));
|
|
573
613
|
}
|
|
574
614
|
lines.push(' ' + chalk.gray(formatPath(plugin.root)));
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
lines.push(' ' + plugin.skills.map((s) => chalk.cyan(s)).join(chalk.gray(', ')));
|
|
579
|
-
}
|
|
580
|
-
if (plugin.hooks.length > 0) {
|
|
581
|
-
lines.push('');
|
|
582
|
-
lines.push(chalk.bold(' Hooks'));
|
|
583
|
-
lines.push(' ' + plugin.hooks.map((h) => chalk.yellow(h)).join(chalk.gray(', ')));
|
|
584
|
-
}
|
|
585
|
-
if (plugin.scripts.length > 0) {
|
|
615
|
+
const section = (label, items, colorFn) => {
|
|
616
|
+
if (items.length === 0)
|
|
617
|
+
return;
|
|
586
618
|
lines.push('');
|
|
587
|
-
lines.push(chalk.bold(
|
|
588
|
-
lines.push(' ' +
|
|
619
|
+
lines.push(chalk.bold(` ${label}`));
|
|
620
|
+
lines.push(' ' + items.map(colorFn).join(chalk.gray(', ')));
|
|
621
|
+
};
|
|
622
|
+
section('Skills', plugin.skills.map((s) => `/${plugin.name}:${s}`), chalk.cyan);
|
|
623
|
+
section('Commands', plugin.commands.map((c) => `/${plugin.name}:${c}`), chalk.cyan);
|
|
624
|
+
section('Subagents', plugin.agentDefs, chalk.magenta);
|
|
625
|
+
section('Hooks', plugin.hooks, chalk.yellow);
|
|
626
|
+
section('MCP Servers', plugin.mcpServers, chalk.green);
|
|
627
|
+
section('LSP Servers', plugin.lspServers, chalk.green);
|
|
628
|
+
section('Monitors', plugin.monitors, chalk.blue);
|
|
629
|
+
section('Bin', plugin.bin, chalk.white);
|
|
630
|
+
section('Scripts', plugin.scripts, chalk.white);
|
|
631
|
+
if (plugin.hasSettings) {
|
|
632
|
+
section('Settings', ['settings.json'], chalk.gray);
|
|
589
633
|
}
|
|
590
634
|
if (targets.length > 0) {
|
|
591
635
|
lines.push('');
|
package/dist/commands/view.js
CHANGED
|
@@ -604,19 +604,28 @@ async function showAgentResources(agentId, requestedVersion) {
|
|
|
604
604
|
const versionStr = agentData.version ? ` (${agentData.version})` : '';
|
|
605
605
|
const agentHeader = home ? termLink(agentData.agentName, home) : agentData.agentName;
|
|
606
606
|
console.log(` ${chalk.bold(agentHeader)}${chalk.gray(versionStr)}:`);
|
|
607
|
+
const pluralize = (n, singular) => `${n} ${singular}${n === 1 ? '' : 's'}`;
|
|
607
608
|
for (const p of plugins) {
|
|
608
609
|
const linkedName = termLink(p.name, linkTarget(p.root));
|
|
609
610
|
const parts = [];
|
|
610
611
|
if (p.skills.length > 0)
|
|
611
|
-
parts.push(
|
|
612
|
+
parts.push(pluralize(p.skills.length, 'skill'));
|
|
612
613
|
if (p.commands.length > 0)
|
|
613
|
-
parts.push(
|
|
614
|
-
if (p.hooks.length > 0)
|
|
615
|
-
parts.push(`${p.hooks.length} hook${p.hooks.length === 1 ? '' : 's'}`);
|
|
614
|
+
parts.push(pluralize(p.commands.length, 'command'));
|
|
616
615
|
if (p.agentDefs.length > 0)
|
|
617
|
-
parts.push(
|
|
618
|
-
if (p.
|
|
619
|
-
parts.push('
|
|
616
|
+
parts.push(pluralize(p.agentDefs.length, 'subagent'));
|
|
617
|
+
if (p.hooks.length > 0)
|
|
618
|
+
parts.push(pluralize(p.hooks.length, 'hook'));
|
|
619
|
+
if (p.mcpServers.length > 0)
|
|
620
|
+
parts.push(`${p.mcpServers.length} MCP`);
|
|
621
|
+
if (p.lspServers.length > 0)
|
|
622
|
+
parts.push(`${p.lspServers.length} LSP`);
|
|
623
|
+
if (p.monitors.length > 0)
|
|
624
|
+
parts.push(pluralize(p.monitors.length, 'monitor'));
|
|
625
|
+
if (p.bin.length > 0)
|
|
626
|
+
parts.push(pluralize(p.bin.length, 'bin'));
|
|
627
|
+
if (p.hasSettings)
|
|
628
|
+
parts.push('settings');
|
|
620
629
|
const contents = parts.length > 0 ? chalk.gray(` (${parts.join(', ')})`) : '';
|
|
621
630
|
console.log(` ${chalk.cyan(linkedName)}${contents} ${chalk.cyan('[user]')}`);
|
|
622
631
|
}
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,36 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
23
23
|
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
24
24
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
25
25
|
const VERSION = packageJson.version;
|
|
26
|
+
// Detect dev/working-tree builds and default the noisy startup steps off.
|
|
27
|
+
// Three cases trip this:
|
|
28
|
+
// 1. Dev install (scripts/install.sh) — package.json version stamped 0.0.0-dev.<sha>
|
|
29
|
+
// 2. Running `node dist/index.js` from a working tree — repo root has .git/
|
|
30
|
+
// 3. Running tsx/ts-node from src/ — also has .git/ at the repo root
|
|
31
|
+
// For all three: skip auto-pull (no network noise + no surprise FF on the
|
|
32
|
+
// system repo while iterating), skip migration (a buggy in-progress migration
|
|
33
|
+
// must not scribble on the user's real ~/.agents/), and skip the update prompt
|
|
34
|
+
// (the "0.0.0-dev -> 1.x.y" message is misleading). Each individual env var
|
|
35
|
+
// can still be set explicitly to override (set to '0' to re-enable).
|
|
36
|
+
const IS_DEV_BUILD = (() => {
|
|
37
|
+
if (VERSION.startsWith('0.0.0-dev'))
|
|
38
|
+
return true;
|
|
39
|
+
try {
|
|
40
|
+
const cliPath = process.argv[1] || '';
|
|
41
|
+
const repoRoot = path.dirname(path.dirname(cliPath));
|
|
42
|
+
return fs.existsSync(path.join(repoRoot, '.git'));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
48
|
+
if (IS_DEV_BUILD) {
|
|
49
|
+
if (process.env.AGENTS_NO_AUTOPULL === undefined)
|
|
50
|
+
process.env.AGENTS_NO_AUTOPULL = '1';
|
|
51
|
+
if (process.env.AGENTS_SKIP_MIGRATION === undefined)
|
|
52
|
+
process.env.AGENTS_SKIP_MIGRATION = '1';
|
|
53
|
+
if (process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE === undefined)
|
|
54
|
+
process.env.AGENTS_CLI_DISABLE_AUTO_UPDATE = '1';
|
|
55
|
+
}
|
|
26
56
|
// Import command registrations
|
|
27
57
|
import { registerPullCommand } from './commands/pull.js';
|
|
28
58
|
import { registerRepoCommands } from './commands/repo.js';
|
package/dist/lib/hooks.js
CHANGED
|
@@ -51,6 +51,18 @@ function isManagedHookCommand(command, prefixes) {
|
|
|
51
51
|
return false;
|
|
52
52
|
}
|
|
53
53
|
import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from './versions.js';
|
|
54
|
+
/**
|
|
55
|
+
* Extensions that are NEVER hooks — docs, configuration, plain data. A file
|
|
56
|
+
* in hooks/ with one of these extensions is auxiliary content (e.g., the
|
|
57
|
+
* `promptcuts.yaml` data file read directly by the expand-promptcuts
|
|
58
|
+
* script, or the `README.md` that documents the hooks directory). They
|
|
59
|
+
* sometimes carry an exec bit by accident (older sync runs chmod 0o755'd
|
|
60
|
+
* everything) but they are not scripts.
|
|
61
|
+
*/
|
|
62
|
+
const NON_SCRIPT_EXTENSIONS = new Set([
|
|
63
|
+
'.md', '.markdown', '.rst', '.txt',
|
|
64
|
+
'.yaml', '.yml', '.json', '.toml', '.ini', '.conf',
|
|
65
|
+
]);
|
|
54
66
|
const SCRIPT_EXTENSIONS = new Set([
|
|
55
67
|
'.sh',
|
|
56
68
|
'.bash',
|
|
@@ -140,9 +152,15 @@ export function listHookEntriesFromDir(dir) {
|
|
|
140
152
|
const entries = [];
|
|
141
153
|
for (const [base, group] of grouped) {
|
|
142
154
|
group.sort((a, b) => a.name.localeCompare(b.name));
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
155
|
+
// A group is a hook only if it has an actual script: a script extension,
|
|
156
|
+
// OR an executable bit on a file whose extension is not a known data /
|
|
157
|
+
// docs type. Files like `README.md` (docs) or `promptcuts.yaml` (data
|
|
158
|
+
// the expand-promptcuts hook reads directly) sit alongside hooks but
|
|
159
|
+
// are NOT hooks themselves and must not surface in the hooks list
|
|
160
|
+
// anywhere — doctor, sync, view, or otherwise. Older sync runs may have
|
|
161
|
+
// chmod 0o755'd these files; an exec bit alone is not enough.
|
|
162
|
+
const script = group.find((f) => SCRIPT_EXTENSIONS.has(f.ext.toLowerCase())) ||
|
|
163
|
+
group.find((f) => f.isExec && !NON_SCRIPT_EXTENSIONS.has(f.ext.toLowerCase()));
|
|
146
164
|
if (!script)
|
|
147
165
|
continue;
|
|
148
166
|
const data = group.find((f) => f !== script);
|
package/dist/lib/migrate.js
CHANGED
|
@@ -58,14 +58,14 @@ function deleteSystemPromptsJson() {
|
|
|
58
58
|
* The teams persistence layer already reads the legacy path as a fallback;
|
|
59
59
|
* moving it here keeps the canonical location consistent.
|
|
60
60
|
*/
|
|
61
|
+
// Delete the legacy ~/.agents-system/config.json. This was the teams agent
|
|
62
|
+
// registry, which no longer exists — `agents teams` discovers agents through
|
|
63
|
+
// `listInstalledVersions` and invokes them through `agents run`.
|
|
61
64
|
function migrateSystemConfigJson() {
|
|
62
65
|
const src = path.join(SYSTEM_DIR, 'config.json');
|
|
63
|
-
|
|
64
|
-
if (!fs.existsSync(src) || fs.existsSync(dest))
|
|
66
|
+
if (!fs.existsSync(src))
|
|
65
67
|
return;
|
|
66
68
|
try {
|
|
67
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true, mode: 0o700 });
|
|
68
|
-
fs.copyFileSync(src, dest);
|
|
69
69
|
fs.unlinkSync(src);
|
|
70
70
|
}
|
|
71
71
|
catch { /* best-effort */ }
|
|
@@ -375,20 +375,41 @@ function deleteUserPromptsJson() {
|
|
|
375
375
|
catch { /* best-effort */ }
|
|
376
376
|
}
|
|
377
377
|
/**
|
|
378
|
-
* Delete ~/.agents/config.json. The
|
|
379
|
-
*
|
|
380
|
-
*
|
|
378
|
+
* Delete ~/.agents/teams/config.json. The teams subsystem no longer carries
|
|
379
|
+
* its own agent registry — agent discovery flows through `listInstalledVersions`
|
|
380
|
+
* (the same source `agents view` uses) and invocation flows through
|
|
381
|
+
* `agents run`. The on-disk file is pure dead state on existing installs.
|
|
382
|
+
*/
|
|
383
|
+
function deleteTeamsConfigJson() {
|
|
384
|
+
const f = path.join(USER_DIR, 'teams', 'config.json');
|
|
385
|
+
if (!fs.existsSync(f))
|
|
386
|
+
return;
|
|
387
|
+
try {
|
|
388
|
+
fs.unlinkSync(f);
|
|
389
|
+
}
|
|
390
|
+
catch { /* best-effort */ }
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Move ~/.agents/teams/registry.json → ~/.agents/.history/teams/registry.json.
|
|
394
|
+
* The registry is per-machine runtime state (timestamps + absolute worktree
|
|
395
|
+
* paths) and belongs in the durable-runtime bucket, not at the user-root
|
|
396
|
+
* where `agents repo push` would sync it across machines.
|
|
397
|
+
*/
|
|
398
|
+
function moveTeamsRegistryToHistory() {
|
|
399
|
+
const src = path.join(USER_DIR, 'teams', 'registry.json');
|
|
400
|
+
const dest = path.join(HISTORY_DIR, 'teams', 'registry.json');
|
|
401
|
+
moveFileOnce(src, dest);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Delete ~/.agents/config.json. This was the legacy teams config location;
|
|
405
|
+
* the teams subsystem no longer carries a config file at all, so the legacy
|
|
406
|
+
* copy is simply removed.
|
|
381
407
|
*/
|
|
382
408
|
function cleanupUserConfigJson() {
|
|
383
409
|
const legacy = path.join(USER_DIR, 'config.json');
|
|
384
410
|
if (!fs.existsSync(legacy))
|
|
385
411
|
return;
|
|
386
|
-
const canonical = path.join(USER_DIR, 'teams', 'config.json');
|
|
387
412
|
try {
|
|
388
|
-
if (!fs.existsSync(canonical)) {
|
|
389
|
-
fs.mkdirSync(path.dirname(canonical), { recursive: true, mode: 0o700 });
|
|
390
|
-
fs.copyFileSync(legacy, canonical);
|
|
391
|
-
}
|
|
392
413
|
fs.unlinkSync(legacy);
|
|
393
414
|
}
|
|
394
415
|
catch { /* best-effort */ }
|
|
@@ -1455,6 +1476,8 @@ export async function runMigration() {
|
|
|
1455
1476
|
migratePermissionSetsToPresets();
|
|
1456
1477
|
deleteUserLinearJson();
|
|
1457
1478
|
deleteUserPromptsJson();
|
|
1479
|
+
deleteTeamsConfigJson();
|
|
1480
|
+
moveTeamsRegistryToHistory();
|
|
1458
1481
|
cleanupUserConfigJson();
|
|
1459
1482
|
cleanupEmptyTopLevelRuns();
|
|
1460
1483
|
foldUserHooksYamlIntoAgentsYaml();
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native plugin marketplace install path for Claude / OpenClaw.
|
|
3
|
+
*
|
|
4
|
+
* Plugins managed by agents-cli are exposed as a synthetic local marketplace
|
|
5
|
+
* named "agents-cli" under each version's plugin directory:
|
|
6
|
+
*
|
|
7
|
+
* <versionHome>/.{claude,openclaw}/plugins/
|
|
8
|
+
* known_marketplaces.json # registers the "agents-cli" marketplace
|
|
9
|
+
* marketplaces/agents-cli/
|
|
10
|
+
* .claude-plugin/marketplace.json # synthesized catalog
|
|
11
|
+
* plugins/<plugin>/ # copied plugin source
|
|
12
|
+
*
|
|
13
|
+
* Plus the version's settings.json gets `enabledPlugins["<plugin>@agents-cli"] = true`.
|
|
14
|
+
*
|
|
15
|
+
* This produces native `/plugin:skill` slash namespacing, visibility in `/plugins`,
|
|
16
|
+
* and `/plugin enable|disable` support — matching the Claude Code spec at
|
|
17
|
+
* https://code.claude.com/docs/en/plugins and /plugin-marketplaces.
|
|
18
|
+
*/
|
|
19
|
+
import type { AgentId, DiscoveredPlugin } from './types.js';
|
|
20
|
+
export declare const MARKETPLACE_NAME = "agents-cli";
|
|
21
|
+
interface MarketplacePluginEntry {
|
|
22
|
+
name: string;
|
|
23
|
+
source: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
version?: string;
|
|
26
|
+
author?: {
|
|
27
|
+
name: string;
|
|
28
|
+
email?: string;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
interface MarketplaceManifest {
|
|
32
|
+
$schema?: string;
|
|
33
|
+
name: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
owner: {
|
|
36
|
+
name: string;
|
|
37
|
+
email?: string;
|
|
38
|
+
};
|
|
39
|
+
plugins: MarketplacePluginEntry[];
|
|
40
|
+
}
|
|
41
|
+
export declare function marketplaceRoot(agent: AgentId, versionHome: string): string;
|
|
42
|
+
export declare function marketplaceManifestPath(agent: AgentId, versionHome: string): string;
|
|
43
|
+
export declare function pluginInstallDir(plugin: DiscoveredPlugin, agent: AgentId, versionHome: string): string;
|
|
44
|
+
export declare function knownMarketplacesPath(agent: AgentId, versionHome: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Copy plugin source into marketplace install dir.
|
|
47
|
+
* Source of truth remains ~/.agents/plugins/<name>/ — this is a per-version snapshot.
|
|
48
|
+
*/
|
|
49
|
+
export declare function copyPluginToMarketplace(plugin: DiscoveredPlugin, agent: AgentId, versionHome: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Re-synthesize <marketplace>/.claude-plugin/marketplace.json from the list of
|
|
52
|
+
* plugins installed under <marketplace>/plugins/. Always run after add or remove
|
|
53
|
+
* so the manifest stays in lockstep with on-disk contents.
|
|
54
|
+
*/
|
|
55
|
+
export declare function syncMarketplaceManifest(agent: AgentId, versionHome: string): MarketplaceManifest | null;
|
|
56
|
+
/**
|
|
57
|
+
* Register the agents-cli marketplace in known_marketplaces.json so Claude Code
|
|
58
|
+
* discovers it on startup. Idempotent: re-running just refreshes lastUpdated.
|
|
59
|
+
*/
|
|
60
|
+
export declare function registerMarketplace(agent: AgentId, versionHome: string): void;
|
|
61
|
+
/**
|
|
62
|
+
* Drop the agents-cli marketplace entry from known_marketplaces.json.
|
|
63
|
+
* Called when the last plugin under it is removed.
|
|
64
|
+
*/
|
|
65
|
+
export declare function unregisterMarketplace(agent: AgentId, versionHome: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Mark a plugin as enabled in <versionHome>/.{agent}/settings.json under
|
|
68
|
+
* enabledPlugins["<plugin>@agents-cli"]: true. Reads, mutates, writes —
|
|
69
|
+
* preserving every other key.
|
|
70
|
+
*/
|
|
71
|
+
export declare function enablePluginInSettings(pluginName: string, agent: AgentId, versionHome: string): void;
|
|
72
|
+
/**
|
|
73
|
+
* Remove the enabledPlugins key for this plugin. Inverse of enablePluginInSettings.
|
|
74
|
+
*/
|
|
75
|
+
export declare function disablePluginInSettings(pluginName: string, agent: AgentId, versionHome: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Remove a plugin's installed marketplace directory. Returns true if the dir
|
|
78
|
+
* existed and was removed.
|
|
79
|
+
*/
|
|
80
|
+
export declare function removePluginFromMarketplace(pluginName: string, agent: AgentId, versionHome: string): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Return true if the marketplace has no plugins left under it.
|
|
83
|
+
*/
|
|
84
|
+
export declare function marketplaceIsEmpty(agent: AgentId, versionHome: string): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Drop the entire marketplace directory. Called after the last plugin removal.
|
|
87
|
+
*/
|
|
88
|
+
export declare function removeEmptyMarketplaceDir(agent: AgentId, versionHome: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* Detect whether a plugin is installed via the native marketplace path.
|
|
91
|
+
*/
|
|
92
|
+
export declare function isInstalledInMarketplace(pluginName: string, agent: AgentId, versionHome: string): boolean;
|
|
93
|
+
export {};
|