@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/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. */
|
|
@@ -100,6 +118,7 @@ export function buildExecEnv(options) {
|
|
|
100
118
|
}
|
|
101
119
|
delete result.CODEX_HOME;
|
|
102
120
|
delete result.COPILOT_HOME;
|
|
121
|
+
delete result.KIMI_CODE_HOME;
|
|
103
122
|
}
|
|
104
123
|
else if (options.agent === 'codex') {
|
|
105
124
|
const cwd = options.cwd || process.cwd();
|
|
@@ -112,6 +131,7 @@ export function buildExecEnv(options) {
|
|
|
112
131
|
}
|
|
113
132
|
delete result.CLAUDE_CONFIG_DIR;
|
|
114
133
|
delete result.COPILOT_HOME;
|
|
134
|
+
delete result.KIMI_CODE_HOME;
|
|
115
135
|
}
|
|
116
136
|
else if (options.agent === 'copilot') {
|
|
117
137
|
// Copilot honors COPILOT_HOME (relocates ~/.copilot, including settings,
|
|
@@ -127,11 +147,28 @@ export function buildExecEnv(options) {
|
|
|
127
147
|
}
|
|
128
148
|
delete result.CLAUDE_CONFIG_DIR;
|
|
129
149
|
delete result.CODEX_HOME;
|
|
150
|
+
delete result.KIMI_CODE_HOME;
|
|
151
|
+
}
|
|
152
|
+
else if (options.agent === 'kimi') {
|
|
153
|
+
// Kimi honors KIMI_CODE_HOME (relocates ~/.kimi-code, including config,
|
|
154
|
+
// skills, hooks, sessions). Pin it at the per-version home.
|
|
155
|
+
const cwd = options.cwd || process.cwd();
|
|
156
|
+
const resolvedVersion = options.version ?? resolveVersion('kimi', cwd);
|
|
157
|
+
const version = options.version
|
|
158
|
+
? resolvedVersion
|
|
159
|
+
: (resolvedVersion && isVersionInstalled('kimi', resolvedVersion) ? resolvedVersion : null);
|
|
160
|
+
if (version) {
|
|
161
|
+
result.KIMI_CODE_HOME = path.join(getVersionHomePath('kimi', version), '.kimi-code');
|
|
162
|
+
}
|
|
163
|
+
delete result.CLAUDE_CONFIG_DIR;
|
|
164
|
+
delete result.CODEX_HOME;
|
|
165
|
+
delete result.COPILOT_HOME;
|
|
130
166
|
}
|
|
131
167
|
else {
|
|
132
168
|
delete result.CLAUDE_CONFIG_DIR;
|
|
133
169
|
delete result.CODEX_HOME;
|
|
134
170
|
delete result.COPILOT_HOME;
|
|
171
|
+
delete result.KIMI_CODE_HOME;
|
|
135
172
|
}
|
|
136
173
|
return {
|
|
137
174
|
...result,
|
|
@@ -167,9 +204,9 @@ export const AGENT_COMMANDS = {
|
|
|
167
204
|
// "workspace-write but no auto-approval" — closer to plan-as-restraint.
|
|
168
205
|
// True read-only requires --sandbox read-only which we haven't wired.
|
|
169
206
|
plan: ['--sandbox', 'workspace-write'],
|
|
170
|
-
edit: ['--sandbox', 'workspace-write', '--
|
|
171
|
-
// skip drops the sandbox entirely; --
|
|
172
|
-
skip: ['--
|
|
207
|
+
edit: ['--sandbox', 'workspace-write', '--dangerously-bypass-approvals-and-sandbox'],
|
|
208
|
+
// skip drops the sandbox entirely; --dangerously-bypass-approvals-and-sandbox then approves anything.
|
|
209
|
+
skip: ['--dangerously-bypass-approvals-and-sandbox'],
|
|
173
210
|
},
|
|
174
211
|
jsonFlags: ['--json'],
|
|
175
212
|
modelFlag: '--model',
|
|
@@ -303,6 +340,18 @@ export const AGENT_COMMANDS = {
|
|
|
303
340
|
jsonFlags: ['--output-format', 'streaming-json'],
|
|
304
341
|
modelFlag: '--model',
|
|
305
342
|
},
|
|
343
|
+
kimi: {
|
|
344
|
+
base: ['kimi'],
|
|
345
|
+
promptFlag: '-p',
|
|
346
|
+
modeFlags: {
|
|
347
|
+
plan: ['--plan'],
|
|
348
|
+
edit: [],
|
|
349
|
+
auto: ['--auto'],
|
|
350
|
+
skip: ['--yolo'],
|
|
351
|
+
},
|
|
352
|
+
jsonFlags: ['--output-format', 'stream-json'],
|
|
353
|
+
modelFlag: '--model',
|
|
354
|
+
},
|
|
306
355
|
};
|
|
307
356
|
/** Assemble the full CLI argument array for an agent invocation. */
|
|
308
357
|
export function buildExecCommand(options) {
|
|
@@ -314,9 +363,14 @@ export function buildExecCommand(options) {
|
|
|
314
363
|
if (options.agent === 'codex' && interactive && cmd[1] === 'exec') {
|
|
315
364
|
cmd.splice(1, 1);
|
|
316
365
|
}
|
|
317
|
-
// Use versioned alias if a specific version was requested (e.g., claude@2.1.98)
|
|
366
|
+
// Use versioned alias if a specific version was requested (e.g., claude@2.1.98).
|
|
367
|
+
// Resolve to the absolute path of the shim so spawn doesn't depend on PATH —
|
|
368
|
+
// on Linux installs where the shims dir isn't on PATH, spawning the bare
|
|
369
|
+
// versioned name fails with ENOENT even though `agents view` shows the agent.
|
|
318
370
|
if (options.version && cmd.length > 0) {
|
|
319
|
-
|
|
371
|
+
const versionedName = `${cmd[0]}@${options.version}`;
|
|
372
|
+
const absPath = path.join(getShimsDir(), versionedName);
|
|
373
|
+
cmd[0] = fs.existsSync(absPath) ? absPath : versionedName;
|
|
320
374
|
}
|
|
321
375
|
// Add reasoning effort flags (before mode flags for codex -c positioning)
|
|
322
376
|
// 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
|
package/dist/lib/hooks.js
CHANGED
|
@@ -11,8 +11,8 @@ import * as fs from 'fs';
|
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import * as yaml from 'yaml';
|
|
13
13
|
import * as TOML from 'smol-toml';
|
|
14
|
-
import { AGENTS,
|
|
15
|
-
import { supports, explainSkip } from './capabilities.js';
|
|
14
|
+
import { AGENTS, agentConfigDirName } from './agents.js';
|
|
15
|
+
import { supports, explainSkip, capableAgents } from './capabilities.js';
|
|
16
16
|
import { setGeminiAutoUpdateDisabled, updateGeminiSettings } from './gemini-settings.js';
|
|
17
17
|
import { getHooksDir as getSystemHooksDir, getUserHooksDir, getUserAgentsDir, getSystemAgentsDir, getProjectAgentsDir, getTrashHooksDir, getEnabledExtraRepos } from './state.js';
|
|
18
18
|
function getCentralHooksDir() { return getUserHooksDir(); }
|
|
@@ -117,7 +117,7 @@ function isExecutable(mode) {
|
|
|
117
117
|
function getHooksDir(agentId) {
|
|
118
118
|
const agent = AGENTS[agentId];
|
|
119
119
|
const home = getEffectiveHome(agentId);
|
|
120
|
-
return path.join(home,
|
|
120
|
+
return path.join(home, agentConfigDirName(agentId), agent.hooksDir);
|
|
121
121
|
}
|
|
122
122
|
function getProjectHooksDirs(agentId, cwd) {
|
|
123
123
|
const agent = AGENTS[agentId];
|
|
@@ -324,7 +324,7 @@ export function listInstalledHooksWithScope(agentId, cwd = process.cwd(), option
|
|
|
324
324
|
}
|
|
325
325
|
// User-scoped hooks (version-aware when home is provided)
|
|
326
326
|
const home = options?.home || getEffectiveHome(agentId);
|
|
327
|
-
const userDir = path.join(home,
|
|
327
|
+
const userDir = path.join(home, agentConfigDirName(agentId), agent.hooksDir);
|
|
328
328
|
const userHooks = listHookEntriesFromDir(userDir);
|
|
329
329
|
for (const hook of userHooks) {
|
|
330
330
|
addHook(hook, 'user', agentId);
|
|
@@ -363,7 +363,7 @@ export async function installHooks(source, agents, options = {}) {
|
|
|
363
363
|
*/
|
|
364
364
|
export function getVersionHooksDir(agent, version) {
|
|
365
365
|
const home = getVersionHomePath(agent, version);
|
|
366
|
-
return path.join(home,
|
|
366
|
+
return path.join(home, agentConfigDirName(agent), AGENTS[agent].hooksDir);
|
|
367
367
|
}
|
|
368
368
|
/**
|
|
369
369
|
* List hook entries in a specific version home.
|
|
@@ -489,7 +489,7 @@ export function removeHookFromVersion(agent, version, hookName) {
|
|
|
489
489
|
*/
|
|
490
490
|
export function iterHooksCapableVersions(filter) {
|
|
491
491
|
const pairs = [];
|
|
492
|
-
const hookAgents =
|
|
492
|
+
const hookAgents = capableAgents('hooks');
|
|
493
493
|
const agents = filter?.agent ? [filter.agent] : hookAgents;
|
|
494
494
|
for (const agent of agents) {
|
|
495
495
|
if (!hookAgents.includes(agent))
|
|
@@ -592,7 +592,7 @@ export async function installHooksCentrally(source) {
|
|
|
592
592
|
return { installed, errors };
|
|
593
593
|
}
|
|
594
594
|
/**
|
|
595
|
-
* List hooks from user (~/.agents/hooks/) and system (~/.agents
|
|
595
|
+
* List hooks from user (~/.agents/hooks/) and system (~/.agents/.system/hooks/) dirs.
|
|
596
596
|
* User dir takes priority; deduplication preserves first occurrence.
|
|
597
597
|
*/
|
|
598
598
|
export function listCentralHooks() {
|
|
@@ -609,7 +609,7 @@ export function listCentralHooks() {
|
|
|
609
609
|
return results;
|
|
610
610
|
}
|
|
611
611
|
/**
|
|
612
|
-
* Parse hook manifests. Reads system hooks from ~/.agents
|
|
612
|
+
* Parse hook manifests. Reads system hooks from ~/.agents/.system/hooks.yaml
|
|
613
613
|
* (npm-shipped defaults) and user hooks from the `hooks:` section of
|
|
614
614
|
* ~/.agents/agents.yaml. Merges with user-wins-on-key-collision precedence.
|
|
615
615
|
* A user entry with `enabled: false` disables the system-shipped hook of
|
|
@@ -663,7 +663,7 @@ const CODEX_MATCHER_EVENTS = new Set(['PreToolUse', 'PostToolUse', 'SessionStart
|
|
|
663
663
|
* Register hooks as lifecycle events in an agent's config.
|
|
664
664
|
* Reads hooks.yaml manifest, merges into the agent's config file(s).
|
|
665
665
|
* Only manages hooks whose command paths are under ~/.agents/hooks/ or
|
|
666
|
-
* ~/.agents
|
|
666
|
+
* ~/.agents/.system/hooks/. Does not remove user-added hooks.
|
|
667
667
|
*
|
|
668
668
|
* @param agentsDirOverride - When provided, treats this single dir as the
|
|
669
669
|
* only managed hook root. Used by tests to inject a temp path. In normal
|
|
@@ -702,7 +702,7 @@ export function registerHooksToSettings(agentId, versionHome, hookManifest, agen
|
|
|
702
702
|
// Scripts are copied into the version home during sync — prefer that stable
|
|
703
703
|
// local path so registered commands don't break when source dirs change.
|
|
704
704
|
const localHooksDir = !overrideRoots
|
|
705
|
-
? path.join(versionHome,
|
|
705
|
+
? path.join(versionHome, agentConfigDirName(agentId), AGENTS[agentId].hooksDir)
|
|
706
706
|
: null;
|
|
707
707
|
const resolveScript = (script) => {
|
|
708
708
|
if (overrideRoots) {
|
|
@@ -740,6 +740,9 @@ export function registerHooksToSettings(agentId, versionHome, hookManifest, agen
|
|
|
740
740
|
if (agentId === 'grok') {
|
|
741
741
|
return registerHooksForGrok(versionHome, manifest, resolveScript, managedPrefixes);
|
|
742
742
|
}
|
|
743
|
+
if (agentId === 'kimi') {
|
|
744
|
+
return registerHooksForKimi(versionHome, manifest, resolveScript, managedPrefixes);
|
|
745
|
+
}
|
|
743
746
|
return { registered: [], errors: [] };
|
|
744
747
|
}
|
|
745
748
|
/**
|
|
@@ -1217,3 +1220,87 @@ function registerHooksForGrok(versionHome, manifest, resolveScript, managedPrefi
|
|
|
1217
1220
|
}
|
|
1218
1221
|
return { registered, errors };
|
|
1219
1222
|
}
|
|
1223
|
+
function registerHooksForKimi(versionHome, manifest, resolveScript, managedPrefixes) {
|
|
1224
|
+
const registered = [];
|
|
1225
|
+
const errors = [];
|
|
1226
|
+
const configPath = path.join(versionHome, '.kimi-code', 'config.toml');
|
|
1227
|
+
// Read existing config.toml
|
|
1228
|
+
let config = {};
|
|
1229
|
+
if (fs.existsSync(configPath)) {
|
|
1230
|
+
try {
|
|
1231
|
+
config = TOML.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
1232
|
+
}
|
|
1233
|
+
catch {
|
|
1234
|
+
errors.push('Failed to parse config.toml');
|
|
1235
|
+
return { registered, errors };
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
// Build set of current manifest command paths for GC
|
|
1239
|
+
const currentManifestPaths = new Set();
|
|
1240
|
+
for (const [hookName, hookDef] of Object.entries(manifest)) {
|
|
1241
|
+
if (!hookDef.events || hookDef.events.length === 0)
|
|
1242
|
+
continue;
|
|
1243
|
+
const resolved = resolveHookCommand(hookName, hookDef, resolveScript);
|
|
1244
|
+
if (resolved)
|
|
1245
|
+
currentManifestPaths.add(resolved);
|
|
1246
|
+
}
|
|
1247
|
+
// Remove stale managed hooks from existing hooks array
|
|
1248
|
+
let hooksArray = [];
|
|
1249
|
+
if (Array.isArray(config.hooks)) {
|
|
1250
|
+
hooksArray = config.hooks;
|
|
1251
|
+
}
|
|
1252
|
+
const filteredHooks = hooksArray.filter((h) => {
|
|
1253
|
+
const cmd = typeof h.command === 'string' ? h.command : '';
|
|
1254
|
+
if (!cmd)
|
|
1255
|
+
return true;
|
|
1256
|
+
const isManaged = managedPrefixes.some((p) => cmd.startsWith(p));
|
|
1257
|
+
if (!isManaged)
|
|
1258
|
+
return true;
|
|
1259
|
+
return currentManifestPaths.has(cmd);
|
|
1260
|
+
});
|
|
1261
|
+
// Add/update hooks from manifest
|
|
1262
|
+
for (const [name, hookDef] of Object.entries(manifest)) {
|
|
1263
|
+
if (!hookDef.events || hookDef.events.length === 0)
|
|
1264
|
+
continue;
|
|
1265
|
+
const commandPath = resolveHookCommand(name, hookDef, resolveScript);
|
|
1266
|
+
if (!commandPath) {
|
|
1267
|
+
errors.push(`${name}: script not found in user or system hooks dir`);
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
const timeout = hookDef.timeout ?? 30;
|
|
1271
|
+
for (const event of hookDef.events) {
|
|
1272
|
+
const matcher = hookDef.matcher;
|
|
1273
|
+
// Find existing hook with same event, command, and matcher
|
|
1274
|
+
const existingIdx = filteredHooks.findIndex((h) => {
|
|
1275
|
+
const sameEvent = h.event === event;
|
|
1276
|
+
const sameCmd = h.command === commandPath;
|
|
1277
|
+
const sameMatcher = (h.matcher ?? '') === (matcher ?? '');
|
|
1278
|
+
return sameEvent && sameCmd && sameMatcher;
|
|
1279
|
+
});
|
|
1280
|
+
const hookEntry = {
|
|
1281
|
+
event,
|
|
1282
|
+
command: commandPath,
|
|
1283
|
+
timeout,
|
|
1284
|
+
};
|
|
1285
|
+
if (matcher) {
|
|
1286
|
+
hookEntry.matcher = matcher;
|
|
1287
|
+
}
|
|
1288
|
+
if (existingIdx >= 0) {
|
|
1289
|
+
filteredHooks[existingIdx] = hookEntry;
|
|
1290
|
+
}
|
|
1291
|
+
else {
|
|
1292
|
+
filteredHooks.push(hookEntry);
|
|
1293
|
+
}
|
|
1294
|
+
registered.push(`${name} -> ${event}`);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
config.hooks = filteredHooks;
|
|
1298
|
+
try {
|
|
1299
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
1300
|
+
fs.writeFileSync(configPath, TOML.stringify(config), 'utf-8');
|
|
1301
|
+
}
|
|
1302
|
+
catch (err) {
|
|
1303
|
+
errors.push(`Failed to write config.toml: ${err.message}`);
|
|
1304
|
+
}
|
|
1305
|
+
return { registered, errors };
|
|
1306
|
+
}
|
package/dist/lib/mcp.js
CHANGED
|
@@ -13,7 +13,8 @@ import * as yaml from 'yaml';
|
|
|
13
13
|
import { execFileSync } from 'child_process';
|
|
14
14
|
import { getMcpDir, getUserMcpDir, getProjectAgentsDir, getVersionsDir } from './state.js';
|
|
15
15
|
import { getBinaryPath, getVersionHomePath } from './versions.js';
|
|
16
|
-
import {
|
|
16
|
+
import { AGENTS } from './agents.js';
|
|
17
|
+
import { isCapable } from './capabilities.js';
|
|
17
18
|
import { setGeminiAutoUpdateDisabled, updateGeminiSettings } from './gemini-settings.js';
|
|
18
19
|
/**
|
|
19
20
|
* Parse an MCP server config from a YAML file.
|
|
@@ -312,6 +313,31 @@ function installMcpToCursorConfig(server, versionHome) {
|
|
|
312
313
|
/**
|
|
313
314
|
* Install MCP server to OpenCode config file.
|
|
314
315
|
*/
|
|
316
|
+
function installMcpToKimiConfig(server, versionHome) {
|
|
317
|
+
const configPath = path.join(versionHome, '.kimi-code', 'mcp.json');
|
|
318
|
+
let config = {};
|
|
319
|
+
if (fs.existsSync(configPath)) {
|
|
320
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
321
|
+
}
|
|
322
|
+
if (!config.mcpServers || typeof config.mcpServers !== 'object') {
|
|
323
|
+
config.mcpServers = {};
|
|
324
|
+
}
|
|
325
|
+
const mcpServers = config.mcpServers;
|
|
326
|
+
if (server.config.transport === 'stdio') {
|
|
327
|
+
mcpServers[server.name] = {
|
|
328
|
+
command: server.config.command,
|
|
329
|
+
args: server.config.args || [],
|
|
330
|
+
env: server.config.env || {},
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
mcpServers[server.name] = {
|
|
335
|
+
url: server.config.url,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
339
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
340
|
+
}
|
|
315
341
|
function installMcpToOpenCodeConfig(server, versionHome) {
|
|
316
342
|
const configPath = path.join(versionHome, '.opencode', 'opencode.jsonc');
|
|
317
343
|
let config = {};
|
|
@@ -354,7 +380,7 @@ function installMcpToOpenCodeConfig(server, versionHome) {
|
|
|
354
380
|
* For others: edits config files directly
|
|
355
381
|
*/
|
|
356
382
|
export function installMcpServers(agentId, version, versionHome, mcpNames, options = {}) {
|
|
357
|
-
if (!
|
|
383
|
+
if (!isCapable(agentId, 'mcp')) {
|
|
358
384
|
return { success: true, applied: [], errors: [] };
|
|
359
385
|
}
|
|
360
386
|
const servers = getMcpServersByName(mcpNames, { cwd: options.cwd });
|
|
@@ -394,6 +420,10 @@ export function installMcpServers(agentId, version, versionHome, mcpNames, optio
|
|
|
394
420
|
// For now the general sync + toml editing via agents mcp works via the path helpers.
|
|
395
421
|
applied.push(server.name);
|
|
396
422
|
}
|
|
423
|
+
else if (agentId === 'kimi') {
|
|
424
|
+
installMcpToKimiConfig(server, versionHome);
|
|
425
|
+
applied.push(server.name);
|
|
426
|
+
}
|
|
397
427
|
}
|
|
398
428
|
catch (err) {
|
|
399
429
|
const message = err.message;
|
package/dist/lib/migrate.d.ts
CHANGED
|
@@ -4,5 +4,56 @@
|
|
|
4
4
|
* Called from postinstall and as a command-time fallback from agents view/use/pull.
|
|
5
5
|
* Each migration is guarded by an existence check so re-running is safe.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Fold ~/.agents-system/ into ~/.agents/.system/.
|
|
9
|
+
*
|
|
10
|
+
* MUST run first in runMigration() — every other migrator reads SYSTEM_DIR
|
|
11
|
+
* (the new path), so the contents have to be there before they execute.
|
|
12
|
+
*
|
|
13
|
+
* Strategy:
|
|
14
|
+
* 1. If legacy dir doesn't exist or is already a symlink, no-op.
|
|
15
|
+
* 2. If new path doesn't exist yet, rename in one shot (fast path).
|
|
16
|
+
* 3. If both exist (mid-migration / re-run on partially-migrated state),
|
|
17
|
+
* merge legacy → new with new winning on collision, then drop legacy.
|
|
18
|
+
*
|
|
19
|
+
* After the contents move, the legacy path becomes a symlink → SYSTEM_DIR
|
|
20
|
+
* so external tooling that still references ~/.agents-system/ keeps
|
|
21
|
+
* resolving correctly. The symlink is harmless on its own and can be
|
|
22
|
+
* removed with `rm ~/.agents-system` once everything has updated.
|
|
23
|
+
*
|
|
24
|
+
* Idempotent: re-running converges to "contents at SYSTEM_DIR, symlink at
|
|
25
|
+
* LEGACY_SYSTEM_DIR" without duplicating data.
|
|
26
|
+
*/
|
|
27
|
+
export declare function foldLegacySystemRepo(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Rename the legacy `extras-extras/` plugin-marketplace dir to `agents-extras/`
|
|
30
|
+
* inside every installed agent version-home, and rewrite cross-references in
|
|
31
|
+
* `known_marketplaces.json` and the agent's `settings.json`.
|
|
32
|
+
*
|
|
33
|
+
* A previous dev build of `agents-cli` named the extras-aliased repo's
|
|
34
|
+
* synthesized marketplace dir `extras-extras` (double "extras" because the alias
|
|
35
|
+
* itself was `extras`). The new naming convention is `agents-<alias>`, so the
|
|
36
|
+
* directory should be `agents-extras`. Without this migration the orphan dir
|
|
37
|
+
* stays on disk and Claude Code loads two parallel marketplaces (the legacy
|
|
38
|
+
* `extras-extras` entry from `known_marketplaces.json` plus the freshly
|
|
39
|
+
* synthesized `agents-extras` from the new code path).
|
|
40
|
+
*
|
|
41
|
+
* Strategy per `<historyDir>/versions/<agent>/<ver>/home/.<agent>/plugins/`:
|
|
42
|
+
* 1. `marketplaces/extras-extras/` → `marketplaces/agents-extras/`
|
|
43
|
+
* (drops `extras-extras/` outright when `agents-extras/` already exists —
|
|
44
|
+
* previous incomplete migration ran)
|
|
45
|
+
* 2. Inside the renamed dir's `.claude-plugin/marketplace.json`, set
|
|
46
|
+
* `"name": "agents-extras"`.
|
|
47
|
+
* 3. In `<configDir>/plugins/known_marketplaces.json`, rename the
|
|
48
|
+
* `extras-extras` key to `agents-extras` and rewrite `source.path` /
|
|
49
|
+
* `installLocation`.
|
|
50
|
+
* 4. In `<configDir>/settings.json`'s `enabledPlugins`, rename every
|
|
51
|
+
* `<plugin>@extras-extras` key to `<plugin>@agents-extras` (preserving
|
|
52
|
+
* its boolean value, skipping if the new key already exists).
|
|
53
|
+
*
|
|
54
|
+
* Idempotent: re-running converges without further writes once everything is on
|
|
55
|
+
* the new name.
|
|
56
|
+
*/
|
|
57
|
+
export declare function migrateExtrasExtrasToAgentsExtras(historyDir?: string): void;
|
|
7
58
|
/** Run all idempotent migrations. Safe to call multiple times. */
|
|
8
59
|
export declare function runMigration(): Promise<void>;
|