@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
|
@@ -15,7 +15,7 @@ import { execFile } from 'child_process';
|
|
|
15
15
|
import { promisify } from 'util';
|
|
16
16
|
import { getAgentsDir, getHistoryDir } from '../state.js';
|
|
17
17
|
const execFileAsync = promisify(execFile);
|
|
18
|
-
import { AGENTS, getCliVersion } from '../agents.js';
|
|
18
|
+
import { AGENTS, agentConfigDirName, getCliVersion } from '../agents.js';
|
|
19
19
|
import { walkForFiles } from '../fs-walk.js';
|
|
20
20
|
import { getConfigSymlinkVersion } from '../shims.js';
|
|
21
21
|
import { SESSION_AGENTS } from './types.js';
|
|
@@ -61,6 +61,7 @@ export async function discoverSessions(options) {
|
|
|
61
61
|
case 'openclaw': return scanOpenClawIncremental();
|
|
62
62
|
case 'rush': return scanRushIncremental(onProgress);
|
|
63
63
|
case 'hermes': return scanHermesIncremental(onProgress);
|
|
64
|
+
case 'kimi': return scanKimiIncremental(onProgress);
|
|
64
65
|
}
|
|
65
66
|
}));
|
|
66
67
|
}
|
|
@@ -201,14 +202,18 @@ export function getAgentSessionDirs(agent, subdir) {
|
|
|
201
202
|
resolved.add(key);
|
|
202
203
|
dirs.push(dir);
|
|
203
204
|
}
|
|
204
|
-
|
|
205
|
+
// Config-dir name relative to home — handles nested layouts (antigravity →
|
|
206
|
+
// .gemini/antigravity-cli) and ~/.config agents (amp, goose) as well as kimi
|
|
207
|
+
// (.kimi-code). Falls back to `.${agent}` for ids not in the registry.
|
|
208
|
+
const configDirName = agent in AGENTS ? agentConfigDirName(agent) : `.${agent}`;
|
|
209
|
+
addDir(path.join(HOME, configDirName, subdir));
|
|
205
210
|
for (const root of VERSIONS_ROOTS) {
|
|
206
211
|
const versionsBase = path.join(root, 'versions', agent);
|
|
207
212
|
if (!fs.existsSync(versionsBase))
|
|
208
213
|
continue;
|
|
209
214
|
try {
|
|
210
215
|
for (const version of fs.readdirSync(versionsBase)) {
|
|
211
|
-
addDir(path.join(versionsBase, version, 'home',
|
|
216
|
+
addDir(path.join(versionsBase, version, 'home', configDirName, subdir));
|
|
212
217
|
}
|
|
213
218
|
}
|
|
214
219
|
catch { /* dir unreadable */ }
|
|
@@ -1574,6 +1579,116 @@ function sumKnownNumbers(values) {
|
|
|
1574
1579
|
// ---------------------------------------------------------------------------
|
|
1575
1580
|
// Time range parsing
|
|
1576
1581
|
// ---------------------------------------------------------------------------
|
|
1582
|
+
// ---------------------------------------------------------------------------
|
|
1583
|
+
// Kimi
|
|
1584
|
+
// ---------------------------------------------------------------------------
|
|
1585
|
+
// Kimi stores sessions under ~/.kimi-code/sessions/<workdir_hash>/session_<uuid>/.
|
|
1586
|
+
// Each session has state.json (metadata) and agents/main/wire.jsonl (conversation).
|
|
1587
|
+
// A session_index.jsonl at ~/.kimi-code/ maps session IDs to directories.
|
|
1588
|
+
/** Incrementally re-scan changed Kimi session state.json files and upsert into the DB. */
|
|
1589
|
+
async function scanKimiIncremental(onProgress) {
|
|
1590
|
+
const filePaths = [];
|
|
1591
|
+
for (const sessionsDir of getAgentSessionDirs('kimi', 'sessions')) {
|
|
1592
|
+
if (!fs.existsSync(sessionsDir))
|
|
1593
|
+
continue;
|
|
1594
|
+
let workDirNames;
|
|
1595
|
+
try {
|
|
1596
|
+
workDirNames = fs.readdirSync(sessionsDir);
|
|
1597
|
+
}
|
|
1598
|
+
catch {
|
|
1599
|
+
continue;
|
|
1600
|
+
}
|
|
1601
|
+
for (const workDirName of workDirNames) {
|
|
1602
|
+
const workDir = path.join(sessionsDir, workDirName);
|
|
1603
|
+
const stat = safeStatSync(workDir);
|
|
1604
|
+
if (!stat?.isDirectory())
|
|
1605
|
+
continue;
|
|
1606
|
+
let sessionNames;
|
|
1607
|
+
try {
|
|
1608
|
+
sessionNames = fs.readdirSync(workDir);
|
|
1609
|
+
}
|
|
1610
|
+
catch {
|
|
1611
|
+
continue;
|
|
1612
|
+
}
|
|
1613
|
+
for (const sessionName of sessionNames) {
|
|
1614
|
+
if (!sessionName.startsWith('session_'))
|
|
1615
|
+
continue;
|
|
1616
|
+
const statePath = path.join(workDir, sessionName, 'state.json');
|
|
1617
|
+
if (!fs.existsSync(statePath))
|
|
1618
|
+
continue;
|
|
1619
|
+
filePaths.push(statePath);
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
const changed = filterChangedFiles(filePaths);
|
|
1624
|
+
if (changed.length === 0)
|
|
1625
|
+
return;
|
|
1626
|
+
onProgress?.({ agent: 'kimi', parsed: 0, total: changed.length });
|
|
1627
|
+
const scanEntries = [];
|
|
1628
|
+
const touched = [];
|
|
1629
|
+
const seen = new Set();
|
|
1630
|
+
let parsed = 0;
|
|
1631
|
+
for (const { filePath, scan } of changed) {
|
|
1632
|
+
try {
|
|
1633
|
+
const result = readKimiMeta(filePath);
|
|
1634
|
+
if (result && !seen.has(result.meta.id)) {
|
|
1635
|
+
seen.add(result.meta.id);
|
|
1636
|
+
scanEntries.push({ meta: result.meta, content: result.content, scan });
|
|
1637
|
+
}
|
|
1638
|
+
else {
|
|
1639
|
+
touched.push({ filePath, scan });
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
catch {
|
|
1643
|
+
touched.push({ filePath, scan });
|
|
1644
|
+
}
|
|
1645
|
+
parsed++;
|
|
1646
|
+
onProgress?.({ agent: 'kimi', parsed, total: changed.length });
|
|
1647
|
+
}
|
|
1648
|
+
upsertSessionsBatch(scanEntries);
|
|
1649
|
+
recordScans(touched);
|
|
1650
|
+
}
|
|
1651
|
+
/** Parse a single Kimi session state.json file to extract session metadata. */
|
|
1652
|
+
function readKimiMeta(filePath) {
|
|
1653
|
+
let state;
|
|
1654
|
+
try {
|
|
1655
|
+
state = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
1656
|
+
}
|
|
1657
|
+
catch {
|
|
1658
|
+
return null;
|
|
1659
|
+
}
|
|
1660
|
+
const sessionDir = path.dirname(filePath);
|
|
1661
|
+
const sessionId = path.basename(sessionDir);
|
|
1662
|
+
if (!sessionId.startsWith('session_'))
|
|
1663
|
+
return null;
|
|
1664
|
+
const title = typeof state.title === 'string' ? state.title : undefined;
|
|
1665
|
+
const lastPrompt = typeof state.lastPrompt === 'string' ? state.lastPrompt : undefined;
|
|
1666
|
+
const topic = title || lastPrompt || undefined;
|
|
1667
|
+
const createdAt = typeof state.createdAt === 'string' ? state.createdAt : undefined;
|
|
1668
|
+
const updatedAt = typeof state.updatedAt === 'string' ? state.updatedAt : undefined;
|
|
1669
|
+
const timestamp = updatedAt || createdAt;
|
|
1670
|
+
const shortId = sessionId.replace(/^session_/, '').slice(0, 8);
|
|
1671
|
+
// Try to infer project from session directory path
|
|
1672
|
+
// ~/.kimi-code/sessions/<workdir_hash>/session_<uuid>/
|
|
1673
|
+
const workDirName = path.basename(path.dirname(sessionDir));
|
|
1674
|
+
let project;
|
|
1675
|
+
if (workDirName.startsWith('wd_')) {
|
|
1676
|
+
const parts = workDirName.slice(3).split('_');
|
|
1677
|
+
if (parts.length >= 2) {
|
|
1678
|
+
project = parts.slice(0, -1).join('/');
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
const meta = {
|
|
1682
|
+
id: sessionId,
|
|
1683
|
+
shortId,
|
|
1684
|
+
agent: 'kimi',
|
|
1685
|
+
timestamp,
|
|
1686
|
+
project,
|
|
1687
|
+
filePath,
|
|
1688
|
+
topic,
|
|
1689
|
+
};
|
|
1690
|
+
return { meta, content: lastPrompt || '' };
|
|
1691
|
+
}
|
|
1577
1692
|
/** Parse a time filter string (relative like '7d' or ISO timestamp) into epoch milliseconds. */
|
|
1578
1693
|
export function parseTimeFilter(input) {
|
|
1579
1694
|
const relativeMatch = input.match(/^(\d+)([mhdw])$/i);
|
|
@@ -105,6 +105,9 @@ export function parseSession(filePath, agent) {
|
|
|
105
105
|
case 'hermes':
|
|
106
106
|
events = parseHermes(filePath);
|
|
107
107
|
break;
|
|
108
|
+
case 'kimi':
|
|
109
|
+
events = [];
|
|
110
|
+
break; // Kimi event parsing not implemented yet — discover.ts builds metadata only
|
|
108
111
|
}
|
|
109
112
|
// Chokepoint: every string field that originated in an untrusted session
|
|
110
113
|
// file gets stripped of terminal escapes here, so renderers downstream can
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* speaks these types.
|
|
8
8
|
*/
|
|
9
9
|
/** Agents that store session data on disk and can be discovered by `agents sessions`. */
|
|
10
|
-
export type SessionAgentId = 'claude' | 'codex' | 'gemini' | 'opencode' | 'openclaw' | 'rush' | 'hermes' | 'grok';
|
|
10
|
+
export type SessionAgentId = 'claude' | 'codex' | 'gemini' | 'opencode' | 'openclaw' | 'rush' | 'hermes' | 'grok' | 'kimi';
|
|
11
11
|
/** All agents with session discovery support, in display order. */
|
|
12
12
|
export declare const SESSION_AGENTS: SessionAgentId[];
|
|
13
13
|
/** A single normalized event within a session (message, tool call, thinking, etc.). */
|
|
@@ -7,4 +7,4 @@
|
|
|
7
7
|
* speaks these types.
|
|
8
8
|
*/
|
|
9
9
|
/** All agents with session discovery support, in display order. */
|
|
10
|
-
export const SESSION_AGENTS = ['claude', 'codex', 'gemini', 'opencode', 'openclaw', 'rush', 'hermes', 'grok'];
|
|
10
|
+
export const SESSION_AGENTS = ['claude', 'codex', 'gemini', 'opencode', 'openclaw', 'rush', 'hermes', 'grok', 'kimi'];
|
package/dist/lib/shims.d.ts
CHANGED
|
@@ -62,8 +62,22 @@ export interface ConflictInfo {
|
|
|
62
62
|
* v15 — remove foreground resource sync / rules refresh from launch shims.
|
|
63
63
|
* Version homes are reconciled by agents-cli management commands; the
|
|
64
64
|
* shim hot path only resolves a version and execs the agent binary.
|
|
65
|
+
* v16 — re-introduce project-scoped compile to the shim hot path via
|
|
66
|
+
* `agents sync --launch`. This stays fast (filesystem-only): compiles
|
|
67
|
+
* project rules, mirrors workspace resources, and synthesizes the
|
|
68
|
+
* scoped plugin marketplaces (agents-cli/agents-system/extras-<alias>/
|
|
69
|
+
* agents-project). Version-home reconciliation stays out of the hot
|
|
70
|
+
* path — management commands still own that.
|
|
71
|
+
* v17 — bash-side skip-fast sentinel under ~/.agents/.cache/launch-sync/.
|
|
72
|
+
* When the sentinel mtime is newer than every source dir, exec the
|
|
73
|
+
* agent binary directly without spawning node. Cuts steady-state
|
|
74
|
+
* hot-path latency from ~680ms (node startup + module init) to ~11ms
|
|
75
|
+
* (a few stat calls). Node writes the sentinel after each successful
|
|
76
|
+
* sync. Documented limitation: POSIX dir mtime only updates on
|
|
77
|
+
* top-level entry add/remove — deep edits to plugin contents won't
|
|
78
|
+
* trigger auto-resync, run `agents sync` for that.
|
|
65
79
|
*/
|
|
66
|
-
export declare const SHIM_SCHEMA_VERSION =
|
|
80
|
+
export declare const SHIM_SCHEMA_VERSION = 17;
|
|
67
81
|
/**
|
|
68
82
|
* Generate the full bash shim script for the given agent. The returned string
|
|
69
83
|
* is written to ~/.agents/shims/{cliCommand} and made executable.
|
|
@@ -223,14 +237,9 @@ export declare function getPathShadowingExecutable(agent: AgentId): string | nul
|
|
|
223
237
|
export declare function removeLegacyUserShim(agent: AgentId, overrides?: {
|
|
224
238
|
homeDir?: string;
|
|
225
239
|
}): boolean;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
* Shell aliases live in the user's session and aren't visible from a Node.js
|
|
230
|
-
* child process. We do a best-effort scan of common RC files for `alias
|
|
231
|
-
* <command>=` patterns. Returns false when detection is inconclusive.
|
|
232
|
-
*/
|
|
233
|
-
export declare function hasAliasShadowingShim(agent: AgentId): boolean;
|
|
240
|
+
export declare function hasAliasShadowingShim(agent: AgentId, overrides?: {
|
|
241
|
+
homeDir?: string;
|
|
242
|
+
}): boolean;
|
|
234
243
|
/**
|
|
235
244
|
* Check if shims directory is in PATH.
|
|
236
245
|
*/
|
package/dist/lib/shims.js
CHANGED
|
@@ -186,8 +186,22 @@ async function promptConflictStrategy(conflictInfos) {
|
|
|
186
186
|
* v15 — remove foreground resource sync / rules refresh from launch shims.
|
|
187
187
|
* Version homes are reconciled by agents-cli management commands; the
|
|
188
188
|
* shim hot path only resolves a version and execs the agent binary.
|
|
189
|
+
* v16 — re-introduce project-scoped compile to the shim hot path via
|
|
190
|
+
* `agents sync --launch`. This stays fast (filesystem-only): compiles
|
|
191
|
+
* project rules, mirrors workspace resources, and synthesizes the
|
|
192
|
+
* scoped plugin marketplaces (agents-cli/agents-system/extras-<alias>/
|
|
193
|
+
* agents-project). Version-home reconciliation stays out of the hot
|
|
194
|
+
* path — management commands still own that.
|
|
195
|
+
* v17 — bash-side skip-fast sentinel under ~/.agents/.cache/launch-sync/.
|
|
196
|
+
* When the sentinel mtime is newer than every source dir, exec the
|
|
197
|
+
* agent binary directly without spawning node. Cuts steady-state
|
|
198
|
+
* hot-path latency from ~680ms (node startup + module init) to ~11ms
|
|
199
|
+
* (a few stat calls). Node writes the sentinel after each successful
|
|
200
|
+
* sync. Documented limitation: POSIX dir mtime only updates on
|
|
201
|
+
* top-level entry add/remove — deep edits to plugin contents won't
|
|
202
|
+
* trigger auto-resync, run `agents sync` for that.
|
|
189
203
|
*/
|
|
190
|
-
export const SHIM_SCHEMA_VERSION =
|
|
204
|
+
export const SHIM_SCHEMA_VERSION = 17;
|
|
191
205
|
/** Internal marker string used to embed the schema version in shim scripts. */
|
|
192
206
|
const SHIM_VERSION_MARKER = 'agents-shim-version:';
|
|
193
207
|
function shellQuote(value) {
|
|
@@ -244,14 +258,19 @@ export COPILOT_HOME="$VERSION_DIR/home/${configDirName}"
|
|
|
244
258
|
# This gives agents-cli full versioned isolation + resource sync for grok.
|
|
245
259
|
export GROK_HOME="$VERSION_DIR/home/.grok"
|
|
246
260
|
`
|
|
247
|
-
: ''
|
|
261
|
+
: agent === 'kimi'
|
|
262
|
+
? `
|
|
263
|
+
# Kimi Code CLI honors KIMI_CODE_HOME to relocate ~/.kimi-code (config.toml,
|
|
264
|
+
# mcp.json, sessions, skills, hooks). Point it at the versioned home.
|
|
265
|
+
export KIMI_CODE_HOME="$VERSION_DIR/home/${configDirName}"
|
|
266
|
+
`
|
|
267
|
+
: '';
|
|
248
268
|
const launchArgs = agent === 'codex' ? ' -c check_for_update_on_startup=false' : '';
|
|
249
269
|
return `#!/bin/bash
|
|
250
270
|
# Auto-generated by agents-cli - do not edit
|
|
251
271
|
# Shim for ${agentConfig.name}
|
|
252
272
|
# ${SHIM_VERSION_MARKER} ${SHIM_SCHEMA_VERSION}
|
|
253
273
|
|
|
254
|
-
AGENTS_SYSTEM_DIR="$HOME/.agents-system"
|
|
255
274
|
AGENTS_USER_DIR="$HOME/.agents"
|
|
256
275
|
AGENTS_BIN=${agentsBin}
|
|
257
276
|
AGENT="${agent}"
|
|
@@ -262,10 +281,10 @@ if [ -z "$AGENTS_BIN" ] || [ ! -x "$AGENTS_BIN" ]; then
|
|
|
262
281
|
exit 127
|
|
263
282
|
fi
|
|
264
283
|
|
|
265
|
-
# Find project agents.yaml walking up from cwd (skip $HOME/.agents
|
|
284
|
+
# Find project agents.yaml walking up from cwd (skip $HOME/.agents/agents.yaml)
|
|
266
285
|
find_project_version() {
|
|
267
286
|
local dir="$PWD"
|
|
268
|
-
local user_agents_yaml="$
|
|
287
|
+
local user_agents_yaml="$AGENTS_USER_DIR/agents.yaml"
|
|
269
288
|
while [ "$dir" != "/" ]; do
|
|
270
289
|
local candidate="$dir/agents.yaml"
|
|
271
290
|
if [ -f "$candidate" ] && [ "$candidate" != "$user_agents_yaml" ]; then
|
|
@@ -384,6 +403,16 @@ if [ "$AGENT" = "grok" ]; then
|
|
|
384
403
|
# Last resort: whatever is on PATH (user may have installed grok globally)
|
|
385
404
|
BINARY=$(command -v grok 2>/dev/null || echo "")
|
|
386
405
|
fi
|
|
406
|
+
# Kimi special case: binary lives in ~/.kimi-code/bin/, not node_modules.
|
|
407
|
+
# We still use the agents-cli version dir purely for KIMI_CODE_HOME isolation.
|
|
408
|
+
elif [ "$AGENT" = "kimi" ]; then
|
|
409
|
+
KIMI_BINARY="$HOME/.kimi-code/bin/kimi"
|
|
410
|
+
if [ -x "$KIMI_BINARY" ]; then
|
|
411
|
+
BINARY="$KIMI_BINARY"
|
|
412
|
+
else
|
|
413
|
+
# Last resort: whatever is on PATH
|
|
414
|
+
BINARY=$(command -v kimi 2>/dev/null || echo "")
|
|
415
|
+
fi
|
|
387
416
|
else
|
|
388
417
|
BINARY="$VERSION_DIR/node_modules/.bin/$CLI_COMMAND"
|
|
389
418
|
fi
|
|
@@ -451,6 +480,34 @@ fi
|
|
|
451
480
|
|
|
452
481
|
${managedEnv}
|
|
453
482
|
|
|
483
|
+
# Project-scoped compile (rules, workspace resources, scoped plugin marketplaces).
|
|
484
|
+
# Skip-fast: if a sentinel from the last sync exists and is newer than all
|
|
485
|
+
# source dirs (project .agents/, user plugins, system plugins), exec the
|
|
486
|
+
# agent binary directly without spawning node. Cuts steady-state hot-path
|
|
487
|
+
# latency from ~680ms (node startup + agents-cli module init) to ~11ms (a
|
|
488
|
+
# handful of stat calls). Never blocks launch on failure of the sync itself.
|
|
489
|
+
#
|
|
490
|
+
# Known limitation: POSIX dir mtime updates only on entry add/remove at that
|
|
491
|
+
# level. Deep edits to existing plugin contents (e.g. editing a SKILL.md
|
|
492
|
+
# inside a plugin) won't bump the parent dir's mtime — the marketplace copy
|
|
493
|
+
# stays stale until \`agents sync\` runs explicitly or a top-level entry
|
|
494
|
+
# changes. Advanced users hot-iterating on plugins know to run sync.
|
|
495
|
+
PROJECT_SLUG=\$(printf '%s' "\$PWD" | tr / _ | tr ' ' _)
|
|
496
|
+
LAUNCH_SENTINEL="\$AGENTS_USER_DIR/.cache/launch-sync/\${AGENT}@\${VERSION}@\${PROJECT_SLUG}"
|
|
497
|
+
LAUNCH_SKIP=0
|
|
498
|
+
if [ -f "\$LAUNCH_SENTINEL" ]; then
|
|
499
|
+
LAUNCH_SKIP=1
|
|
500
|
+
for LAUNCH_SRC in "\$PWD/.agents" "\$AGENTS_USER_DIR/plugins" "\$AGENTS_USER_DIR/.system/plugins"; do
|
|
501
|
+
if [ -e "\$LAUNCH_SRC" ] && [ "\$LAUNCH_SRC" -nt "\$LAUNCH_SENTINEL" ]; then
|
|
502
|
+
LAUNCH_SKIP=0
|
|
503
|
+
break
|
|
504
|
+
fi
|
|
505
|
+
done
|
|
506
|
+
fi
|
|
507
|
+
if [ "\$LAUNCH_SKIP" = "0" ]; then
|
|
508
|
+
"\$AGENTS_BIN" sync --agent "\$AGENT" --agent-version "\$VERSION" --launch --cwd "\$PWD" --quiet 2>/dev/null || true
|
|
509
|
+
fi
|
|
510
|
+
|
|
454
511
|
exec "$BINARY"${launchArgs} "$@"
|
|
455
512
|
`;
|
|
456
513
|
}
|
|
@@ -540,7 +597,13 @@ export CODEX_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${co
|
|
|
540
597
|
# version MCP and session state are isolated.
|
|
541
598
|
export COPILOT_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
|
|
542
599
|
`
|
|
543
|
-
: ''
|
|
600
|
+
: agent === 'kimi'
|
|
601
|
+
? `
|
|
602
|
+
# Kimi Code CLI honors KIMI_CODE_HOME to relocate ~/.kimi-code (config.toml,
|
|
603
|
+
# mcp.json, sessions, skills, hooks). Point direct aliases at the versioned home.
|
|
604
|
+
export KIMI_CODE_HOME="$HOME/.agents/.history/versions/${agent}/${version}/home/${configDirName}"
|
|
605
|
+
`
|
|
606
|
+
: '';
|
|
544
607
|
const launchArgs = agent === 'codex' ? ' -c check_for_update_on_startup=false' : '';
|
|
545
608
|
return `#!/bin/bash
|
|
546
609
|
# Auto-generated by agents-cli - do not edit
|
|
@@ -754,7 +817,7 @@ export async function switchConfigSymlink(agent, version) {
|
|
|
754
817
|
// Different target - update it
|
|
755
818
|
fs.unlinkSync(configPath);
|
|
756
819
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
757
|
-
fs.symlinkSync(versionConfigPath, configPath);
|
|
820
|
+
fs.symlinkSync(versionConfigPath, configPath, process.platform === 'win32' ? 'junction' : undefined);
|
|
758
821
|
return { success: true };
|
|
759
822
|
}
|
|
760
823
|
else if (stat.isDirectory()) {
|
|
@@ -766,8 +829,25 @@ export async function switchConfigSymlink(agent, version) {
|
|
|
766
829
|
const finalBackupPath = path.join(agentBackupDir, String(timestamp));
|
|
767
830
|
fs.mkdirSync(agentBackupDir, { recursive: true });
|
|
768
831
|
fs.renameSync(configPath, finalBackupPath);
|
|
832
|
+
// Session JSONLs that lived under the old configPath have just moved to
|
|
833
|
+
// finalBackupPath on disk. Rewrite any DB rows pointing at the old prefix
|
|
834
|
+
// so querySessions stops returning phantom rows (issue #136). The
|
|
835
|
+
// discoverer at src/lib/session/discover.ts already scans backup dirs, so
|
|
836
|
+
// future indexer runs will find the new files — this just keeps the
|
|
837
|
+
// existing rows valid in the meantime.
|
|
838
|
+
//
|
|
839
|
+
// Dynamic import so loading shims.ts doesn't transitively open the
|
|
840
|
+
// sessions DB — many tests partially mock state.js and would break.
|
|
841
|
+
try {
|
|
842
|
+
const { updateSessionFilePaths } = await import('./session/db.js');
|
|
843
|
+
updateSessionFilePaths(configPath, finalBackupPath);
|
|
844
|
+
}
|
|
845
|
+
catch (err) {
|
|
846
|
+
console.error(`Warning: failed to update session file_paths after backing up ${configPath}: ` +
|
|
847
|
+
`${err.message}. Stale rows may appear in session listings until the next scan.`);
|
|
848
|
+
}
|
|
769
849
|
// Create symlink (parent already exists since the dir we just moved was here)
|
|
770
|
-
fs.symlinkSync(versionConfigPath, configPath);
|
|
850
|
+
fs.symlinkSync(versionConfigPath, configPath, process.platform === 'win32' ? 'junction' : undefined);
|
|
771
851
|
return { success: true, backupPath: finalBackupPath };
|
|
772
852
|
}
|
|
773
853
|
else {
|
|
@@ -780,7 +860,7 @@ export async function switchConfigSymlink(agent, version) {
|
|
|
780
860
|
// For nested layouts (e.g., ~/.gemini/antigravity-cli) the parent dir
|
|
781
861
|
// may also be missing if the parent agent (Gemini) is not installed.
|
|
782
862
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
783
|
-
fs.symlinkSync(versionConfigPath, configPath);
|
|
863
|
+
fs.symlinkSync(versionConfigPath, configPath, process.platform === 'win32' ? 'junction' : undefined);
|
|
784
864
|
return { success: true };
|
|
785
865
|
}
|
|
786
866
|
return { success: false, error: err.message };
|
|
@@ -1251,23 +1331,53 @@ export function removeLegacyUserShim(agent, overrides) {
|
|
|
1251
1331
|
* Shell aliases live in the user's session and aren't visible from a Node.js
|
|
1252
1332
|
* child process. We do a best-effort scan of common RC files for `alias
|
|
1253
1333
|
* <command>=` patterns. Returns false when detection is inconclusive.
|
|
1334
|
+
*
|
|
1335
|
+
* Tracks the LAST `alias` / `unalias` action for this command per rc file —
|
|
1336
|
+
* a trailing `unalias codex` cancels an earlier `alias codex=...`, and
|
|
1337
|
+
* `unalias` can name multiple commands on one line. Without this, an
|
|
1338
|
+
* `alias` line elsewhere in the file would surface as a false positive
|
|
1339
|
+
* (e.g. seen in zshrc setups that conditionally clear an alias later).
|
|
1254
1340
|
*/
|
|
1255
|
-
|
|
1341
|
+
function escapeRegex(value) {
|
|
1342
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1343
|
+
}
|
|
1344
|
+
/** Walk rc lines in order; a later `unalias` clears an earlier `alias`. */
|
|
1345
|
+
function isAliasActiveInRcContent(content, cliCommand) {
|
|
1346
|
+
let active = false;
|
|
1347
|
+
const aliasPattern = new RegExp(`^\\s*alias\\s+${escapeRegex(cliCommand)}\\s*=`);
|
|
1348
|
+
for (const line of content.split('\n')) {
|
|
1349
|
+
const trimmed = line.trim();
|
|
1350
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
1351
|
+
continue;
|
|
1352
|
+
if (aliasPattern.test(line)) {
|
|
1353
|
+
active = true;
|
|
1354
|
+
continue;
|
|
1355
|
+
}
|
|
1356
|
+
const unaliasMatch = trimmed.match(/^unalias\s+(.+)$/);
|
|
1357
|
+
if (!unaliasMatch)
|
|
1358
|
+
continue;
|
|
1359
|
+
const tokens = unaliasMatch[1].split(/\s+/).filter((token) => !token.startsWith('-'));
|
|
1360
|
+
if (tokens.includes(cliCommand)) {
|
|
1361
|
+
active = false;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
return active;
|
|
1365
|
+
}
|
|
1366
|
+
export function hasAliasShadowingShim(agent, overrides) {
|
|
1256
1367
|
const cliCommand = AGENTS[agent].cliCommand;
|
|
1257
|
-
const
|
|
1368
|
+
const homeDir = overrides?.homeDir ?? os.homedir();
|
|
1258
1369
|
const rcFiles = [
|
|
1259
|
-
path.join(
|
|
1260
|
-
path.join(
|
|
1261
|
-
path.join(
|
|
1262
|
-
path.join(
|
|
1370
|
+
path.join(homeDir, '.zshrc'),
|
|
1371
|
+
path.join(homeDir, '.bashrc'),
|
|
1372
|
+
path.join(homeDir, '.bash_profile'),
|
|
1373
|
+
path.join(homeDir, '.profile'),
|
|
1263
1374
|
];
|
|
1264
|
-
const pattern = new RegExp(`^\\s*alias\\s+${cliCommand}\\s*=`, 'm');
|
|
1265
1375
|
for (const rcFile of rcFiles) {
|
|
1266
1376
|
try {
|
|
1267
1377
|
if (!fs.existsSync(rcFile))
|
|
1268
1378
|
continue;
|
|
1269
1379
|
const content = fs.readFileSync(rcFile, 'utf-8');
|
|
1270
|
-
if (
|
|
1380
|
+
if (isAliasActiveInRcContent(content, cliCommand))
|
|
1271
1381
|
return true;
|
|
1272
1382
|
}
|
|
1273
1383
|
catch {
|
|
@@ -1303,6 +1413,7 @@ function isShimPathCommandLine(line, shimsDir) {
|
|
|
1303
1413
|
const markerRegexes = exactMarkers.map((marker) => new RegExp(`${escapeRegex(marker)}(?=$|[:\\s])`));
|
|
1304
1414
|
const suffixRegexes = [
|
|
1305
1415
|
/\/\.agents-system\/shims(?=$|[:\s])/,
|
|
1416
|
+
/\/\.agents\/\.cache\/shims(?=$|[:\s])/,
|
|
1306
1417
|
/\/\.agents\/shims(?=$|[:\s])/,
|
|
1307
1418
|
];
|
|
1308
1419
|
const touchesShimPath = [...markerRegexes, ...suffixRegexes].some((pattern) => pattern.test(normalized));
|
|
@@ -1364,10 +1475,10 @@ export function getPathSetupInstructions() {
|
|
|
1364
1475
|
return `Add to ~/.config/fish/config.fish:
|
|
1365
1476
|
fish_add_path ${shimsDir}`;
|
|
1366
1477
|
}
|
|
1367
|
-
return `Add to ~/${rcFile} (
|
|
1478
|
+
return `Add to the end of ~/${rcFile} (after any nvm/node setup and agent installers):
|
|
1368
1479
|
export PATH="${shimsDir}:$PATH"
|
|
1369
1480
|
|
|
1370
|
-
IMPORTANT: Shims must
|
|
1481
|
+
IMPORTANT: Shims must be the last PATH prepend in your shell config to override global installs.
|
|
1371
1482
|
|
|
1372
1483
|
Then restart your shell or run:
|
|
1373
1484
|
source ~/${rcFile}`;
|
|
@@ -1398,25 +1509,6 @@ export function addShimsToPath(overrides) {
|
|
|
1398
1509
|
exportBlock = `# agents-cli: version-managed agent CLIs\nexport PATH="${shimsDir}:$PATH"\n`;
|
|
1399
1510
|
}
|
|
1400
1511
|
const contentWithoutShimLines = stripShimPathLines(content, shimsDir);
|
|
1401
|
-
// Find insertion point - AFTER node/nvm/fnm setup if present, otherwise append.
|
|
1402
|
-
const insertAfterPatterns = [
|
|
1403
|
-
/^export NVM_DIR=/m,
|
|
1404
|
-
/^source.*nvm/m,
|
|
1405
|
-
/^\[ -s.*nvm/m,
|
|
1406
|
-
/^eval.*fnm/m,
|
|
1407
|
-
/^export PATH.*node/m,
|
|
1408
|
-
/^export PATH.*npm/m,
|
|
1409
|
-
];
|
|
1410
|
-
let insertIndex = -1;
|
|
1411
|
-
const lines = contentWithoutShimLines.split('\n');
|
|
1412
|
-
let offset = 0;
|
|
1413
|
-
for (const line of lines) {
|
|
1414
|
-
const lineWithNewline = `${line}\n`;
|
|
1415
|
-
if (insertAfterPatterns.some((pattern) => pattern.test(line))) {
|
|
1416
|
-
insertIndex = offset + lineWithNewline.length;
|
|
1417
|
-
}
|
|
1418
|
-
offset += lineWithNewline.length;
|
|
1419
|
-
}
|
|
1420
1512
|
// Write the updated content
|
|
1421
1513
|
try {
|
|
1422
1514
|
// Ensure parent directories exist (especially for fish: ~/.config/fish/)
|
|
@@ -1424,18 +1516,9 @@ export function addShimsToPath(overrides) {
|
|
|
1424
1516
|
if (!fs.existsSync(rcDir)) {
|
|
1425
1517
|
fs.mkdirSync(rcDir, { recursive: true });
|
|
1426
1518
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
const before = contentWithoutShimLines.slice(0, insertIndex);
|
|
1431
|
-
const separator = before.length > 0 && !before.endsWith('\n\n') ? '\n' : '';
|
|
1432
|
-
newContent = before + separator + exportBlock + contentWithoutShimLines.slice(insertIndex);
|
|
1433
|
-
}
|
|
1434
|
-
else {
|
|
1435
|
-
// Append to end
|
|
1436
|
-
const separator = contentWithoutShimLines.length > 0 && !contentWithoutShimLines.endsWith('\n') ? '\n' : '';
|
|
1437
|
-
newContent = contentWithoutShimLines + separator + exportBlock;
|
|
1438
|
-
}
|
|
1519
|
+
// Append at EOF so later installer PATH prepends cannot shadow the shims.
|
|
1520
|
+
const separator = contentWithoutShimLines.length > 0 && !contentWithoutShimLines.endsWith('\n') ? '\n' : '';
|
|
1521
|
+
let newContent = contentWithoutShimLines + separator + exportBlock;
|
|
1439
1522
|
newContent = newContent.replace(/\n{2,}$/g, '\n');
|
|
1440
1523
|
if (newContent === content) {
|
|
1441
1524
|
return { success: true, alreadyPresent: true, rcFile };
|
package/dist/lib/skills.d.ts
CHANGED
|
@@ -73,7 +73,7 @@ export declare function skillExists(agentId: AgentId, skillName: string): boolea
|
|
|
73
73
|
*/
|
|
74
74
|
export declare function skillContentMatches(agentId: AgentId, skillName: string, sourcePath: string): boolean;
|
|
75
75
|
/**
|
|
76
|
-
* List skill names from user (~/.agents/skills/) and system (~/.agents
|
|
76
|
+
* List skill names from user (~/.agents/skills/) and system (~/.agents/.system/skills/) dirs.
|
|
77
77
|
* User dir takes priority; deduplication preserves first occurrence.
|
|
78
78
|
*/
|
|
79
79
|
export declare function listCentralSkills(): string[];
|
package/dist/lib/skills.js
CHANGED
|
@@ -10,7 +10,8 @@ import * as fs from 'fs';
|
|
|
10
10
|
import * as path from 'path';
|
|
11
11
|
import * as os from 'os';
|
|
12
12
|
import * as yaml from 'yaml';
|
|
13
|
-
import {
|
|
13
|
+
import { ensureSkillsDir, agentConfigDirName } from './agents.js';
|
|
14
|
+
import { capableAgents, isCapable } from './capabilities.js';
|
|
14
15
|
import { getUserSkillsDir, getSkillsDir as getSystemSkillsDir, getProjectAgentsDir, getEnabledExtraRepos, getTrashSkillsDir } from './state.js';
|
|
15
16
|
import { getEffectiveHome, getVersionHomePath, listInstalledVersions } from './versions.js';
|
|
16
17
|
import { emit } from './events.js';
|
|
@@ -27,7 +28,7 @@ export function ensureCentralSkillsDir() {
|
|
|
27
28
|
}
|
|
28
29
|
export function getAgentSkillsDir(agentId) {
|
|
29
30
|
const home = getEffectiveHome(agentId);
|
|
30
|
-
return path.join(home,
|
|
31
|
+
return path.join(home, agentConfigDirName(agentId), 'skills');
|
|
31
32
|
}
|
|
32
33
|
export function getProjectSkillsDir(agentId, cwd = process.cwd()) {
|
|
33
34
|
const dirs = [];
|
|
@@ -227,7 +228,7 @@ export function installSkill(sourcePath, skillName, agents, method = 'symlink')
|
|
|
227
228
|
}
|
|
228
229
|
// Symlink to each agent
|
|
229
230
|
for (const agentId of agents) {
|
|
230
|
-
if (!
|
|
231
|
+
if (!isCapable(agentId, 'skills')) {
|
|
231
232
|
continue;
|
|
232
233
|
}
|
|
233
234
|
ensureSkillsDir(agentId);
|
|
@@ -328,7 +329,7 @@ export function skillContentMatches(agentId, skillName, sourcePath) {
|
|
|
328
329
|
}
|
|
329
330
|
}
|
|
330
331
|
/**
|
|
331
|
-
* List skill names from user (~/.agents/skills/) and system (~/.agents
|
|
332
|
+
* List skill names from user (~/.agents/skills/) and system (~/.agents/.system/skills/) dirs.
|
|
332
333
|
* User dir takes priority; deduplication preserves first occurrence.
|
|
333
334
|
*/
|
|
334
335
|
export function listCentralSkills() {
|
|
@@ -388,7 +389,7 @@ export function listAllSkills() {
|
|
|
388
389
|
*/
|
|
389
390
|
export function getVersionSkillsDir(agent, version) {
|
|
390
391
|
const home = getVersionHomePath(agent, version);
|
|
391
|
-
return path.join(home,
|
|
392
|
+
return path.join(home, agentConfigDirName(agent), 'skills');
|
|
392
393
|
}
|
|
393
394
|
/**
|
|
394
395
|
* List skill names installed in a specific version home.
|
|
@@ -586,9 +587,9 @@ export function removeSkillFromVersion(agent, version, skillName) {
|
|
|
586
587
|
*/
|
|
587
588
|
export function iterSkillsCapableVersions(filter) {
|
|
588
589
|
const pairs = [];
|
|
589
|
-
const agents = filter?.agent ? [filter.agent] :
|
|
590
|
+
const agents = filter?.agent ? [filter.agent] : capableAgents('skills');
|
|
590
591
|
for (const agent of agents) {
|
|
591
|
-
if (!
|
|
592
|
+
if (!capableAgents('skills').includes(agent))
|
|
592
593
|
continue;
|
|
593
594
|
const versions = listInstalledVersions(agent);
|
|
594
595
|
for (const version of versions) {
|
|
@@ -606,7 +607,7 @@ export function uninstallSkill(skillName) {
|
|
|
606
607
|
return { success: false, error: `Skill '${skillName}' not found` };
|
|
607
608
|
}
|
|
608
609
|
// Remove from all agents
|
|
609
|
-
for (const agentId of
|
|
610
|
+
for (const agentId of capableAgents('skills')) {
|
|
610
611
|
const agentSkillPath = path.join(getAgentSkillsDir(agentId), skillName);
|
|
611
612
|
if (fs.existsSync(agentSkillPath)) {
|
|
612
613
|
try {
|
|
@@ -727,7 +728,7 @@ export function listInstalledSkillsWithScope(agentId, cwd = process.cwd(), optio
|
|
|
727
728
|
}
|
|
728
729
|
// User-scoped skills (version-aware when home is provided)
|
|
729
730
|
const userSkillsDir = options?.home
|
|
730
|
-
? path.join(options.home,
|
|
731
|
+
? path.join(options.home, agentConfigDirName(agentId), 'skills')
|
|
731
732
|
: getAgentSkillsDir(agentId);
|
|
732
733
|
if (fs.existsSync(userSkillsDir)) {
|
|
733
734
|
try {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commands detector — mirrors versions.ts:343-357. Inspects the version home,
|
|
3
|
+
* returns command names. Honors the commands-as-skills marker for grok and
|
|
4
|
+
* Codex >= 0.117.0; falls back to scanning `{agentDir}/<commandsSubdir>/` for
|
|
5
|
+
* the native path.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { AGENTS, agentConfigDirName } from '../../agents.js';
|
|
10
|
+
import { shouldInstallCommandAsSkill, listCommandSkillsInVersion } from '../../command-skills.js';
|
|
11
|
+
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
12
|
+
function buildCommandsDetector(agent) {
|
|
13
|
+
return {
|
|
14
|
+
kind: 'commands',
|
|
15
|
+
agent,
|
|
16
|
+
list({ version, versionHome }) {
|
|
17
|
+
const agentConfig = AGENTS[agent];
|
|
18
|
+
const agentDir = path.join(versionHome, agentConfigDirName(agent));
|
|
19
|
+
if (shouldInstallCommandAsSkill(agent, version)) {
|
|
20
|
+
return listCommandSkillsInVersion(agentDir);
|
|
21
|
+
}
|
|
22
|
+
const commandsDir = path.join(agentDir, agentConfig.commandsSubdir);
|
|
23
|
+
if (!fs.existsSync(commandsDir))
|
|
24
|
+
return [];
|
|
25
|
+
const ext = agentConfig.format === 'toml' ? '.toml' : '.md';
|
|
26
|
+
return fs.readdirSync(commandsDir)
|
|
27
|
+
.filter(f => f.endsWith(ext))
|
|
28
|
+
.map(f => f.replace(new RegExp(`\\${ext}$`), ''));
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// Detector registration mirrors writers/commands.ts — see that file for the
|
|
33
|
+
// openclaw vs grok asymmetry.
|
|
34
|
+
export const commandsDetectors = lazyAgentMap(() => {
|
|
35
|
+
const m = {};
|
|
36
|
+
for (const id of Object.keys(AGENTS)) {
|
|
37
|
+
const cfg = AGENTS[id];
|
|
38
|
+
if (cfg.capabilities.commands === false && (!cfg.commandsSubdir || cfg.commandsSubdir === '') && id !== 'grok')
|
|
39
|
+
continue;
|
|
40
|
+
const hasCommands = cfg.capabilities.commands !== false;
|
|
41
|
+
const hasSkills = cfg.capabilities.skills !== false;
|
|
42
|
+
if (hasCommands || hasSkills)
|
|
43
|
+
m[id] = buildCommandsDetector(id);
|
|
44
|
+
}
|
|
45
|
+
return m;
|
|
46
|
+
});
|