@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
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks detector — names of hook scripts materialized in the version home
|
|
3
|
+
* whose contents match the central source. Mirrors versions.ts:391-421.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import { agentConfigDirName } from '../../agents.js';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { capableAgents } from '../../capabilities.js';
|
|
9
|
+
import { resolveHookSource } from '../writers/sources.js';
|
|
10
|
+
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
11
|
+
function buildHooksDetector(agent) {
|
|
12
|
+
return {
|
|
13
|
+
kind: 'hooks',
|
|
14
|
+
agent,
|
|
15
|
+
list({ versionHome }) {
|
|
16
|
+
const hooksDir = path.join(versionHome, agentConfigDirName(agent), 'hooks');
|
|
17
|
+
if (!fs.existsSync(hooksDir))
|
|
18
|
+
return [];
|
|
19
|
+
const installed = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
|
|
20
|
+
const synced = [];
|
|
21
|
+
for (const hook of installed) {
|
|
22
|
+
const src = resolveHookSource(hook);
|
|
23
|
+
if (!src) {
|
|
24
|
+
// True orphan — count as accounted for.
|
|
25
|
+
synced.push(hook);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
if (fs.readFileSync(src, 'utf-8') === fs.readFileSync(path.join(hooksDir, hook), 'utf-8')) {
|
|
30
|
+
synced.push(hook);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch { /* read failure → not synced */ }
|
|
34
|
+
}
|
|
35
|
+
return synced;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export const hooksDetectors = lazyAgentMap(() => {
|
|
40
|
+
const m = {};
|
|
41
|
+
for (const agent of capableAgents('hooks'))
|
|
42
|
+
m[agent] = buildHooksDetector(agent);
|
|
43
|
+
return m;
|
|
44
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP detector — parses the agent's canonical MCP config in the version home
|
|
3
|
+
* and returns server names. Mirrors versions.ts:432-443.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import { capableAgents } from '../../capabilities.js';
|
|
7
|
+
import { getMcpConfigPathForHome, parseMcpConfig } from '../../agents.js';
|
|
8
|
+
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
9
|
+
function buildMcpDetector(agent) {
|
|
10
|
+
return {
|
|
11
|
+
kind: 'mcp',
|
|
12
|
+
agent,
|
|
13
|
+
list({ versionHome }) {
|
|
14
|
+
const p = getMcpConfigPathForHome(agent, versionHome);
|
|
15
|
+
if (!fs.existsSync(p))
|
|
16
|
+
return [];
|
|
17
|
+
try {
|
|
18
|
+
return Object.keys(parseMcpConfig(agent, p));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export const mcpDetectors = lazyAgentMap(() => {
|
|
27
|
+
const m = {};
|
|
28
|
+
for (const agent of capableAgents('mcp'))
|
|
29
|
+
m[agent] = buildMcpDetector(agent);
|
|
30
|
+
return m;
|
|
31
|
+
});
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permissions detector — inspects the agent's native permission storage and
|
|
3
|
+
* reports the permission GROUP names that have been applied.
|
|
4
|
+
*
|
|
5
|
+
* For claude/opencode the detector intersects with discovered groups (a group
|
|
6
|
+
* is "applied" if any of its allow/deny rules are present). For Codex / Gemini /
|
|
7
|
+
* Antigravity / Grok the on-disk format is lossy — once any group has been
|
|
8
|
+
* applied the storage doesn't carry per-group provenance back, so we report
|
|
9
|
+
* "all known groups applied" when any permission artifact is present. This
|
|
10
|
+
* matches the existing behavior in versions.ts:445-518 (extended to the
|
|
11
|
+
* agents that were previously silent-skipped).
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import * as TOML from 'smol-toml';
|
|
16
|
+
import { capableAgents } from '../../capabilities.js';
|
|
17
|
+
import { discoverPermissionGroups, buildPermissionsFromGroups, CODEX_RULES_FILENAME, } from '../../permissions.js';
|
|
18
|
+
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
19
|
+
function buildClaudeDetector() {
|
|
20
|
+
return {
|
|
21
|
+
kind: 'permissions',
|
|
22
|
+
agent: 'claude',
|
|
23
|
+
list({ versionHome }) {
|
|
24
|
+
const settingsPath = path.join(versionHome, '.claude', 'settings.json');
|
|
25
|
+
if (!fs.existsSync(settingsPath))
|
|
26
|
+
return [];
|
|
27
|
+
try {
|
|
28
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
29
|
+
const allowRules = settings.permissions?.allow || [];
|
|
30
|
+
const denyRules = settings.permissions?.deny || [];
|
|
31
|
+
if (allowRules.length === 0 && denyRules.length === 0)
|
|
32
|
+
return [];
|
|
33
|
+
const groups = discoverPermissionGroups();
|
|
34
|
+
const applied = [];
|
|
35
|
+
for (const group of groups) {
|
|
36
|
+
const built = buildPermissionsFromGroups([group.name]);
|
|
37
|
+
// Empty groups (header files) count as synced when anything is applied.
|
|
38
|
+
if (built.allow.length === 0 && (!built.deny || built.deny.length === 0)) {
|
|
39
|
+
applied.push(group.name);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const hasAllow = built.allow.some(r => allowRules.includes(r));
|
|
43
|
+
const hasDeny = built.deny?.some(r => denyRules.includes(r)) || false;
|
|
44
|
+
if (hasAllow || hasDeny)
|
|
45
|
+
applied.push(group.name);
|
|
46
|
+
}
|
|
47
|
+
return applied;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function buildCodexDetector() {
|
|
56
|
+
return {
|
|
57
|
+
kind: 'permissions',
|
|
58
|
+
agent: 'codex',
|
|
59
|
+
list({ versionHome }) {
|
|
60
|
+
const codexConfigPath = path.join(versionHome, '.codex', 'config.toml');
|
|
61
|
+
const codexRulesPath = path.join(versionHome, '.codex', 'rules', CODEX_RULES_FILENAME);
|
|
62
|
+
const hasConfig = fs.existsSync(codexConfigPath);
|
|
63
|
+
const hasRules = fs.existsSync(codexRulesPath);
|
|
64
|
+
if (!hasConfig && !hasRules)
|
|
65
|
+
return [];
|
|
66
|
+
try {
|
|
67
|
+
let hasPermKeys = false;
|
|
68
|
+
if (hasConfig) {
|
|
69
|
+
const config = TOML.parse(fs.readFileSync(codexConfigPath, 'utf-8'));
|
|
70
|
+
hasPermKeys = !!(config.approval_policy || config.sandbox_mode || config.sandbox_workspace_write);
|
|
71
|
+
}
|
|
72
|
+
if (hasPermKeys || hasRules) {
|
|
73
|
+
return discoverPermissionGroups().map(g => g.name);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch { /* parse fail */ }
|
|
77
|
+
return [];
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function buildOpenCodeDetector() {
|
|
82
|
+
return {
|
|
83
|
+
kind: 'permissions',
|
|
84
|
+
agent: 'opencode',
|
|
85
|
+
list({ versionHome }) {
|
|
86
|
+
const opencodeConfigPath = path.join(versionHome, '.opencode', 'opencode.jsonc');
|
|
87
|
+
if (!fs.existsSync(opencodeConfigPath))
|
|
88
|
+
return [];
|
|
89
|
+
try {
|
|
90
|
+
const content = fs.readFileSync(opencodeConfigPath, 'utf-8');
|
|
91
|
+
const stripped = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
92
|
+
const config = JSON.parse(stripped);
|
|
93
|
+
if (config.permission && Object.keys(config.permission.bash || {}).length > 0) {
|
|
94
|
+
return discoverPermissionGroups().map(g => g.name);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch { /* parse fail */ }
|
|
98
|
+
return [];
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function buildGeminiDetector() {
|
|
103
|
+
return {
|
|
104
|
+
kind: 'permissions',
|
|
105
|
+
agent: 'gemini',
|
|
106
|
+
list({ versionHome }) {
|
|
107
|
+
const settingsPath = path.join(versionHome, '.gemini', 'settings.json');
|
|
108
|
+
if (!fs.existsSync(settingsPath))
|
|
109
|
+
return [];
|
|
110
|
+
try {
|
|
111
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
112
|
+
const allowed = settings?.tools?.allowed;
|
|
113
|
+
if (Array.isArray(allowed) && allowed.length > 0) {
|
|
114
|
+
return discoverPermissionGroups().map(g => g.name);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch { /* parse fail */ }
|
|
118
|
+
return [];
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function buildAntigravityDetector() {
|
|
123
|
+
return {
|
|
124
|
+
kind: 'permissions',
|
|
125
|
+
agent: 'antigravity',
|
|
126
|
+
list({ versionHome }) {
|
|
127
|
+
const settingsPath = path.join(versionHome, '.gemini', 'antigravity-cli', 'settings.json');
|
|
128
|
+
if (!fs.existsSync(settingsPath))
|
|
129
|
+
return [];
|
|
130
|
+
try {
|
|
131
|
+
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
132
|
+
const perms = settings?.permissions;
|
|
133
|
+
const hasAllow = Array.isArray(perms?.allow) && perms.allow.length > 0;
|
|
134
|
+
const hasDeny = Array.isArray(perms?.deny) && perms.deny.length > 0;
|
|
135
|
+
if (hasAllow || hasDeny) {
|
|
136
|
+
return discoverPermissionGroups().map(g => g.name);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch { /* parse fail */ }
|
|
140
|
+
return [];
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function buildGrokDetector() {
|
|
145
|
+
return {
|
|
146
|
+
kind: 'permissions',
|
|
147
|
+
agent: 'grok',
|
|
148
|
+
list({ versionHome }) {
|
|
149
|
+
const configPath = path.join(versionHome, '.grok', 'config.toml');
|
|
150
|
+
if (!fs.existsSync(configPath))
|
|
151
|
+
return [];
|
|
152
|
+
try {
|
|
153
|
+
const config = TOML.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
154
|
+
const perm = config.permission;
|
|
155
|
+
if (perm && Array.isArray(perm.rules) && perm.rules.length > 0) {
|
|
156
|
+
return discoverPermissionGroups().map(g => g.name);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch { /* parse fail */ }
|
|
160
|
+
return [];
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
function buildKimiDetector() {
|
|
165
|
+
return {
|
|
166
|
+
kind: 'permissions',
|
|
167
|
+
agent: 'kimi',
|
|
168
|
+
list({ versionHome }) {
|
|
169
|
+
const configPath = path.join(versionHome, '.kimi-code', 'config.toml');
|
|
170
|
+
if (!fs.existsSync(configPath))
|
|
171
|
+
return [];
|
|
172
|
+
try {
|
|
173
|
+
const config = TOML.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
174
|
+
const perm = config.permission;
|
|
175
|
+
if (perm && Array.isArray(perm.rules) && perm.rules.length > 0) {
|
|
176
|
+
return discoverPermissionGroups().map(g => g.name);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch { /* parse fail */ }
|
|
180
|
+
return [];
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const handlers = {
|
|
185
|
+
claude: buildClaudeDetector,
|
|
186
|
+
codex: buildCodexDetector,
|
|
187
|
+
opencode: buildOpenCodeDetector,
|
|
188
|
+
gemini: buildGeminiDetector,
|
|
189
|
+
antigravity: buildAntigravityDetector,
|
|
190
|
+
grok: buildGrokDetector,
|
|
191
|
+
kimi: buildKimiDetector,
|
|
192
|
+
};
|
|
193
|
+
export const permissionsDetectors = lazyAgentMap(() => {
|
|
194
|
+
const m = {};
|
|
195
|
+
for (const agent of capableAgents('allowlist')) {
|
|
196
|
+
const f = handlers[agent];
|
|
197
|
+
if (f)
|
|
198
|
+
m[agent] = f();
|
|
199
|
+
}
|
|
200
|
+
return m;
|
|
201
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugins detector — for each discovered plugin, ask `isPluginSynced` whether
|
|
3
|
+
* its expected artifacts are present in the version home. Mirrors
|
|
4
|
+
* versions.ts:541-549.
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentId } from '../../types.js';
|
|
7
|
+
import type { ResourceDetector } from './types.js';
|
|
8
|
+
export declare const pluginsDetectors: Partial<Record<AgentId, ResourceDetector>>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { capableAgents } from '../../capabilities.js';
|
|
2
|
+
import { discoverPlugins, isPluginSynced } from '../../plugins.js';
|
|
3
|
+
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
4
|
+
function buildPluginsDetector(agent) {
|
|
5
|
+
return {
|
|
6
|
+
kind: 'plugins',
|
|
7
|
+
agent,
|
|
8
|
+
list({ versionHome }) {
|
|
9
|
+
const synced = [];
|
|
10
|
+
for (const plugin of discoverPlugins()) {
|
|
11
|
+
if (isPluginSynced(plugin, agent, versionHome))
|
|
12
|
+
synced.push(plugin.name);
|
|
13
|
+
}
|
|
14
|
+
return synced;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export const pluginsDetectors = lazyAgentMap(() => {
|
|
19
|
+
const m = {};
|
|
20
|
+
for (const agent of capableAgents('plugins'))
|
|
21
|
+
m[agent] = buildPluginsDetector(agent);
|
|
22
|
+
return m;
|
|
23
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rules detector — reports whether the composed instructions file exists in
|
|
3
|
+
* the version home. Uses the active rules preset name as the detected
|
|
4
|
+
* "resource name" so the diff stays meaningful (one preset = one synced
|
|
5
|
+
* name, mirroring what getAvailableResources surfaces under `memory`).
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { AGENTS, agentConfigDirName } from '../../agents.js';
|
|
10
|
+
import { capableAgents } from '../../capabilities.js';
|
|
11
|
+
import { getActiveRulesPreset } from '../../state.js';
|
|
12
|
+
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
13
|
+
function buildRulesDetector(agent) {
|
|
14
|
+
return {
|
|
15
|
+
kind: 'rules',
|
|
16
|
+
agent,
|
|
17
|
+
list({ version, versionHome }) {
|
|
18
|
+
const cap = AGENTS[agent].capabilities.rules;
|
|
19
|
+
if (cap === false)
|
|
20
|
+
return [];
|
|
21
|
+
const agentDir = path.join(versionHome, agentConfigDirName(agent));
|
|
22
|
+
const instrFile = path.join(agentDir, cap.file);
|
|
23
|
+
if (!fs.existsSync(instrFile))
|
|
24
|
+
return [];
|
|
25
|
+
return [getActiveRulesPreset(agent, version)];
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export const rulesDetectors = lazyAgentMap(() => {
|
|
30
|
+
const m = {};
|
|
31
|
+
for (const agent of capableAgents('rules'))
|
|
32
|
+
m[agent] = buildRulesDetector(agent);
|
|
33
|
+
return m;
|
|
34
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills detector — names of skill directories materialized in the version
|
|
3
|
+
* home that match the central source content. Mirrors versions.ts:359-389.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { AGENTS, agentConfigDirName } from '../../agents.js';
|
|
8
|
+
import { capableAgents } from '../../capabilities.js';
|
|
9
|
+
import { resolveSkillSource } from '../writers/sources.js';
|
|
10
|
+
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
11
|
+
const SKILL_COPY_IGNORE = new Set(['.DS_Store', '.git', '.gitignore', '.venv', '__pycache__', 'node_modules']);
|
|
12
|
+
function skillDirsMatch(src, dest) {
|
|
13
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
if (entry.isSymbolicLink())
|
|
16
|
+
continue;
|
|
17
|
+
if (SKILL_COPY_IGNORE.has(entry.name))
|
|
18
|
+
continue;
|
|
19
|
+
const srcPath = path.join(src, entry.name);
|
|
20
|
+
const destPath = path.join(dest, entry.name);
|
|
21
|
+
if (entry.isDirectory()) {
|
|
22
|
+
if (!fs.existsSync(destPath))
|
|
23
|
+
return false;
|
|
24
|
+
if (!skillDirsMatch(srcPath, destPath))
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
if (!fs.existsSync(destPath))
|
|
29
|
+
return false;
|
|
30
|
+
if (fs.readFileSync(srcPath, 'utf-8') !== fs.readFileSync(destPath, 'utf-8'))
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
function buildSkillsDetector(agent) {
|
|
37
|
+
return {
|
|
38
|
+
kind: 'skills',
|
|
39
|
+
agent,
|
|
40
|
+
list({ versionHome }) {
|
|
41
|
+
const skillsDir = path.join(versionHome, agentConfigDirName(agent), 'skills');
|
|
42
|
+
if (!fs.existsSync(skillsDir))
|
|
43
|
+
return [];
|
|
44
|
+
const installed = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
45
|
+
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
46
|
+
.map(d => d.name);
|
|
47
|
+
const synced = [];
|
|
48
|
+
for (const name of installed) {
|
|
49
|
+
const src = resolveSkillSource(name);
|
|
50
|
+
if (!src) {
|
|
51
|
+
// True orphan — no source. Still count so cleanup knows it's accounted for.
|
|
52
|
+
synced.push(name);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (skillDirsMatch(src, path.join(skillsDir, name))) {
|
|
56
|
+
synced.push(name);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return synced;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export const skillsDetectors = lazyAgentMap(() => {
|
|
64
|
+
const m = {};
|
|
65
|
+
for (const agent of capableAgents('skills')) {
|
|
66
|
+
if (AGENTS[agent].nativeAgentsSkillsDir)
|
|
67
|
+
continue;
|
|
68
|
+
m[agent] = buildSkillsDetector(agent);
|
|
69
|
+
}
|
|
70
|
+
return m;
|
|
71
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagents detector. Claude: flat .md files under `<agentDir>/agents/`.
|
|
3
|
+
* OpenClaw: subdirectories containing AGENTS.md under `<versionHome>/.openclaw/`.
|
|
4
|
+
* Mirrors versions.ts:521-539.
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { capableAgents } from '../../capabilities.js';
|
|
9
|
+
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
10
|
+
function buildClaudeDetector() {
|
|
11
|
+
return {
|
|
12
|
+
kind: 'subagents',
|
|
13
|
+
agent: 'claude',
|
|
14
|
+
list({ versionHome }) {
|
|
15
|
+
const agentsDir = path.join(versionHome, '.claude', 'agents');
|
|
16
|
+
if (!fs.existsSync(agentsDir))
|
|
17
|
+
return [];
|
|
18
|
+
return fs.readdirSync(agentsDir)
|
|
19
|
+
.filter(f => f.endsWith('.md'))
|
|
20
|
+
.map(f => f.replace('.md', ''));
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function buildOpenclawDetector() {
|
|
25
|
+
return {
|
|
26
|
+
kind: 'subagents',
|
|
27
|
+
agent: 'openclaw',
|
|
28
|
+
list({ versionHome }) {
|
|
29
|
+
const openclawDir = path.join(versionHome, '.openclaw');
|
|
30
|
+
if (!fs.existsSync(openclawDir))
|
|
31
|
+
return [];
|
|
32
|
+
return fs.readdirSync(openclawDir, { withFileTypes: true })
|
|
33
|
+
.filter(d => d.isDirectory() && fs.existsSync(path.join(openclawDir, d.name, 'AGENTS.md')))
|
|
34
|
+
.map(d => d.name);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const handlers = {
|
|
39
|
+
claude: buildClaudeDetector,
|
|
40
|
+
openclaw: buildOpenclawDetector,
|
|
41
|
+
};
|
|
42
|
+
export const subagentsDetectors = lazyAgentMap(() => {
|
|
43
|
+
const m = {};
|
|
44
|
+
for (const agent of capableAgents('subagents')) {
|
|
45
|
+
const f = handlers[agent];
|
|
46
|
+
if (f)
|
|
47
|
+
m[agent] = f();
|
|
48
|
+
}
|
|
49
|
+
return m;
|
|
50
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-(kind, agent) detector contract.
|
|
3
|
+
*
|
|
4
|
+
* A detector inspects a version home and reports which resource names of a
|
|
5
|
+
* given kind are materialized there. The aggregator at
|
|
6
|
+
* `getActuallySyncedResources` calls one detector per kind/agent pair to build
|
|
7
|
+
* the "what is actually on disk" view, which is then diffed against
|
|
8
|
+
* "what is available" to drive the resource prompt in `agents view`.
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentId } from '../../types.js';
|
|
11
|
+
import type { ResourceKind } from '../writers/kinds.js';
|
|
12
|
+
export interface DetectArgs {
|
|
13
|
+
version: string;
|
|
14
|
+
versionHome: string;
|
|
15
|
+
/** Working directory — needed by detectors that resolve project state. */
|
|
16
|
+
cwd: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ResourceDetector {
|
|
19
|
+
readonly kind: ResourceKind;
|
|
20
|
+
readonly agent: AgentId;
|
|
21
|
+
list(args: DetectArgs): string[];
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflows detector — scans `{versionHome}/workflows/` for subdirectories
|
|
3
|
+
* containing WORKFLOW.md. Mirrors versions.ts:551-558.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { capableAgents } from '../../capabilities.js';
|
|
8
|
+
import { lazyAgentMap } from '../writers/lazy-map.js';
|
|
9
|
+
function buildWorkflowsDetector(agent) {
|
|
10
|
+
return {
|
|
11
|
+
kind: 'workflows',
|
|
12
|
+
agent,
|
|
13
|
+
list({ versionHome }) {
|
|
14
|
+
const workflowsDir = path.join(versionHome, 'workflows');
|
|
15
|
+
if (!fs.existsSync(workflowsDir))
|
|
16
|
+
return [];
|
|
17
|
+
return fs.readdirSync(workflowsDir, { withFileTypes: true })
|
|
18
|
+
.filter(d => d.isDirectory() && fs.existsSync(path.join(workflowsDir, d.name, 'WORKFLOW.md')))
|
|
19
|
+
.map(d => d.name);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export const workflowsDetectors = lazyAgentMap(() => {
|
|
24
|
+
const m = {};
|
|
25
|
+
for (const agent of capableAgents('workflows'))
|
|
26
|
+
m[agent] = buildWorkflowsDetector(agent);
|
|
27
|
+
return m;
|
|
28
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AgentId } from '../types.js';
|
|
2
|
+
import { type ResourceKind } from './writers/kinds.js';
|
|
3
|
+
import type { ResourceWriter } from './writers/types.js';
|
|
4
|
+
import type { ResourceDetector } from './detectors/types.js';
|
|
5
|
+
import type { RulesSelection } from './writers/rules.js';
|
|
6
|
+
export type { ResourceKind } from './writers/kinds.js';
|
|
7
|
+
export { kindToCapability, ALL_RESOURCE_KINDS } from './writers/kinds.js';
|
|
8
|
+
export type { ResourceWriter, WriteArgs, WriteResult } from './writers/types.js';
|
|
9
|
+
export type { ResourceDetector, DetectArgs } from './detectors/types.js';
|
|
10
|
+
export type { RulesSelection } from './writers/rules.js';
|
|
11
|
+
/** Per-kind selection payload. Most kinds are string[]; rules is special. */
|
|
12
|
+
export type SelectionFor<K extends ResourceKind> = K extends 'rules' ? RulesSelection : string[];
|
|
13
|
+
export declare const WRITERS: Record<ResourceKind, Partial<Record<AgentId, ResourceWriter<any>>>>;
|
|
14
|
+
export declare const DETECTORS: Record<ResourceKind, Partial<Record<AgentId, ResourceDetector>>>;
|
|
15
|
+
/**
|
|
16
|
+
* Verify every supported (agent, kind) pair has both a writer and a
|
|
17
|
+
* detector. Deferred from module-import time to the first `getWriter` /
|
|
18
|
+
* `getDetector` call to dodge the agents.ts ↔ versions.ts ↔ registry.ts
|
|
19
|
+
* import cycle (the same one the lazy writer/detector maps work around).
|
|
20
|
+
* Idempotent — runs at most once per process.
|
|
21
|
+
*/
|
|
22
|
+
export declare function assertRegistryComplete(): void;
|
|
23
|
+
/** Return the writer for (kind, agent), or undefined if unsupported. */
|
|
24
|
+
export declare function getWriter<K extends ResourceKind>(kind: K, agent: AgentId): ResourceWriter<SelectionFor<K>> | undefined;
|
|
25
|
+
/** Return the detector for (kind, agent), or undefined if unsupported. */
|
|
26
|
+
export declare function getDetector(kind: ResourceKind, agent: AgentId): ResourceDetector | undefined;
|