@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
package/dist/lib/commands.js
CHANGED
|
@@ -9,13 +9,78 @@
|
|
|
9
9
|
import * as fs from 'fs';
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import * as yaml from 'yaml';
|
|
12
|
-
import { AGENTS,
|
|
12
|
+
import { AGENTS, ensureCommandsDir, agentConfigDirName, resolveAgentName } from './agents.js';
|
|
13
|
+
import { capableAgents, isCapable, supports } from './capabilities.js';
|
|
13
14
|
import { markdownToToml } from './convert.js';
|
|
14
15
|
import { getCommandsDir, getUserCommandsDir, getEnabledExtraRepos, getProjectAgentsDir, getSkillsDir, getTrashCommandsDir } from './state.js';
|
|
15
|
-
import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from './versions.js';
|
|
16
|
+
import { getEffectiveHome, getVersionHomePath, listInstalledVersions, resolveVersion } from './versions.js';
|
|
16
17
|
import { commandSkillMatches, installCommandSkillToVersion, listCommandSkillsInVersion, removeCommandSkillFromVersion, shouldInstallCommandAsSkill, } from './command-skills.js';
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
function compareVersions(a, b) {
|
|
19
|
+
const aParts = a.split('.').map((n) => parseInt(n, 10) || 0);
|
|
20
|
+
const bParts = b.split('.').map((n) => parseInt(n, 10) || 0);
|
|
21
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
22
|
+
const aVal = aParts[i] || 0;
|
|
23
|
+
const bVal = bParts[i] || 0;
|
|
24
|
+
if (aVal !== bVal)
|
|
25
|
+
return aVal - bVal;
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
function parseAgentsField(raw) {
|
|
30
|
+
if (raw == null)
|
|
31
|
+
return undefined;
|
|
32
|
+
const tokens = Array.isArray(raw)
|
|
33
|
+
? raw
|
|
34
|
+
: typeof raw === 'string'
|
|
35
|
+
? raw.split(/[,\s]+/)
|
|
36
|
+
: [];
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const token of tokens) {
|
|
39
|
+
const id = resolveAgentName(String(token).trim());
|
|
40
|
+
if (id && !out.includes(id))
|
|
41
|
+
out.push(id);
|
|
42
|
+
}
|
|
43
|
+
return out.length > 0 ? out : undefined;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Whether a slash command should sync to the given agent@version. Checks
|
|
47
|
+
* frontmatter `agents` / `since` / `until` after the agent-level commands
|
|
48
|
+
* (or commands-as-skills) capability gate.
|
|
49
|
+
*/
|
|
50
|
+
export function commandAppliesTo(agent, version, metadata) {
|
|
51
|
+
if (!supports(agent, 'commands', version).ok && !shouldInstallCommandAsSkill(agent, version)) {
|
|
52
|
+
return { ok: false, reason: 'unsupported' };
|
|
53
|
+
}
|
|
54
|
+
if (metadata?.agents?.length && !metadata.agents.includes(agent)) {
|
|
55
|
+
return { ok: false, reason: 'agent_excluded' };
|
|
56
|
+
}
|
|
57
|
+
if (metadata?.since && compareVersions(version, metadata.since) < 0) {
|
|
58
|
+
return { ok: false, reason: 'too_old', need: `>= ${metadata.since}` };
|
|
59
|
+
}
|
|
60
|
+
if (metadata?.until && compareVersions(version, metadata.until) >= 0) {
|
|
61
|
+
return { ok: false, reason: 'too_new', need: `< ${metadata.until}` };
|
|
62
|
+
}
|
|
63
|
+
return { ok: true };
|
|
64
|
+
}
|
|
65
|
+
export function explainCommandSkip(agent, version, commandName, result) {
|
|
66
|
+
if (result.ok)
|
|
67
|
+
return '';
|
|
68
|
+
const tag = `${agent}@${version}`;
|
|
69
|
+
switch (result.reason) {
|
|
70
|
+
case 'unsupported':
|
|
71
|
+
return `${tag}: /${commandName} not supported for this agent version`;
|
|
72
|
+
case 'agent_excluded':
|
|
73
|
+
return `${tag}: /${commandName} excluded by command frontmatter agents list`;
|
|
74
|
+
case 'too_old':
|
|
75
|
+
return `${tag}: /${commandName} requires ${result.need}`;
|
|
76
|
+
case 'too_new':
|
|
77
|
+
return `${tag}: /${commandName} requires ${result.need}`;
|
|
78
|
+
default:
|
|
79
|
+
return `${tag}: /${commandName} skipped`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Parse command metadata (name, description, targeting) from YAML frontmatter or TOML headers. */
|
|
83
|
+
export function parseCommandMetadata(filePath) {
|
|
19
84
|
if (!fs.existsSync(filePath)) {
|
|
20
85
|
return null;
|
|
21
86
|
}
|
|
@@ -31,6 +96,9 @@ function parseCommandMetadata(filePath) {
|
|
|
31
96
|
return {
|
|
32
97
|
name: parsed.name || '',
|
|
33
98
|
description: parsed.description || '',
|
|
99
|
+
agents: parseAgentsField(parsed.agents),
|
|
100
|
+
since: typeof parsed.since === 'string' ? parsed.since : undefined,
|
|
101
|
+
until: typeof parsed.until === 'string' ? parsed.until : undefined,
|
|
34
102
|
};
|
|
35
103
|
}
|
|
36
104
|
}
|
|
@@ -126,10 +194,22 @@ export function installCommand(sourcePath, agentId, commandName, method = 'symli
|
|
|
126
194
|
warnings: validation.warnings,
|
|
127
195
|
};
|
|
128
196
|
}
|
|
197
|
+
const pinnedVersion = resolveVersion(agentId, process.cwd());
|
|
198
|
+
if (pinnedVersion) {
|
|
199
|
+
const gate = commandAppliesTo(agentId, pinnedVersion, metadata);
|
|
200
|
+
if (!gate.ok) {
|
|
201
|
+
return {
|
|
202
|
+
path: '',
|
|
203
|
+
method: 'copy',
|
|
204
|
+
error: explainCommandSkip(agentId, pinnedVersion, commandName, gate),
|
|
205
|
+
warnings: validation.warnings,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
129
209
|
const agent = AGENTS[agentId];
|
|
130
210
|
ensureCommandsDir(agentId);
|
|
131
211
|
const home = getEffectiveHome(agentId);
|
|
132
|
-
const commandsDir = path.join(home,
|
|
212
|
+
const commandsDir = path.join(home, agentConfigDirName(agentId), agent.commandsSubdir);
|
|
133
213
|
fs.mkdirSync(commandsDir, { recursive: true });
|
|
134
214
|
const ext = agent.format === 'toml' ? '.toml' : '.md';
|
|
135
215
|
const targetPath = path.join(commandsDir, `${commandName}${ext}`);
|
|
@@ -157,14 +237,14 @@ export function installCommand(sourcePath, agentId, commandName, method = 'symli
|
|
|
157
237
|
*/
|
|
158
238
|
export function getVersionCommandsDir(agent, version) {
|
|
159
239
|
const home = getVersionHomePath(agent, version);
|
|
160
|
-
return path.join(home,
|
|
240
|
+
return path.join(home, agentConfigDirName(agent), AGENTS[agent].commandsSubdir);
|
|
161
241
|
}
|
|
162
242
|
/**
|
|
163
243
|
* List command names (without extension) installed in a specific version home.
|
|
164
244
|
*/
|
|
165
245
|
export function listCommandsInVersionHome(agent, version) {
|
|
166
246
|
const versionHome = getVersionHomePath(agent, version);
|
|
167
|
-
const agentDir = path.join(versionHome,
|
|
247
|
+
const agentDir = path.join(versionHome, agentConfigDirName(agent));
|
|
168
248
|
if (shouldInstallCommandAsSkill(agent, version)) {
|
|
169
249
|
return listCommandSkillsInVersion(agentDir);
|
|
170
250
|
}
|
|
@@ -186,7 +266,7 @@ function versionCommandMatches(agent, version, commandName) {
|
|
|
186
266
|
if (!fs.existsSync(sourcePath))
|
|
187
267
|
return false;
|
|
188
268
|
const versionHome = getVersionHomePath(agent, version);
|
|
189
|
-
const agentDir = path.join(versionHome,
|
|
269
|
+
const agentDir = path.join(versionHome, agentConfigDirName(agent));
|
|
190
270
|
if (shouldInstallCommandAsSkill(agent, version)) {
|
|
191
271
|
return commandSkillMatches(agentDir, commandName, sourcePath);
|
|
192
272
|
}
|
|
@@ -217,8 +297,13 @@ export function diffVersionCommands(agent, version) {
|
|
|
217
297
|
const toAdd = [];
|
|
218
298
|
const toUpdate = [];
|
|
219
299
|
const matched = [];
|
|
300
|
+
const toRemove = [];
|
|
220
301
|
const orphans = [];
|
|
221
302
|
for (const name of central) {
|
|
303
|
+
const sourcePath = path.join(getCommandsDir(), `${name}.md`);
|
|
304
|
+
const metadata = parseCommandMetadata(sourcePath);
|
|
305
|
+
if (!commandAppliesTo(agent, version, metadata).ok)
|
|
306
|
+
continue;
|
|
222
307
|
if (!installed.has(name)) {
|
|
223
308
|
toAdd.push(name);
|
|
224
309
|
}
|
|
@@ -230,10 +315,25 @@ export function diffVersionCommands(agent, version) {
|
|
|
230
315
|
}
|
|
231
316
|
}
|
|
232
317
|
for (const name of installed) {
|
|
233
|
-
if (!central.has(name))
|
|
318
|
+
if (!central.has(name)) {
|
|
234
319
|
orphans.push(name);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
const sourcePath = path.join(getCommandsDir(), `${name}.md`);
|
|
323
|
+
const metadata = parseCommandMetadata(sourcePath);
|
|
324
|
+
if (!commandAppliesTo(agent, version, metadata).ok) {
|
|
325
|
+
toRemove.push(name);
|
|
326
|
+
}
|
|
235
327
|
}
|
|
236
|
-
return {
|
|
328
|
+
return {
|
|
329
|
+
agent,
|
|
330
|
+
version,
|
|
331
|
+
toAdd: toAdd.sort(),
|
|
332
|
+
toUpdate: toUpdate.sort(),
|
|
333
|
+
matched,
|
|
334
|
+
toRemove: toRemove.sort(),
|
|
335
|
+
orphans: orphans.sort(),
|
|
336
|
+
};
|
|
237
337
|
}
|
|
238
338
|
/**
|
|
239
339
|
* Install a single command from central into a specific version home.
|
|
@@ -244,8 +344,13 @@ export function installCommandToVersion(agent, version, commandName, method = 'c
|
|
|
244
344
|
if (!fs.existsSync(sourcePath)) {
|
|
245
345
|
return { success: false, error: `Command '${commandName}' not found in central` };
|
|
246
346
|
}
|
|
347
|
+
const metadata = parseCommandMetadata(sourcePath);
|
|
348
|
+
const gate = commandAppliesTo(agent, version, metadata);
|
|
349
|
+
if (!gate.ok) {
|
|
350
|
+
return { success: true, skipped: true, skipReason: explainCommandSkip(agent, version, commandName, gate) };
|
|
351
|
+
}
|
|
247
352
|
const versionHome = getVersionHomePath(agent, version);
|
|
248
|
-
const agentDir = path.join(versionHome,
|
|
353
|
+
const agentDir = path.join(versionHome, agentConfigDirName(agent));
|
|
249
354
|
if (shouldInstallCommandAsSkill(agent, version)) {
|
|
250
355
|
return installCommandSkillToVersion(agentDir, commandName, sourcePath, [
|
|
251
356
|
getSkillsDir(),
|
|
@@ -283,7 +388,7 @@ export function installCommandToVersion(agent, version, commandName, method = 'c
|
|
|
283
388
|
*/
|
|
284
389
|
export function removeCommandFromVersion(agent, version, commandName) {
|
|
285
390
|
const versionHome = getVersionHomePath(agent, version);
|
|
286
|
-
const agentDir = path.join(versionHome,
|
|
391
|
+
const agentDir = path.join(versionHome, agentConfigDirName(agent));
|
|
287
392
|
if (shouldInstallCommandAsSkill(agent, version)) {
|
|
288
393
|
return removeCommandSkillFromVersion(agentDir, commandName);
|
|
289
394
|
}
|
|
@@ -309,9 +414,9 @@ export function removeCommandFromVersion(agent, version, commandName) {
|
|
|
309
414
|
*/
|
|
310
415
|
export function iterCommandsCapableVersions(filter) {
|
|
311
416
|
const pairs = [];
|
|
312
|
-
const agents = filter?.agent ? [filter.agent] :
|
|
417
|
+
const agents = filter?.agent ? [filter.agent] : capableAgents('commands');
|
|
313
418
|
for (const agent of agents) {
|
|
314
|
-
if (!
|
|
419
|
+
if (!isCapable(agent, 'commands'))
|
|
315
420
|
continue;
|
|
316
421
|
const versions = listInstalledVersions(agent);
|
|
317
422
|
for (const version of versions) {
|
|
@@ -326,7 +431,7 @@ export function iterCommandsCapableVersions(filter) {
|
|
|
326
431
|
export function uninstallCommand(agentId, commandName) {
|
|
327
432
|
const agent = AGENTS[agentId];
|
|
328
433
|
const home = getEffectiveHome(agentId);
|
|
329
|
-
const commandsDir = path.join(home,
|
|
434
|
+
const commandsDir = path.join(home, agentConfigDirName(agentId), agent.commandsSubdir);
|
|
330
435
|
const ext = agent.format === 'toml' ? '.toml' : '.md';
|
|
331
436
|
const targetPath = path.join(commandsDir, `${commandName}${ext}`);
|
|
332
437
|
if (fs.existsSync(targetPath)) {
|
|
@@ -339,7 +444,7 @@ export function uninstallCommand(agentId, commandName) {
|
|
|
339
444
|
function listInstalledCommands(agentId) {
|
|
340
445
|
const agent = AGENTS[agentId];
|
|
341
446
|
const home = getEffectiveHome(agentId);
|
|
342
|
-
const commandsDir = path.join(home,
|
|
447
|
+
const commandsDir = path.join(home, agentConfigDirName(agentId), agent.commandsSubdir);
|
|
343
448
|
if (!fs.existsSync(commandsDir)) {
|
|
344
449
|
return [];
|
|
345
450
|
}
|
|
@@ -355,7 +460,7 @@ function listInstalledCommands(agentId) {
|
|
|
355
460
|
function commandExists(agentId, commandName) {
|
|
356
461
|
const agent = AGENTS[agentId];
|
|
357
462
|
const home = getEffectiveHome(agentId);
|
|
358
|
-
const commandsDir = path.join(home,
|
|
463
|
+
const commandsDir = path.join(home, agentConfigDirName(agentId), agent.commandsSubdir);
|
|
359
464
|
const ext = agent.format === 'toml' ? '.toml' : '.md';
|
|
360
465
|
const targetPath = path.join(commandsDir, `${commandName}${ext}`);
|
|
361
466
|
return fs.existsSync(targetPath);
|
|
@@ -373,7 +478,7 @@ function normalizeContent(content) {
|
|
|
373
478
|
function commandContentMatches(agentId, commandName, sourcePath) {
|
|
374
479
|
const agent = AGENTS[agentId];
|
|
375
480
|
const home = getEffectiveHome(agentId);
|
|
376
|
-
const commandsDir = path.join(home,
|
|
481
|
+
const commandsDir = path.join(home, agentConfigDirName(agentId), agent.commandsSubdir);
|
|
377
482
|
const ext = agent.format === 'toml' ? '.toml' : '.md';
|
|
378
483
|
const installedPath = path.join(commandsDir, `${commandName}${ext}`);
|
|
379
484
|
if (!fs.existsSync(installedPath) || !fs.existsSync(sourcePath)) {
|
|
@@ -455,7 +560,7 @@ export function listInstalledCommandsWithScope(agentId, cwd = process.cwd(), opt
|
|
|
455
560
|
}
|
|
456
561
|
// User-scoped commands (version-aware when home is provided)
|
|
457
562
|
const home = options?.home || getEffectiveHome(agentId);
|
|
458
|
-
const userCommandsDir = path.join(home,
|
|
563
|
+
const userCommandsDir = path.join(home, agentConfigDirName(agentId), agent.commandsSubdir);
|
|
459
564
|
const userExts = [ext];
|
|
460
565
|
const userCommands = listCommandsFromDir(userCommandsDir, userExts);
|
|
461
566
|
for (const name of userCommands) {
|
|
@@ -509,7 +614,7 @@ export function installCommandCentrally(sourcePath, commandName) {
|
|
|
509
614
|
}
|
|
510
615
|
}
|
|
511
616
|
/**
|
|
512
|
-
* List commands from user (~/.agents/commands/) and system (~/.agents
|
|
617
|
+
* List commands from user (~/.agents/commands/) and system (~/.agents/.system/commands/) dirs.
|
|
513
618
|
* User dir takes priority; deduplication preserves first occurrence.
|
|
514
619
|
*/
|
|
515
620
|
export function listCentralCommands() {
|
package/dist/lib/doctor-diff.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import * as fs from 'fs';
|
|
23
23
|
import * as path from 'path';
|
|
24
|
-
import { AGENTS } from './agents.js';
|
|
24
|
+
import { AGENTS, agentConfigDirName } from './agents.js';
|
|
25
25
|
import { getProjectAgentsDir, getUserAgentsDir, getSystemAgentsDir, getEnabledExtraRepos, getResolvedRulesDir, getUserRulesDir, getEffectivePromptcutsPath, } from './state.js';
|
|
26
26
|
import { getAvailableResources, getActuallySyncedResources, getVersionHomePath, } from './versions.js';
|
|
27
27
|
import { markdownToToml } from './convert.js';
|
|
@@ -352,7 +352,7 @@ function expectedRuleContent(agent, name, sourcePath) {
|
|
|
352
352
|
function diffRules(agent, version, cwd) {
|
|
353
353
|
const agentConfig = AGENTS[agent];
|
|
354
354
|
const versionHome = getVersionHomePath(agent, version);
|
|
355
|
-
const configDir = path.join(versionHome,
|
|
355
|
+
const configDir = path.join(versionHome, agentConfigDirName(agent));
|
|
356
356
|
const sourcesByName = listRulesNames(cwd);
|
|
357
357
|
// Files actually present in the version home.
|
|
358
358
|
const homeFiles = new Set();
|
package/dist/lib/exec.d.ts
CHANGED
|
@@ -23,6 +23,20 @@ export declare function normalizeMode(input: string | null | undefined): Mode;
|
|
|
23
23
|
* - `plan` on an agent without plan support throws the same way.
|
|
24
24
|
*/
|
|
25
25
|
export declare function resolveMode(agent: AgentId, requested: Mode): Mode;
|
|
26
|
+
/**
|
|
27
|
+
* The mode an agent should run in when the caller has no preference.
|
|
28
|
+
*
|
|
29
|
+
* Returns the first entry in the agent's `capabilities.modes` table — the
|
|
30
|
+
* declaration order is the source of truth for "the safest mode this agent
|
|
31
|
+
* supports." Agents that include `plan` list it first; agents like
|
|
32
|
+
* antigravity that have no read-only mode list `edit` first.
|
|
33
|
+
*
|
|
34
|
+
* Use this when the user did not pass `--mode` explicitly. When the user
|
|
35
|
+
* *did* pass `--mode plan` and the agent doesn't support it, call
|
|
36
|
+
* `resolveMode` instead so the user sees a loud error rather than a silent
|
|
37
|
+
* elevation from read-only to writable.
|
|
38
|
+
*/
|
|
39
|
+
export declare function defaultModeFor(agent: AgentId): Mode;
|
|
26
40
|
/** Reasoning effort levels passed to agents that support them. 'auto' defers to the agent's default. */
|
|
27
41
|
export type ExecEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max' | 'auto';
|
|
28
42
|
/** Options for spawning an agent process. Omitting `prompt` launches the CLI interactively. */
|
package/dist/lib/exec.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { spawn } from 'child_process';
|
|
8
8
|
import { randomUUID } from 'crypto';
|
|
9
|
+
import * as fs from 'fs';
|
|
9
10
|
import * as path from 'path';
|
|
10
11
|
import { ALL_MODES } from './types.js';
|
|
11
12
|
import { AGENTS } from './agents.js';
|
|
@@ -14,6 +15,7 @@ import { getVersionHomePath, isVersionInstalled, resolveVersion } from './versio
|
|
|
14
15
|
import { resolveModel, buildReasoningFlags } from './models.js';
|
|
15
16
|
import { maybeRotate, createTimer, redactPrompt, redactArgs } from './events.js';
|
|
16
17
|
import { sanitizeProcessEnv } from './secrets/bundles.js';
|
|
18
|
+
import { getShimsDir } from './state.js';
|
|
17
19
|
/**
|
|
18
20
|
* Map a raw mode string (CLI flag, YAML field, env var) to the canonical Mode.
|
|
19
21
|
*
|
|
@@ -53,6 +55,22 @@ export function resolveMode(agent, requested) {
|
|
|
53
55
|
}
|
|
54
56
|
throw new Error(`${agent} does not support '${requested}' mode. Supported modes: ${supported.join(', ')}.`);
|
|
55
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* The mode an agent should run in when the caller has no preference.
|
|
60
|
+
*
|
|
61
|
+
* Returns the first entry in the agent's `capabilities.modes` table — the
|
|
62
|
+
* declaration order is the source of truth for "the safest mode this agent
|
|
63
|
+
* supports." Agents that include `plan` list it first; agents like
|
|
64
|
+
* antigravity that have no read-only mode list `edit` first.
|
|
65
|
+
*
|
|
66
|
+
* Use this when the user did not pass `--mode` explicitly. When the user
|
|
67
|
+
* *did* pass `--mode plan` and the agent doesn't support it, call
|
|
68
|
+
* `resolveMode` instead so the user sees a loud error rather than a silent
|
|
69
|
+
* elevation from read-only to writable.
|
|
70
|
+
*/
|
|
71
|
+
export function defaultModeFor(agent) {
|
|
72
|
+
return AGENTS[agent].capabilities.modes[0];
|
|
73
|
+
}
|
|
56
74
|
/** Pattern for valid environment variable names (C identifier rules). */
|
|
57
75
|
const EXEC_ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
58
76
|
/** Parse a single KEY=VALUE string into a tuple, validating the key name. */
|
|
@@ -167,9 +185,9 @@ export const AGENT_COMMANDS = {
|
|
|
167
185
|
// "workspace-write but no auto-approval" — closer to plan-as-restraint.
|
|
168
186
|
// True read-only requires --sandbox read-only which we haven't wired.
|
|
169
187
|
plan: ['--sandbox', 'workspace-write'],
|
|
170
|
-
edit: ['--sandbox', 'workspace-write', '--
|
|
171
|
-
// skip drops the sandbox entirely; --
|
|
172
|
-
skip: ['--
|
|
188
|
+
edit: ['--sandbox', 'workspace-write', '--dangerously-bypass-approvals-and-sandbox'],
|
|
189
|
+
// skip drops the sandbox entirely; --dangerously-bypass-approvals-and-sandbox then approves anything.
|
|
190
|
+
skip: ['--dangerously-bypass-approvals-and-sandbox'],
|
|
173
191
|
},
|
|
174
192
|
jsonFlags: ['--json'],
|
|
175
193
|
modelFlag: '--model',
|
|
@@ -303,6 +321,17 @@ export const AGENT_COMMANDS = {
|
|
|
303
321
|
jsonFlags: ['--output-format', 'streaming-json'],
|
|
304
322
|
modelFlag: '--model',
|
|
305
323
|
},
|
|
324
|
+
kimi: {
|
|
325
|
+
base: ['kimi-code'],
|
|
326
|
+
promptFlag: '-p',
|
|
327
|
+
modeFlags: {
|
|
328
|
+
plan: [],
|
|
329
|
+
edit: [],
|
|
330
|
+
skip: [],
|
|
331
|
+
},
|
|
332
|
+
jsonFlags: [],
|
|
333
|
+
modelFlag: '--model',
|
|
334
|
+
},
|
|
306
335
|
};
|
|
307
336
|
/** Assemble the full CLI argument array for an agent invocation. */
|
|
308
337
|
export function buildExecCommand(options) {
|
|
@@ -314,9 +343,14 @@ export function buildExecCommand(options) {
|
|
|
314
343
|
if (options.agent === 'codex' && interactive && cmd[1] === 'exec') {
|
|
315
344
|
cmd.splice(1, 1);
|
|
316
345
|
}
|
|
317
|
-
// Use versioned alias if a specific version was requested (e.g., claude@2.1.98)
|
|
346
|
+
// Use versioned alias if a specific version was requested (e.g., claude@2.1.98).
|
|
347
|
+
// Resolve to the absolute path of the shim so spawn doesn't depend on PATH —
|
|
348
|
+
// on Linux installs where the shims dir isn't on PATH, spawning the bare
|
|
349
|
+
// versioned name fails with ENOENT even though `agents view` shows the agent.
|
|
318
350
|
if (options.version && cmd.length > 0) {
|
|
319
|
-
|
|
351
|
+
const versionedName = `${cmd[0]}@${options.version}`;
|
|
352
|
+
const absPath = path.join(getShimsDir(), versionedName);
|
|
353
|
+
cmd[0] = fs.existsSync(absPath) ? absPath : versionedName;
|
|
320
354
|
}
|
|
321
355
|
// Add reasoning effort flags (before mode flags for codex -c positioning)
|
|
322
356
|
// For codex, -c must come before 'exec' subcommand, so we insert at position 1
|
package/dist/lib/fuzzy.d.ts
CHANGED
|
@@ -4,11 +4,20 @@
|
|
|
4
4
|
*/
|
|
5
5
|
/** Levenshtein edit distance between two strings. */
|
|
6
6
|
export declare function levenshtein(a: string, b: string): number;
|
|
7
|
+
/**
|
|
8
|
+
* Damerau-Levenshtein (optimal string alignment) distance.
|
|
9
|
+
* Counts a transposition of two adjacent characters as a single edit,
|
|
10
|
+
* so `cladue` -> `claude` is distance 1, matching the user's notion of
|
|
11
|
+
* "one misspelling."
|
|
12
|
+
*/
|
|
13
|
+
export declare function damerauLevenshtein(a: string, b: string): number;
|
|
7
14
|
export interface FuzzyOptions {
|
|
8
15
|
/** Absolute max edit distance allowed. */
|
|
9
16
|
maxDistance?: number;
|
|
10
17
|
/** Max ratio of distance to input length. If set, effective threshold = min(maxDistance, floor(len * maxRatio)). */
|
|
11
18
|
maxRatio?: number;
|
|
19
|
+
/** Use Damerau-Levenshtein (transposition = 1 edit) instead of plain Levenshtein. */
|
|
20
|
+
damerau?: boolean;
|
|
12
21
|
}
|
|
13
22
|
/**
|
|
14
23
|
* Fuzzy match an input string against a list of candidates.
|
|
@@ -20,9 +29,10 @@ export declare function fuzzyMatch<T extends string>(input: string, candidates:
|
|
|
20
29
|
* Based on pairwise distance analysis of candidate pools.
|
|
21
30
|
*/
|
|
22
31
|
export declare const FUZZY_PRESETS: {
|
|
23
|
-
/** Agents:
|
|
32
|
+
/** Agents: 1 mistype (insertion/deletion/substitution/transposition). Damerau so `cladue`->`claude` is 1. */
|
|
24
33
|
readonly agents: {
|
|
25
|
-
readonly maxDistance:
|
|
34
|
+
readonly maxDistance: 1;
|
|
35
|
+
readonly damerau: true;
|
|
26
36
|
};
|
|
27
37
|
/** Modes: plan/edit/full all at dist=4, lenient */
|
|
28
38
|
readonly modes: {
|
package/dist/lib/fuzzy.js
CHANGED
|
@@ -19,12 +19,36 @@ export function levenshtein(a, b) {
|
|
|
19
19
|
}
|
|
20
20
|
return dp[m][n];
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Damerau-Levenshtein (optimal string alignment) distance.
|
|
24
|
+
* Counts a transposition of two adjacent characters as a single edit,
|
|
25
|
+
* so `cladue` -> `claude` is distance 1, matching the user's notion of
|
|
26
|
+
* "one misspelling."
|
|
27
|
+
*/
|
|
28
|
+
export function damerauLevenshtein(a, b) {
|
|
29
|
+
const m = a.length, n = b.length;
|
|
30
|
+
if (m === 0)
|
|
31
|
+
return n;
|
|
32
|
+
if (n === 0)
|
|
33
|
+
return m;
|
|
34
|
+
const dp = Array.from({ length: m + 1 }, (_, i) => Array.from({ length: n + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0)));
|
|
35
|
+
for (let i = 1; i <= m; i++) {
|
|
36
|
+
for (let j = 1; j <= n; j++) {
|
|
37
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
38
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
39
|
+
if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {
|
|
40
|
+
dp[i][j] = Math.min(dp[i][j], dp[i - 2][j - 2] + 1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return dp[m][n];
|
|
45
|
+
}
|
|
22
46
|
/**
|
|
23
47
|
* Fuzzy match an input string against a list of candidates.
|
|
24
48
|
* Returns the single best match within tolerance, or null if no match or ambiguous.
|
|
25
49
|
*/
|
|
26
50
|
export function fuzzyMatch(input, candidates, options = {}) {
|
|
27
|
-
const { maxDistance = 2, maxRatio } = options;
|
|
51
|
+
const { maxDistance = 2, maxRatio, damerau = false } = options;
|
|
28
52
|
const lower = input.toLowerCase();
|
|
29
53
|
// Reject inputs that are too short - they're too ambiguous
|
|
30
54
|
if (lower.length < 3)
|
|
@@ -33,10 +57,11 @@ export function fuzzyMatch(input, candidates, options = {}) {
|
|
|
33
57
|
const threshold = maxRatio
|
|
34
58
|
? Math.min(maxDistance, Math.floor(lower.length * maxRatio))
|
|
35
59
|
: maxDistance;
|
|
60
|
+
const distance = damerau ? damerauLevenshtein : levenshtein;
|
|
36
61
|
// Find all candidates within threshold (excluding exact matches)
|
|
37
62
|
const matches = [];
|
|
38
63
|
for (const candidate of candidates) {
|
|
39
|
-
const dist =
|
|
64
|
+
const dist = distance(lower, candidate.toLowerCase());
|
|
40
65
|
if (dist > 0 && dist <= threshold) {
|
|
41
66
|
matches.push({ candidate, dist });
|
|
42
67
|
}
|
|
@@ -55,8 +80,8 @@ export function fuzzyMatch(input, candidates, options = {}) {
|
|
|
55
80
|
* Based on pairwise distance analysis of candidate pools.
|
|
56
81
|
*/
|
|
57
82
|
export const FUZZY_PRESETS = {
|
|
58
|
-
/** Agents:
|
|
59
|
-
agents: { maxDistance:
|
|
83
|
+
/** Agents: 1 mistype (insertion/deletion/substitution/transposition). Damerau so `cladue`->`claude` is 1. */
|
|
84
|
+
agents: { maxDistance: 1, damerau: true },
|
|
60
85
|
/** Modes: plan/edit/full all at dist=4, lenient */
|
|
61
86
|
modes: { maxDistance: 2 },
|
|
62
87
|
/** Efforts: high/xhigh at dist=1, must be strict */
|
package/dist/lib/git.js
CHANGED
|
@@ -32,7 +32,14 @@ function installGithooksSymlinks(repoDir) {
|
|
|
32
32
|
if (fs.lstatSync(dest, { throwIfNoEntry: false })) {
|
|
33
33
|
fs.rmSync(dest);
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
try {
|
|
36
|
+
fs.symlinkSync(target, dest);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
// Windows requires Developer Mode or elevated privileges for symlinks; skip gracefully.
|
|
40
|
+
if (err.code !== 'EPERM')
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
/**
|
package/dist/lib/hooks.d.ts
CHANGED
|
@@ -110,12 +110,12 @@ export declare function installHooksCentrally(source: string): Promise<{
|
|
|
110
110
|
errors: string[];
|
|
111
111
|
}>;
|
|
112
112
|
/**
|
|
113
|
-
* List hooks from user (~/.agents/hooks/) and system (~/.agents
|
|
113
|
+
* List hooks from user (~/.agents/hooks/) and system (~/.agents/.system/hooks/) dirs.
|
|
114
114
|
* User dir takes priority; deduplication preserves first occurrence.
|
|
115
115
|
*/
|
|
116
116
|
export declare function listCentralHooks(): HookEntry[];
|
|
117
117
|
/**
|
|
118
|
-
* Parse hook manifests. Reads system hooks from ~/.agents
|
|
118
|
+
* Parse hook manifests. Reads system hooks from ~/.agents/.system/hooks.yaml
|
|
119
119
|
* (npm-shipped defaults) and user hooks from the `hooks:` section of
|
|
120
120
|
* ~/.agents/agents.yaml. Merges with user-wins-on-key-collision precedence.
|
|
121
121
|
* A user entry with `enabled: false` disables the system-shipped hook of
|