@swarmify/agents-cli 1.6.13 → 1.6.14
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/dist/commands/sync.d.ts +3 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +62 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/view.d.ts.map +1 -1
- package/dist/commands/view.js +11 -7
- package/dist/commands/view.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/agents.js +1 -0
- package/dist/lib/agents.js.map +1 -1
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +36 -25
- package/dist/lib/commands.js.map +1 -1
- package/dist/lib/convert.js +3 -3
- package/dist/lib/convert.js.map +1 -1
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +30 -20
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/mcp.d.ts +8 -3
- package/dist/lib/mcp.d.ts.map +1 -1
- package/dist/lib/mcp.js +31 -24
- package/dist/lib/mcp.js.map +1 -1
- package/dist/lib/memory.d.ts.map +1 -1
- package/dist/lib/memory.js +16 -6
- package/dist/lib/memory.js.map +1 -1
- package/dist/lib/resources.d.ts.map +1 -1
- package/dist/lib/resources.js +14 -2
- package/dist/lib/resources.js.map +1 -1
- package/dist/lib/shims.d.ts.map +1 -1
- package/dist/lib/shims.js +22 -0
- package/dist/lib/shims.js.map +1 -1
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +45 -26
- package/dist/lib/skills.js.map +1 -1
- package/dist/lib/state.d.ts +5 -0
- package/dist/lib/state.d.ts.map +1 -1
- package/dist/lib/state.js +32 -0
- package/dist/lib/state.js.map +1 -1
- package/dist/lib/types.d.ts +1 -0
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/versions.d.ts +8 -3
- package/dist/lib/versions.d.ts.map +1 -1
- package/dist/lib/versions.js +188 -97
- package/dist/lib/versions.js.map +1 -1
- package/package.json +1 -1
package/dist/lib/versions.js
CHANGED
|
@@ -6,7 +6,7 @@ import { promisify } from 'util';
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import * as TOML from 'smol-toml';
|
|
8
8
|
import { checkbox, select } from '@inquirer/prompts';
|
|
9
|
-
import { getVersionsDir, ensureAgentsDir, readMeta, writeMeta, getCommandsDir, getSkillsDir, getHooksDir, getMemoryDir,
|
|
9
|
+
import { getVersionsDir, ensureAgentsDir, readMeta, writeMeta, getCommandsDir, getSkillsDir, getHooksDir, getMemoryDir, clearVersionResources, recordVersionResources, getProjectAgentsDir } from './state.js';
|
|
10
10
|
import { AGENTS, getAccountEmail, MCP_CAPABLE_AGENTS, COMMANDS_CAPABLE_AGENTS, getMcpConfigPathForHome, parseMcpConfig } from './agents.js';
|
|
11
11
|
import { applyPermissionsToVersion as applyPermsToVersion, PERMISSIONS_CAPABLE_AGENTS, discoverPermissionGroups, buildPermissionsFromGroups, CODEX_RULES_FILENAME } from './permissions.js';
|
|
12
12
|
import { installMcpServers } from './mcp.js';
|
|
@@ -20,7 +20,7 @@ const execAsync = promisify(exec);
|
|
|
20
20
|
/**
|
|
21
21
|
* Get all available resources from ~/.agents/.
|
|
22
22
|
*/
|
|
23
|
-
export function getAvailableResources() {
|
|
23
|
+
export function getAvailableResources(cwd = process.cwd()) {
|
|
24
24
|
const result = {
|
|
25
25
|
commands: [],
|
|
26
26
|
skills: [],
|
|
@@ -31,60 +31,114 @@ export function getAvailableResources() {
|
|
|
31
31
|
subagents: [],
|
|
32
32
|
plugins: [],
|
|
33
33
|
};
|
|
34
|
+
const projectAgentsDir = getProjectAgentsDir(cwd);
|
|
35
|
+
const userBase = path.dirname(getCommandsDir());
|
|
36
|
+
const resourceBases = [];
|
|
37
|
+
if (projectAgentsDir) {
|
|
38
|
+
resourceBases.push({ scope: 'project', base: projectAgentsDir });
|
|
39
|
+
}
|
|
40
|
+
resourceBases.push({ scope: 'user', base: userBase });
|
|
34
41
|
// Commands (*.md files)
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
const commandNames = new Set();
|
|
43
|
+
for (const { base } of resourceBases) {
|
|
44
|
+
const commandsDir = path.join(base, 'commands');
|
|
45
|
+
if (!fs.existsSync(commandsDir))
|
|
46
|
+
continue;
|
|
47
|
+
const names = fs.readdirSync(commandsDir)
|
|
38
48
|
.filter(f => f.endsWith('.md'))
|
|
39
49
|
.map(f => f.replace(/\.md$/, ''));
|
|
50
|
+
for (const name of names) {
|
|
51
|
+
commandNames.add(name);
|
|
52
|
+
}
|
|
40
53
|
}
|
|
54
|
+
result.commands = Array.from(commandNames);
|
|
41
55
|
// Skills (directories, excluding hidden)
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
const skillNames = new Set();
|
|
57
|
+
for (const { base } of resourceBases) {
|
|
58
|
+
const skillsDir = path.join(base, 'skills');
|
|
59
|
+
if (!fs.existsSync(skillsDir))
|
|
60
|
+
continue;
|
|
61
|
+
const names = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
45
62
|
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
46
63
|
.map(d => d.name);
|
|
64
|
+
for (const name of names) {
|
|
65
|
+
skillNames.add(name);
|
|
66
|
+
}
|
|
47
67
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
result.skills = Array.from(skillNames);
|
|
69
|
+
// Hooks (files)
|
|
70
|
+
const hookNames = new Set();
|
|
71
|
+
for (const { base } of resourceBases) {
|
|
72
|
+
const hooksDir = path.join(base, 'hooks');
|
|
73
|
+
if (!fs.existsSync(hooksDir))
|
|
74
|
+
continue;
|
|
75
|
+
const names = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
|
|
76
|
+
for (const name of names) {
|
|
77
|
+
hookNames.add(name);
|
|
78
|
+
}
|
|
53
79
|
}
|
|
80
|
+
result.hooks = Array.from(hookNames);
|
|
54
81
|
// Memory (*.md files, excluding symlinks)
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
const memoryNames = new Set();
|
|
83
|
+
for (const { base } of resourceBases) {
|
|
84
|
+
const memoryDir = path.join(base, 'memory');
|
|
85
|
+
if (!fs.existsSync(memoryDir))
|
|
86
|
+
continue;
|
|
87
|
+
const names = fs.readdirSync(memoryDir)
|
|
58
88
|
.filter(f => {
|
|
59
89
|
if (!f.endsWith('.md'))
|
|
60
90
|
return false;
|
|
61
|
-
// Skip symlinks (e.g., CLAUDE.md -> AGENTS.md)
|
|
62
91
|
const stat = fs.lstatSync(path.join(memoryDir, f));
|
|
63
92
|
return !stat.isSymbolicLink();
|
|
64
93
|
})
|
|
65
94
|
.map(f => f.replace(/\.md$/, ''));
|
|
95
|
+
for (const name of names) {
|
|
96
|
+
memoryNames.add(name);
|
|
97
|
+
}
|
|
66
98
|
}
|
|
99
|
+
result.memory = Array.from(memoryNames);
|
|
67
100
|
// MCP servers (*.yaml files)
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
101
|
+
const mcpNames = new Set();
|
|
102
|
+
for (const { base } of resourceBases) {
|
|
103
|
+
const mcpDir = path.join(base, 'mcp');
|
|
104
|
+
if (!fs.existsSync(mcpDir))
|
|
105
|
+
continue;
|
|
106
|
+
const names = fs.readdirSync(mcpDir)
|
|
71
107
|
.filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
|
|
72
108
|
.map(f => f.replace(/\.(yaml|yml)$/, ''));
|
|
109
|
+
for (const name of names) {
|
|
110
|
+
mcpNames.add(name);
|
|
111
|
+
}
|
|
73
112
|
}
|
|
113
|
+
result.mcp = Array.from(mcpNames);
|
|
74
114
|
// Permission groups (from permissions/groups/*.yaml)
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
115
|
+
const permissionNames = new Set();
|
|
116
|
+
for (const { base } of resourceBases) {
|
|
117
|
+
const permsGroupsDir = path.join(base, 'permissions', 'groups');
|
|
118
|
+
if (!fs.existsSync(permsGroupsDir))
|
|
119
|
+
continue;
|
|
120
|
+
const names = fs.readdirSync(permsGroupsDir)
|
|
78
121
|
.filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))
|
|
79
122
|
.map(f => f.replace(/\.(yaml|yml)$/, ''));
|
|
123
|
+
for (const name of names) {
|
|
124
|
+
permissionNames.add(name);
|
|
125
|
+
}
|
|
80
126
|
}
|
|
127
|
+
result.permissions = Array.from(permissionNames);
|
|
81
128
|
// Subagents (directories with AGENT.md)
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
129
|
+
const subagentNames = new Set();
|
|
130
|
+
for (const { base } of resourceBases) {
|
|
131
|
+
const subagentsDir = path.join(base, 'subagents');
|
|
132
|
+
if (!fs.existsSync(subagentsDir))
|
|
133
|
+
continue;
|
|
134
|
+
const names = fs.readdirSync(subagentsDir, { withFileTypes: true })
|
|
85
135
|
.filter(d => d.isDirectory() && fs.existsSync(path.join(subagentsDir, d.name, 'AGENT.md')))
|
|
86
136
|
.map(d => d.name);
|
|
137
|
+
for (const name of names) {
|
|
138
|
+
subagentNames.add(name);
|
|
139
|
+
}
|
|
87
140
|
}
|
|
141
|
+
result.subagents = Array.from(subagentNames);
|
|
88
142
|
// Plugins (directories with .claude-plugin/plugin.json)
|
|
89
143
|
const allPlugins = discoverPlugins();
|
|
90
144
|
result.plugins = allPlugins.map(p => p.name);
|
|
@@ -119,10 +173,11 @@ function skillDirsMatch(src, dest) {
|
|
|
119
173
|
* Get what's ACTUALLY synced to a version by inspecting the version home.
|
|
120
174
|
* This is the source of truth - not the tracking in agents.yaml.
|
|
121
175
|
*/
|
|
122
|
-
export function getActuallySyncedResources(agent, version) {
|
|
176
|
+
export function getActuallySyncedResources(agent, version, options = {}) {
|
|
123
177
|
const agentConfig = AGENTS[agent];
|
|
124
178
|
const versionHome = path.join(getVersionsDir(), agent, version, 'home');
|
|
125
179
|
const configDir = path.join(versionHome, `.${agent}`);
|
|
180
|
+
const projectAgentsDir = getProjectAgentsDir(options.cwd || process.cwd());
|
|
126
181
|
const result = {
|
|
127
182
|
commands: [],
|
|
128
183
|
skills: [],
|
|
@@ -144,20 +199,23 @@ export function getActuallySyncedResources(agent, version) {
|
|
|
144
199
|
// Skills - check what directories exist AND content matches central source
|
|
145
200
|
const skillsDir = path.join(configDir, 'skills');
|
|
146
201
|
const centralSkillsDir = getSkillsDir();
|
|
202
|
+
const projectSkillsDir = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
|
|
147
203
|
if (fs.existsSync(skillsDir)) {
|
|
148
204
|
const installedSkills = fs.readdirSync(skillsDir, { withFileTypes: true })
|
|
149
205
|
.filter(d => d.isDirectory() && !d.name.startsWith('.'))
|
|
150
206
|
.map(d => d.name);
|
|
151
207
|
for (const skill of installedSkills) {
|
|
152
|
-
const centralSkillDir = path.join(centralSkillsDir, skill);
|
|
153
208
|
const versionSkillDir = path.join(skillsDir, skill);
|
|
154
|
-
|
|
155
|
-
|
|
209
|
+
const projectSourceDir = projectSkillsDir ? path.join(projectSkillsDir, skill) : null;
|
|
210
|
+
const centralSkillDir = path.join(centralSkillsDir, skill);
|
|
211
|
+
const hasProjectSource = projectSourceDir ? fs.existsSync(projectSourceDir) : false;
|
|
212
|
+
const hasUserSource = fs.existsSync(centralSkillDir);
|
|
213
|
+
if (!hasProjectSource && !hasUserSource) {
|
|
156
214
|
result.skills.push(skill);
|
|
157
215
|
continue;
|
|
158
216
|
}
|
|
159
|
-
|
|
160
|
-
const allMatch = skillDirsMatch(
|
|
217
|
+
const sourceDir = hasProjectSource ? projectSourceDir : centralSkillDir;
|
|
218
|
+
const allMatch = skillDirsMatch(sourceDir, versionSkillDir);
|
|
161
219
|
if (allMatch) {
|
|
162
220
|
result.skills.push(skill);
|
|
163
221
|
}
|
|
@@ -166,19 +224,22 @@ export function getActuallySyncedResources(agent, version) {
|
|
|
166
224
|
// Hooks - check what files exist AND content matches central source
|
|
167
225
|
const hooksDir = path.join(configDir, 'hooks');
|
|
168
226
|
const centralHooksDir = getHooksDir();
|
|
227
|
+
const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
|
|
169
228
|
if (fs.existsSync(hooksDir)) {
|
|
170
229
|
const installedHooks = fs.readdirSync(hooksDir).filter(f => !f.startsWith('.'));
|
|
171
230
|
for (const hook of installedHooks) {
|
|
231
|
+
const projectFile = projectHooksDir ? path.join(projectHooksDir, hook) : null;
|
|
172
232
|
const centralFile = path.join(centralHooksDir, hook);
|
|
173
233
|
const versionFile = path.join(hooksDir, hook);
|
|
174
|
-
|
|
175
|
-
|
|
234
|
+
const hasProject = projectFile ? fs.existsSync(projectFile) : false;
|
|
235
|
+
const hasCentral = fs.existsSync(centralFile);
|
|
236
|
+
const sourceFile = hasProject ? projectFile : centralFile;
|
|
237
|
+
if (!hasProject && !hasCentral) {
|
|
176
238
|
result.hooks.push(hook);
|
|
177
239
|
continue;
|
|
178
240
|
}
|
|
179
|
-
// Content-match: version hook must match central hook
|
|
180
241
|
try {
|
|
181
|
-
const centralContent = fs.readFileSync(
|
|
242
|
+
const centralContent = fs.readFileSync(sourceFile, 'utf-8');
|
|
182
243
|
const versionContent = fs.readFileSync(versionFile, 'utf-8');
|
|
183
244
|
if (centralContent === versionContent) {
|
|
184
245
|
result.hooks.push(hook);
|
|
@@ -191,22 +252,39 @@ export function getActuallySyncedResources(agent, version) {
|
|
|
191
252
|
}
|
|
192
253
|
// Memory - check which memory files are actually in sync (content matches)
|
|
193
254
|
const memoryDir = getMemoryDir();
|
|
255
|
+
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, 'memory') : null;
|
|
256
|
+
const memoryFiles = new Set();
|
|
194
257
|
if (fs.existsSync(memoryDir)) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
258
|
+
fs.readdirSync(memoryDir).filter(f => f.endsWith('.md')).forEach(f => memoryFiles.add(f));
|
|
259
|
+
}
|
|
260
|
+
if (projectMemoryDir && fs.existsSync(projectMemoryDir)) {
|
|
261
|
+
fs.readdirSync(projectMemoryDir).filter(f => f.endsWith('.md')).forEach(f => memoryFiles.add(f));
|
|
262
|
+
}
|
|
263
|
+
for (const file of memoryFiles) {
|
|
264
|
+
const memName = file.replace(/\.md$/, '');
|
|
265
|
+
const targetName = file === 'AGENTS.md' ? agentConfig.instructionsFile : file;
|
|
266
|
+
const versionFile = path.join(configDir, targetName);
|
|
267
|
+
if (!fs.existsSync(versionFile))
|
|
268
|
+
continue;
|
|
269
|
+
const projectFile = projectMemoryDir ? path.join(projectMemoryDir, file) : null;
|
|
270
|
+
const centralFile = path.join(memoryDir, file);
|
|
271
|
+
const hasProject = projectFile ? fs.existsSync(projectFile) : false;
|
|
272
|
+
const hasCentral = fs.existsSync(centralFile);
|
|
273
|
+
const sourceFile = hasProject ? projectFile : centralFile;
|
|
274
|
+
if (!hasProject && !hasCentral) {
|
|
275
|
+
result.memory.push(memName);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
const centralContent = fs.readFileSync(sourceFile, 'utf-8');
|
|
280
|
+
const versionContent = fs.readFileSync(versionFile, 'utf-8');
|
|
281
|
+
if (centralContent === versionContent) {
|
|
282
|
+
result.memory.push(memName);
|
|
208
283
|
}
|
|
209
284
|
}
|
|
285
|
+
catch {
|
|
286
|
+
// Ignore
|
|
287
|
+
}
|
|
210
288
|
}
|
|
211
289
|
// MCP - use canonical config path + parser per agent
|
|
212
290
|
if (MCP_CAPABLE_AGENTS.includes(agent)) {
|
|
@@ -1006,19 +1084,21 @@ export function getResourceDiff(agent, version) {
|
|
|
1006
1084
|
diff.commands.dangling = ['commands/'];
|
|
1007
1085
|
}
|
|
1008
1086
|
}
|
|
1009
|
-
// Skills: check directory symlink
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1087
|
+
// Skills: check directory symlink (skip if agent natively reads ~/.agents/skills/)
|
|
1088
|
+
if (!agentConfig.nativeAgentsSkillsDir) {
|
|
1089
|
+
const centralSkills = getSkillsDir();
|
|
1090
|
+
const skillsTarget = path.join(agentDir, 'skills');
|
|
1091
|
+
const skillsStatus = getSymlinkStatus(skillsTarget);
|
|
1092
|
+
if (skillsStatus === 'none' && fs.existsSync(centralSkills)) {
|
|
1093
|
+
const dirs = fs.readdirSync(centralSkills).filter(f => {
|
|
1094
|
+
const stat = fs.statSync(path.join(centralSkills, f));
|
|
1095
|
+
return stat.isDirectory() && !f.startsWith('.');
|
|
1096
|
+
});
|
|
1097
|
+
diff.skills.added = dirs;
|
|
1098
|
+
}
|
|
1099
|
+
else if (skillsStatus === 'dangling') {
|
|
1100
|
+
diff.skills.dangling = ['skills/'];
|
|
1101
|
+
}
|
|
1022
1102
|
}
|
|
1023
1103
|
// Hooks: check directory symlink (if agent supports hooks)
|
|
1024
1104
|
if (agentConfig.supportsHooks) {
|
|
@@ -1066,13 +1146,15 @@ export function getResourceDiff(agent, version) {
|
|
|
1066
1146
|
*
|
|
1067
1147
|
* For Gemini: commands are converted from markdown to TOML.
|
|
1068
1148
|
*/
|
|
1069
|
-
export function syncResourcesToVersion(agent, version, selection) {
|
|
1149
|
+
export function syncResourcesToVersion(agent, version, selection, options = {}) {
|
|
1070
1150
|
const agentConfig = AGENTS[agent];
|
|
1071
1151
|
const versionHome = getVersionHomePath(agent, version);
|
|
1072
1152
|
const agentDir = path.join(versionHome, `.${agent}`);
|
|
1073
1153
|
fs.mkdirSync(agentDir, { recursive: true });
|
|
1074
1154
|
const result = { commands: false, skills: false, hooks: false, memory: [], permissions: false, mcp: [], subagents: [], plugins: [] };
|
|
1075
|
-
const
|
|
1155
|
+
const cwd = options.cwd || process.cwd();
|
|
1156
|
+
const projectAgentsDir = options.projectDir || getProjectAgentsDir(cwd);
|
|
1157
|
+
const available = getAvailableResources(cwd);
|
|
1076
1158
|
// Helper: remove a path (symlink or real) if it exists
|
|
1077
1159
|
const removePath = (p) => {
|
|
1078
1160
|
try {
|
|
@@ -1115,22 +1197,22 @@ export function syncResourcesToVersion(agent, version, selection) {
|
|
|
1115
1197
|
: available.commands; // No selection = sync all
|
|
1116
1198
|
if (commandsToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
1117
1199
|
const centralCommands = getCommandsDir();
|
|
1200
|
+
const projectCommandsDir = projectAgentsDir ? path.join(projectAgentsDir, 'commands') : null;
|
|
1118
1201
|
const commandsTarget = path.join(agentDir, agentConfig.commandsSubdir);
|
|
1119
|
-
// Don't remove existing - just ensure dir exists and add/overwrite selected items
|
|
1120
1202
|
fs.mkdirSync(commandsTarget, { recursive: true });
|
|
1121
1203
|
const syncedCommands = [];
|
|
1122
1204
|
for (const cmd of commandsToSync) {
|
|
1123
|
-
const
|
|
1205
|
+
const projectSource = projectCommandsDir ? path.join(projectCommandsDir, `${cmd}.md`) : null;
|
|
1206
|
+
const userSource = path.join(centralCommands, `${cmd}.md`);
|
|
1207
|
+
const srcFile = projectSource && fs.existsSync(projectSource) ? projectSource : userSource;
|
|
1124
1208
|
if (!fs.existsSync(srcFile))
|
|
1125
1209
|
continue;
|
|
1126
1210
|
if (agentConfig.format === 'toml') {
|
|
1127
|
-
// Gemini: convert markdown to TOML
|
|
1128
1211
|
const content = fs.readFileSync(srcFile, 'utf-8');
|
|
1129
1212
|
const tomlContent = markdownToToml(cmd, content);
|
|
1130
1213
|
fs.writeFileSync(path.join(commandsTarget, `${cmd}.toml`), tomlContent);
|
|
1131
1214
|
}
|
|
1132
1215
|
else {
|
|
1133
|
-
// Copy markdown file
|
|
1134
1216
|
fs.copyFileSync(srcFile, path.join(commandsTarget, `${cmd}.md`));
|
|
1135
1217
|
}
|
|
1136
1218
|
syncedCommands.push(cmd);
|
|
@@ -1140,29 +1222,36 @@ export function syncResourcesToVersion(agent, version, selection) {
|
|
|
1140
1222
|
recordVersionResources(agent, version, 'commands', syncedCommands);
|
|
1141
1223
|
}
|
|
1142
1224
|
}
|
|
1143
|
-
// Sync skills
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
: available.skills;
|
|
1147
|
-
if (skillsToSync.length > 0) {
|
|
1148
|
-
const centralSkills = getSkillsDir();
|
|
1225
|
+
// Sync skills (skip if agent natively reads ~/.agents/skills/)
|
|
1226
|
+
if (agentConfig.nativeAgentsSkillsDir) {
|
|
1227
|
+
// Clean up stale skills symlink/dir — agent reads from ~/.agents/skills/ directly
|
|
1149
1228
|
const skillsTarget = path.join(agentDir, 'skills');
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
const
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
syncedSkills
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1229
|
+
removePath(skillsTarget);
|
|
1230
|
+
}
|
|
1231
|
+
else {
|
|
1232
|
+
const skillsToSync = selection
|
|
1233
|
+
? resolveSelection(selection.skills, available.skills)
|
|
1234
|
+
: available.skills;
|
|
1235
|
+
if (skillsToSync.length > 0) {
|
|
1236
|
+
const centralSkills = getSkillsDir();
|
|
1237
|
+
const projectSkills = projectAgentsDir ? path.join(projectAgentsDir, 'skills') : null;
|
|
1238
|
+
const skillsTarget = path.join(agentDir, 'skills');
|
|
1239
|
+
fs.mkdirSync(skillsTarget, { recursive: true });
|
|
1240
|
+
const syncedSkills = [];
|
|
1241
|
+
for (const skill of skillsToSync) {
|
|
1242
|
+
const projectSource = projectSkills ? path.join(projectSkills, skill) : null;
|
|
1243
|
+
const srcDir = projectSource && fs.existsSync(projectSource) ? projectSource : path.join(centralSkills, skill);
|
|
1244
|
+
if (!fs.existsSync(srcDir))
|
|
1245
|
+
continue;
|
|
1246
|
+
const destDir = path.join(skillsTarget, skill);
|
|
1247
|
+
removePath(destDir);
|
|
1248
|
+
copyDir(srcDir, destDir);
|
|
1249
|
+
syncedSkills.push(skill);
|
|
1250
|
+
}
|
|
1251
|
+
result.skills = syncedSkills.length > 0;
|
|
1252
|
+
if (syncedSkills.length > 0) {
|
|
1253
|
+
recordVersionResources(agent, version, 'skills', syncedSkills);
|
|
1254
|
+
}
|
|
1166
1255
|
}
|
|
1167
1256
|
}
|
|
1168
1257
|
// Sync hooks (if agent supports them)
|
|
@@ -1172,17 +1261,17 @@ export function syncResourcesToVersion(agent, version, selection) {
|
|
|
1172
1261
|
: available.hooks;
|
|
1173
1262
|
if (hooksToSync.length > 0) {
|
|
1174
1263
|
const centralHooks = getHooksDir();
|
|
1264
|
+
const projectHooksDir = projectAgentsDir ? path.join(projectAgentsDir, 'hooks') : null;
|
|
1175
1265
|
const hooksTarget = path.join(agentDir, 'hooks');
|
|
1176
|
-
// Don't remove existing - just ensure dir exists and add/overwrite selected items
|
|
1177
1266
|
fs.mkdirSync(hooksTarget, { recursive: true });
|
|
1178
1267
|
const syncedHooks = [];
|
|
1179
1268
|
for (const hook of hooksToSync) {
|
|
1180
|
-
const
|
|
1269
|
+
const projectSource = projectHooksDir ? path.join(projectHooksDir, hook) : null;
|
|
1270
|
+
const srcFile = projectSource && fs.existsSync(projectSource) ? projectSource : path.join(centralHooks, hook);
|
|
1181
1271
|
if (!fs.existsSync(srcFile))
|
|
1182
1272
|
continue;
|
|
1183
1273
|
const destFile = path.join(hooksTarget, hook);
|
|
1184
1274
|
fs.copyFileSync(srcFile, destFile);
|
|
1185
|
-
// Preserve executable permission
|
|
1186
1275
|
fs.chmodSync(destFile, 0o755);
|
|
1187
1276
|
syncedHooks.push(hook);
|
|
1188
1277
|
}
|
|
@@ -1190,7 +1279,6 @@ export function syncResourcesToVersion(agent, version, selection) {
|
|
|
1190
1279
|
if (syncedHooks.length > 0) {
|
|
1191
1280
|
recordVersionResources(agent, version, 'hooks', syncedHooks);
|
|
1192
1281
|
}
|
|
1193
|
-
// Register hooks as lifecycle events in settings.json
|
|
1194
1282
|
if (agent === 'claude') {
|
|
1195
1283
|
registerHooksToSettings(agent, versionHome);
|
|
1196
1284
|
}
|
|
@@ -1202,12 +1290,15 @@ export function syncResourcesToVersion(agent, version, selection) {
|
|
|
1202
1290
|
: available.memory;
|
|
1203
1291
|
if (memoryToSync.length > 0 && COMMANDS_CAPABLE_AGENTS.includes(agent)) {
|
|
1204
1292
|
const centralMemory = getMemoryDir();
|
|
1293
|
+
const projectMemoryDir = projectAgentsDir ? path.join(projectAgentsDir, 'memory') : null;
|
|
1205
1294
|
const syncedMemory = [];
|
|
1206
1295
|
for (const mem of memoryToSync) {
|
|
1207
|
-
const
|
|
1296
|
+
const projectSource = projectMemoryDir ? path.join(projectMemoryDir, `${mem}.md`) : null;
|
|
1297
|
+
const srcFile = projectSource && fs.existsSync(projectSource)
|
|
1298
|
+
? projectSource
|
|
1299
|
+
: path.join(centralMemory, `${mem}.md`);
|
|
1208
1300
|
if (!fs.existsSync(srcFile))
|
|
1209
1301
|
continue;
|
|
1210
|
-
// AGENTS.md gets renamed to the agent's instructionsFile name
|
|
1211
1302
|
const targetName = mem === 'AGENTS' ? agentConfig.instructionsFile : `${mem}.md`;
|
|
1212
1303
|
const destFile = path.join(agentDir, targetName);
|
|
1213
1304
|
removePath(destFile);
|
|
@@ -1244,7 +1335,7 @@ export function syncResourcesToVersion(agent, version, selection) {
|
|
|
1244
1335
|
? resolveSelection(selection.mcp, available.mcp)
|
|
1245
1336
|
: (MCP_CAPABLE_AGENTS.includes(agent) ? available.mcp : []);
|
|
1246
1337
|
if (mcpToSync.length > 0 && MCP_CAPABLE_AGENTS.includes(agent)) {
|
|
1247
|
-
const mcpResult = installMcpServers(agent, version, versionHome, mcpToSync);
|
|
1338
|
+
const mcpResult = installMcpServers(agent, version, versionHome, mcpToSync, { cwd });
|
|
1248
1339
|
result.mcp = mcpResult.applied;
|
|
1249
1340
|
if (mcpResult.applied.length > 0) {
|
|
1250
1341
|
recordVersionResources(agent, version, 'mcp', mcpResult.applied);
|