@intellectronica/ruler 0.3.41 → 0.3.43
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/README.md +135 -36
- package/dist/agents/AbstractAgent.d.ts +53 -0
- package/dist/agents/AbstractAgent.js +3 -2
- package/dist/agents/AgentsMdAgent.d.ts +14 -0
- package/dist/agents/AgentsMdAgent.js +3 -2
- package/dist/agents/AiderAgent.d.ts +14 -0
- package/dist/agents/AiderAgent.js +7 -4
- package/dist/agents/AmazonQCliAgent.d.ts +13 -0
- package/dist/agents/AmazonQCliAgent.js +6 -4
- package/dist/agents/AmpAgent.d.ts +6 -0
- package/dist/agents/AntigravityAgent.d.ts +10 -0
- package/dist/agents/AugmentCodeAgent.d.ts +13 -0
- package/dist/agents/AugmentCodeAgent.js +3 -2
- package/dist/agents/ClaudeAgent.d.ts +13 -0
- package/dist/agents/ClineAgent.d.ts +9 -0
- package/dist/agents/CodexCliAgent.d.ts +31 -0
- package/dist/agents/CodexCliAgent.js +1 -1
- package/dist/agents/CopilotAgent.d.ts +20 -0
- package/dist/agents/CrushAgent.d.ts +14 -0
- package/dist/agents/CrushAgent.js +18 -6
- package/dist/agents/CursorAgent.d.ts +17 -0
- package/dist/agents/FactoryDroidAgent.d.ts +13 -0
- package/dist/agents/FirebaseAgent.d.ts +11 -0
- package/dist/agents/FirebenderAgent.d.ts +36 -0
- package/dist/agents/FirebenderAgent.js +5 -4
- package/dist/agents/GeminiCliAgent.d.ts +12 -0
- package/dist/agents/GeminiCliAgent.js +13 -7
- package/dist/agents/GooseAgent.d.ts +12 -0
- package/dist/agents/IAgent.d.ts +74 -0
- package/dist/agents/JetBrainsAiAssistantAgent.d.ts +10 -0
- package/dist/agents/JulesAgent.d.ts +5 -0
- package/dist/agents/JunieAgent.d.ts +12 -0
- package/dist/agents/KiloCodeAgent.d.ts +14 -0
- package/dist/agents/KiroAgent.d.ts +8 -0
- package/dist/agents/MistralVibeAgent.d.ts +31 -0
- package/dist/agents/MistralVibeAgent.js +14 -3
- package/dist/agents/OpenCodeAgent.d.ts +11 -0
- package/dist/agents/OpenCodeAgent.js +24 -12
- package/dist/agents/OpenHandsAgent.d.ts +8 -0
- package/dist/agents/PiAgent.d.ts +9 -0
- package/dist/agents/QwenCodeAgent.d.ts +11 -0
- package/dist/agents/QwenCodeAgent.js +11 -5
- package/dist/agents/RooCodeAgent.d.ts +16 -0
- package/dist/agents/RooCodeAgent.js +3 -2
- package/dist/agents/TraeAgent.d.ts +10 -0
- package/dist/agents/WarpAgent.d.ts +12 -0
- package/dist/agents/WindsurfAgent.d.ts +13 -0
- package/dist/agents/ZedAgent.d.ts +21 -0
- package/dist/agents/ZedAgent.js +8 -5
- package/dist/agents/agent-utils.d.ts +5 -0
- package/dist/agents/agent-utils.js +8 -5
- package/dist/agents/index.d.ts +9 -0
- package/dist/cli/commands.d.ts +4 -0
- package/dist/cli/commands.js +1 -2
- package/dist/cli/handlers.d.ts +41 -0
- package/dist/cli/handlers.js +75 -59
- package/dist/cli/index.d.ts +2 -0
- package/dist/constants.d.ts +35 -0
- package/dist/constants.js +1 -1
- package/dist/core/ConfigLoader.d.ts +59 -0
- package/dist/core/ConfigLoader.js +178 -44
- package/dist/core/FileSystemUtils.d.ts +53 -0
- package/dist/core/FileSystemUtils.js +157 -20
- package/dist/core/GitignoreUtils.d.ts +25 -0
- package/dist/core/GitignoreUtils.js +94 -32
- package/dist/core/RuleProcessor.d.ts +8 -0
- package/dist/core/SkillsProcessor.d.ts +127 -0
- package/dist/core/SkillsProcessor.js +118 -223
- package/dist/core/SkillsUtils.d.ts +26 -0
- package/dist/core/SubagentsProcessor.d.ts +38 -0
- package/dist/core/SubagentsProcessor.js +8 -5
- package/dist/core/SubagentsUtils.d.ts +34 -0
- package/dist/core/UnifiedConfigLoader.d.ts +10 -0
- package/dist/core/UnifiedConfigLoader.js +115 -33
- package/dist/core/UnifiedConfigTypes.d.ts +97 -0
- package/dist/core/agent-selection.d.ts +12 -0
- package/dist/core/agent-selection.js +17 -7
- package/dist/core/apply-engine.d.ts +70 -0
- package/dist/core/apply-engine.js +88 -58
- package/dist/core/config-utils.d.ts +14 -0
- package/dist/core/config-utils.js +9 -3
- package/dist/core/hash.d.ts +2 -0
- package/dist/core/path-utils.d.ts +1 -0
- package/dist/core/path-utils.js +42 -0
- package/dist/core/revert-engine.d.ts +37 -0
- package/dist/core/revert-engine.js +142 -34
- package/dist/lib.d.ts +13 -0
- package/dist/lib.js +24 -8
- package/dist/mcp/capabilities.d.ts +20 -0
- package/dist/mcp/merge.d.ts +10 -0
- package/dist/mcp/merge.js +36 -16
- package/dist/mcp/propagateOpenCodeMcp.d.ts +2 -0
- package/dist/mcp/propagateOpenCodeMcp.js +30 -11
- package/dist/mcp/propagateOpenHandsMcp.d.ts +2 -0
- package/dist/mcp/propagateOpenHandsMcp.js +48 -21
- package/dist/mcp/validate.d.ts +7 -0
- package/dist/mcp/validate.js +6 -1
- package/dist/paths/mcp.d.ts +8 -0
- package/dist/paths/mcp.js +44 -8
- package/dist/revert.d.ts +6 -0
- package/dist/revert.js +58 -46
- package/dist/types.d.ts +87 -0
- package/dist/vscode/settings.d.ts +40 -0
- package/dist/vscode/settings.js +3 -3
- package/package.json +8 -5
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.discoverSkills = discoverSkills;
|
|
37
37
|
exports.getSkillsGitignorePaths = getSkillsGitignorePaths;
|
|
38
|
+
exports.replaceSkillsDirectory = replaceSkillsDirectory;
|
|
38
39
|
exports.propagateSkills = propagateSkills;
|
|
39
40
|
exports.propagateSkillsForClaude = propagateSkillsForClaude;
|
|
40
41
|
exports.propagateSkillsForCodex = propagateSkillsForCodex;
|
|
@@ -53,6 +54,7 @@ const path = __importStar(require("path"));
|
|
|
53
54
|
const fs = __importStar(require("fs/promises"));
|
|
54
55
|
const constants_1 = require("../constants");
|
|
55
56
|
const SkillsUtils_1 = require("./SkillsUtils");
|
|
57
|
+
const FileSystemUtils_1 = require("./FileSystemUtils");
|
|
56
58
|
/**
|
|
57
59
|
* Discovers skills in the project's .ruler/skills directory.
|
|
58
60
|
* Returns discovered skills and any validation warnings.
|
|
@@ -101,7 +103,8 @@ async function getSkillsGitignorePaths(projectRoot, agents) {
|
|
|
101
103
|
factory: FACTORY_SKILLS_PATH,
|
|
102
104
|
antigravity: ANTIGRAVITY_SKILLS_PATH,
|
|
103
105
|
};
|
|
104
|
-
|
|
106
|
+
const pathSet = new Set(Array.from(selectedTargets).map((target) => path.join(projectRoot, targetPaths[target])));
|
|
107
|
+
return Array.from(pathSet);
|
|
105
108
|
}
|
|
106
109
|
/**
|
|
107
110
|
* Module-level state to track if experimental warning has been shown.
|
|
@@ -126,7 +129,7 @@ const SKILL_TARGET_TO_IDENTIFIERS = new Map([
|
|
|
126
129
|
['codex', ['codex']],
|
|
127
130
|
['opencode', ['opencode']],
|
|
128
131
|
['pi', ['pi']],
|
|
129
|
-
['goose', ['goose', 'amp']],
|
|
132
|
+
['goose', ['goose', 'amp', 'zed']],
|
|
130
133
|
['vibe', ['mistral']],
|
|
131
134
|
['roo', ['roo']],
|
|
132
135
|
['gemini', ['gemini-cli']],
|
|
@@ -136,6 +139,21 @@ const SKILL_TARGET_TO_IDENTIFIERS = new Map([
|
|
|
136
139
|
['factory', ['factory']],
|
|
137
140
|
['antigravity', ['antigravity']],
|
|
138
141
|
]);
|
|
142
|
+
const SKILL_TARGET_PATHS = Array.from(new Set([
|
|
143
|
+
constants_1.CLAUDE_SKILLS_PATH,
|
|
144
|
+
constants_1.CODEX_SKILLS_PATH,
|
|
145
|
+
constants_1.OPENCODE_SKILLS_PATH,
|
|
146
|
+
constants_1.PI_SKILLS_PATH,
|
|
147
|
+
constants_1.GOOSE_SKILLS_PATH,
|
|
148
|
+
constants_1.VIBE_SKILLS_PATH,
|
|
149
|
+
constants_1.ROO_SKILLS_PATH,
|
|
150
|
+
constants_1.GEMINI_SKILLS_PATH,
|
|
151
|
+
constants_1.JUNIE_SKILLS_PATH,
|
|
152
|
+
constants_1.CURSOR_SKILLS_PATH,
|
|
153
|
+
constants_1.WINDSURF_SKILLS_PATH,
|
|
154
|
+
constants_1.FACTORY_SKILLS_PATH,
|
|
155
|
+
constants_1.ANTIGRAVITY_SKILLS_PATH,
|
|
156
|
+
]));
|
|
139
157
|
function getSelectedSkillTargets(agents) {
|
|
140
158
|
const selectedIdentifiers = new Set(agents
|
|
141
159
|
.filter((agent) => agent.supportsNativeSkills?.())
|
|
@@ -148,205 +166,82 @@ function getSelectedSkillTargets(agents) {
|
|
|
148
166
|
}
|
|
149
167
|
return targets;
|
|
150
168
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
const piSkillsPath = path.join(projectRoot, constants_1.PI_SKILLS_PATH);
|
|
160
|
-
const gooseSkillsPath = path.join(projectRoot, constants_1.GOOSE_SKILLS_PATH);
|
|
161
|
-
const vibeSkillsPath = path.join(projectRoot, constants_1.VIBE_SKILLS_PATH);
|
|
162
|
-
const rooSkillsPath = path.join(projectRoot, constants_1.ROO_SKILLS_PATH);
|
|
163
|
-
const geminiSkillsPath = path.join(projectRoot, constants_1.GEMINI_SKILLS_PATH);
|
|
164
|
-
const junieSkillsPath = path.join(projectRoot, constants_1.JUNIE_SKILLS_PATH);
|
|
165
|
-
const cursorSkillsPath = path.join(projectRoot, constants_1.CURSOR_SKILLS_PATH);
|
|
166
|
-
const windsurfSkillsPath = path.join(projectRoot, constants_1.WINDSURF_SKILLS_PATH);
|
|
167
|
-
const factorySkillsPath = path.join(projectRoot, constants_1.FACTORY_SKILLS_PATH);
|
|
168
|
-
const antigravitySkillsPath = path.join(projectRoot, constants_1.ANTIGRAVITY_SKILLS_PATH);
|
|
169
|
-
// Clean up .claude/skills
|
|
170
|
-
try {
|
|
171
|
-
await fs.access(claudeSkillsPath);
|
|
172
|
-
if (dryRun) {
|
|
173
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.CLAUDE_SKILLS_PATH}`, verbose, dryRun);
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
await fs.rm(claudeSkillsPath, { recursive: true, force: true });
|
|
177
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.CLAUDE_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
catch {
|
|
181
|
-
// Directory doesn't exist, nothing to clean
|
|
182
|
-
}
|
|
183
|
-
// Clean up .codex/skills
|
|
184
|
-
try {
|
|
185
|
-
await fs.access(codexSkillsPath);
|
|
186
|
-
if (dryRun) {
|
|
187
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.CODEX_SKILLS_PATH}`, verbose, dryRun);
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
await fs.rm(codexSkillsPath, { recursive: true, force: true });
|
|
191
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.CODEX_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
catch {
|
|
195
|
-
// Directory doesn't exist, nothing to clean
|
|
196
|
-
}
|
|
197
|
-
// Clean up .opencode/skills
|
|
198
|
-
try {
|
|
199
|
-
await fs.access(opencodeSkillsPath);
|
|
200
|
-
if (dryRun) {
|
|
201
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.OPENCODE_SKILLS_PATH}`, verbose, dryRun);
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
204
|
-
await fs.rm(opencodeSkillsPath, { recursive: true, force: true });
|
|
205
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.OPENCODE_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
catch {
|
|
209
|
-
// Directory doesn't exist, nothing to clean
|
|
210
|
-
}
|
|
211
|
-
// Clean up .pi/skills
|
|
212
|
-
try {
|
|
213
|
-
await fs.access(piSkillsPath);
|
|
214
|
-
if (dryRun) {
|
|
215
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.PI_SKILLS_PATH}`, verbose, dryRun);
|
|
216
|
-
}
|
|
217
|
-
else {
|
|
218
|
-
await fs.rm(piSkillsPath, { recursive: true, force: true });
|
|
219
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.PI_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
// Directory doesn't exist, nothing to clean
|
|
224
|
-
}
|
|
225
|
-
// Clean up .agents/skills
|
|
226
|
-
try {
|
|
227
|
-
await fs.access(gooseSkillsPath);
|
|
228
|
-
if (dryRun) {
|
|
229
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.GOOSE_SKILLS_PATH}`, verbose, dryRun);
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
await fs.rm(gooseSkillsPath, { recursive: true, force: true });
|
|
233
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.GOOSE_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
catch {
|
|
237
|
-
// Directory doesn't exist, nothing to clean
|
|
238
|
-
}
|
|
239
|
-
// Clean up .vibe/skills
|
|
240
|
-
try {
|
|
241
|
-
await fs.access(vibeSkillsPath);
|
|
242
|
-
if (dryRun) {
|
|
243
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.VIBE_SKILLS_PATH}`, verbose, dryRun);
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
await fs.rm(vibeSkillsPath, { recursive: true, force: true });
|
|
247
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.VIBE_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
catch {
|
|
251
|
-
// Directory doesn't exist, nothing to clean
|
|
252
|
-
}
|
|
253
|
-
// Clean up .roo/skills
|
|
254
|
-
try {
|
|
255
|
-
await fs.access(rooSkillsPath);
|
|
256
|
-
if (dryRun) {
|
|
257
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.ROO_SKILLS_PATH}`, verbose, dryRun);
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
await fs.rm(rooSkillsPath, { recursive: true, force: true });
|
|
261
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.ROO_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
catch {
|
|
265
|
-
// Directory doesn't exist, nothing to clean
|
|
266
|
-
}
|
|
267
|
-
// Clean up .gemini/skills
|
|
268
|
-
try {
|
|
269
|
-
await fs.access(geminiSkillsPath);
|
|
270
|
-
if (dryRun) {
|
|
271
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.GEMINI_SKILLS_PATH}`, verbose, dryRun);
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
await fs.rm(geminiSkillsPath, { recursive: true, force: true });
|
|
275
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.GEMINI_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
catch {
|
|
279
|
-
// Directory doesn't exist, nothing to clean
|
|
280
|
-
}
|
|
281
|
-
// Clean up .junie/skills
|
|
169
|
+
function isMissingPathError(error) {
|
|
170
|
+
return (typeof error === 'object' &&
|
|
171
|
+
error !== null &&
|
|
172
|
+
'code' in error &&
|
|
173
|
+
error.code === 'ENOENT');
|
|
174
|
+
}
|
|
175
|
+
async function cleanupSkillsDirectory(projectRoot, relativePath, dryRun, verbose) {
|
|
176
|
+
const targetPath = path.join(projectRoot, relativePath);
|
|
282
177
|
try {
|
|
283
|
-
await fs.access(
|
|
284
|
-
if (dryRun) {
|
|
285
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.JUNIE_SKILLS_PATH}`, verbose, dryRun);
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
await fs.rm(junieSkillsPath, { recursive: true, force: true });
|
|
289
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.JUNIE_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
catch {
|
|
293
|
-
// Directory doesn't exist, nothing to clean
|
|
178
|
+
await fs.access(targetPath);
|
|
294
179
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (dryRun) {
|
|
299
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.CURSOR_SKILLS_PATH}`, verbose, dryRun);
|
|
300
|
-
}
|
|
301
|
-
else {
|
|
302
|
-
await fs.rm(cursorSkillsPath, { recursive: true, force: true });
|
|
303
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.CURSOR_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
180
|
+
catch (error) {
|
|
181
|
+
if (isMissingPathError(error)) {
|
|
182
|
+
return;
|
|
304
183
|
}
|
|
184
|
+
throw error;
|
|
305
185
|
}
|
|
306
|
-
|
|
307
|
-
|
|
186
|
+
if (dryRun) {
|
|
187
|
+
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${relativePath}`, verbose, dryRun);
|
|
188
|
+
return;
|
|
308
189
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
await fs.rm(windsurfSkillsPath, { recursive: true, force: true });
|
|
317
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.WINDSURF_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
318
|
-
}
|
|
190
|
+
await (0, FileSystemUtils_1.assertManagedPathInsideRoot)(targetPath, projectRoot, 'Refusing to remove skills directory through symlinked path');
|
|
191
|
+
await fs.rm(targetPath, { recursive: true, force: true });
|
|
192
|
+
(0, constants_1.logVerboseInfo)(`Removed ${relativePath} (skills disabled)`, verbose, dryRun);
|
|
193
|
+
}
|
|
194
|
+
async function createTempSkillsDir(parentDir, containmentRoot) {
|
|
195
|
+
if (containmentRoot) {
|
|
196
|
+
await (0, FileSystemUtils_1.assertManagedPathInsideRoot)(parentDir, containmentRoot, 'Refusing to create temporary skills directory through symlinked path');
|
|
319
197
|
}
|
|
320
|
-
|
|
321
|
-
|
|
198
|
+
return fs.mkdtemp(path.join(parentDir, 'skills.tmp-'));
|
|
199
|
+
}
|
|
200
|
+
const TRANSIENT_RENAME_ERROR_CODES = new Set(['EPERM', 'EBUSY', 'ENOTEMPTY']);
|
|
201
|
+
const RENAME_RETRY_ATTEMPTS = 5;
|
|
202
|
+
const RENAME_RETRY_DELAY_MS = 50;
|
|
203
|
+
function isTransientRenameError(error) {
|
|
204
|
+
return (typeof error === 'object' &&
|
|
205
|
+
error !== null &&
|
|
206
|
+
'code' in error &&
|
|
207
|
+
typeof error.code === 'string' &&
|
|
208
|
+
TRANSIENT_RENAME_ERROR_CODES.has(error.code));
|
|
209
|
+
}
|
|
210
|
+
function wait(ms) {
|
|
211
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
212
|
+
}
|
|
213
|
+
async function replaceSkillsDirectory(tempDir, targetDir, fsOps = fs, containmentRoot) {
|
|
214
|
+
if (containmentRoot) {
|
|
215
|
+
await (0, FileSystemUtils_1.assertManagedPathInsideRoot)(targetDir, containmentRoot, 'Refusing to replace skills directory through symlinked path');
|
|
322
216
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
217
|
+
let lastError;
|
|
218
|
+
for (let attempt = 1; attempt <= RENAME_RETRY_ATTEMPTS; attempt += 1) {
|
|
219
|
+
try {
|
|
220
|
+
await fsOps.rename(tempDir, targetDir);
|
|
221
|
+
return;
|
|
328
222
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
(
|
|
223
|
+
catch (error) {
|
|
224
|
+
lastError = error;
|
|
225
|
+
if (!isTransientRenameError(error) || attempt === RENAME_RETRY_ATTEMPTS) {
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
await wait(RENAME_RETRY_DELAY_MS * attempt);
|
|
332
229
|
}
|
|
333
230
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
try {
|
|
339
|
-
await fs.access(antigravitySkillsPath);
|
|
340
|
-
if (dryRun) {
|
|
341
|
-
(0, constants_1.logVerboseInfo)(`DRY RUN: Would remove ${constants_1.ANTIGRAVITY_SKILLS_PATH}`, verbose, dryRun);
|
|
342
|
-
}
|
|
343
|
-
else {
|
|
344
|
-
await fs.rm(antigravitySkillsPath, { recursive: true, force: true });
|
|
345
|
-
(0, constants_1.logVerboseInfo)(`Removed ${constants_1.ANTIGRAVITY_SKILLS_PATH} (skills disabled)`, verbose, dryRun);
|
|
346
|
-
}
|
|
231
|
+
if (isTransientRenameError(lastError)) {
|
|
232
|
+
await fsOps.cp(tempDir, targetDir, { recursive: true, force: true });
|
|
233
|
+
await fsOps.rm(tempDir, { recursive: true, force: true });
|
|
234
|
+
return;
|
|
347
235
|
}
|
|
348
|
-
|
|
349
|
-
|
|
236
|
+
throw lastError;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Cleans up skills directories when skills are disabled.
|
|
240
|
+
* This ensures that stale skills from previous runs don't persist when skills are turned off.
|
|
241
|
+
*/
|
|
242
|
+
async function cleanupSkillsDirectories(projectRoot, dryRun, verbose) {
|
|
243
|
+
for (const skillPath of SKILL_TARGET_PATHS) {
|
|
244
|
+
await cleanupSkillsDirectory(projectRoot, skillPath, dryRun, verbose);
|
|
350
245
|
}
|
|
351
246
|
}
|
|
352
247
|
/**
|
|
@@ -416,7 +311,7 @@ async function propagateSkills(projectRoot, agents, skillsEnabled, verbose, dryR
|
|
|
416
311
|
await propagateSkillsForPi(projectRoot, { dryRun });
|
|
417
312
|
}
|
|
418
313
|
if (selectedTargets.has('goose')) {
|
|
419
|
-
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.GOOSE_SKILLS_PATH} for Goose and
|
|
314
|
+
(0, constants_1.logVerboseInfo)(`Copying skills to ${constants_1.GOOSE_SKILLS_PATH} for Goose, Amp and Zed`, verbose, dryRun);
|
|
420
315
|
await propagateSkillsForGoose(projectRoot, { dryRun });
|
|
421
316
|
}
|
|
422
317
|
if (selectedTargets.has('vibe')) {
|
|
@@ -476,7 +371,7 @@ async function propagateSkillsForClaude(projectRoot, options) {
|
|
|
476
371
|
// Ensure .claude directory exists
|
|
477
372
|
await fs.mkdir(claudeDir, { recursive: true });
|
|
478
373
|
// Use atomic replace: copy to temp, then rename
|
|
479
|
-
const tempDir =
|
|
374
|
+
const tempDir = await createTempSkillsDir(claudeDir, projectRoot);
|
|
480
375
|
try {
|
|
481
376
|
// Copy to temp directory
|
|
482
377
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -489,7 +384,7 @@ async function propagateSkillsForClaude(projectRoot, options) {
|
|
|
489
384
|
// Target didn't exist, that's fine
|
|
490
385
|
}
|
|
491
386
|
// Rename temp to target
|
|
492
|
-
await
|
|
387
|
+
await replaceSkillsDirectory(tempDir, claudeSkillsPath, fs, projectRoot);
|
|
493
388
|
}
|
|
494
389
|
catch (error) {
|
|
495
390
|
// Clean up temp directory on error
|
|
@@ -504,14 +399,14 @@ async function propagateSkillsForClaude(projectRoot, options) {
|
|
|
504
399
|
return [];
|
|
505
400
|
}
|
|
506
401
|
/**
|
|
507
|
-
* Propagates skills for OpenAI Codex CLI by copying .ruler/skills to .
|
|
402
|
+
* Propagates skills for OpenAI Codex CLI by copying .ruler/skills to .agents/skills.
|
|
508
403
|
* Uses atomic replace to ensure safe overwriting of existing skills.
|
|
509
404
|
* Returns dry-run steps if dryRun is true, otherwise returns empty array.
|
|
510
405
|
*/
|
|
511
406
|
async function propagateSkillsForCodex(projectRoot, options) {
|
|
512
407
|
const skillsDir = path.join(projectRoot, constants_1.RULER_SKILLS_PATH);
|
|
513
|
-
const
|
|
514
|
-
const
|
|
408
|
+
const agentsSkillsPath = path.join(projectRoot, constants_1.CODEX_SKILLS_PATH);
|
|
409
|
+
const agentsDir = path.dirname(agentsSkillsPath);
|
|
515
410
|
// Check if source skills directory exists
|
|
516
411
|
try {
|
|
517
412
|
await fs.access(skillsDir);
|
|
@@ -523,23 +418,23 @@ async function propagateSkillsForCodex(projectRoot, options) {
|
|
|
523
418
|
if (options.dryRun) {
|
|
524
419
|
return [`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.CODEX_SKILLS_PATH}`];
|
|
525
420
|
}
|
|
526
|
-
// Ensure .
|
|
527
|
-
await fs.mkdir(
|
|
421
|
+
// Ensure .agents directory exists
|
|
422
|
+
await fs.mkdir(agentsDir, { recursive: true });
|
|
528
423
|
// Use atomic replace: copy to temp, then rename
|
|
529
|
-
const tempDir =
|
|
424
|
+
const tempDir = await createTempSkillsDir(agentsDir, projectRoot);
|
|
530
425
|
try {
|
|
531
426
|
// Copy to temp directory
|
|
532
427
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
533
428
|
// Atomically replace the target
|
|
534
429
|
// First, remove existing target if it exists
|
|
535
430
|
try {
|
|
536
|
-
await fs.rm(
|
|
431
|
+
await fs.rm(agentsSkillsPath, { recursive: true, force: true });
|
|
537
432
|
}
|
|
538
433
|
catch {
|
|
539
434
|
// Target didn't exist, that's fine
|
|
540
435
|
}
|
|
541
436
|
// Rename temp to target
|
|
542
|
-
await
|
|
437
|
+
await replaceSkillsDirectory(tempDir, agentsSkillsPath, fs, projectRoot);
|
|
543
438
|
}
|
|
544
439
|
catch (error) {
|
|
545
440
|
// Clean up temp directory on error
|
|
@@ -576,7 +471,7 @@ async function propagateSkillsForOpenCode(projectRoot, options) {
|
|
|
576
471
|
// Ensure .opencode directory exists
|
|
577
472
|
await fs.mkdir(opencodeDir, { recursive: true });
|
|
578
473
|
// Use atomic replace: copy to temp, then rename
|
|
579
|
-
const tempDir =
|
|
474
|
+
const tempDir = await createTempSkillsDir(opencodeDir, projectRoot);
|
|
580
475
|
try {
|
|
581
476
|
// Copy to temp directory
|
|
582
477
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -589,7 +484,7 @@ async function propagateSkillsForOpenCode(projectRoot, options) {
|
|
|
589
484
|
// Target didn't exist, that's fine
|
|
590
485
|
}
|
|
591
486
|
// Rename temp to target
|
|
592
|
-
await
|
|
487
|
+
await replaceSkillsDirectory(tempDir, opencodeSkillsPath, fs, projectRoot);
|
|
593
488
|
}
|
|
594
489
|
catch (error) {
|
|
595
490
|
// Clean up temp directory on error
|
|
@@ -626,7 +521,7 @@ async function propagateSkillsForPi(projectRoot, options) {
|
|
|
626
521
|
// Ensure .pi directory exists
|
|
627
522
|
await fs.mkdir(piDir, { recursive: true });
|
|
628
523
|
// Use atomic replace: copy to temp, then rename
|
|
629
|
-
const tempDir =
|
|
524
|
+
const tempDir = await createTempSkillsDir(piDir, projectRoot);
|
|
630
525
|
try {
|
|
631
526
|
// Copy to temp directory
|
|
632
527
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -639,7 +534,7 @@ async function propagateSkillsForPi(projectRoot, options) {
|
|
|
639
534
|
// Target didn't exist, that's fine
|
|
640
535
|
}
|
|
641
536
|
// Rename temp to target
|
|
642
|
-
await
|
|
537
|
+
await replaceSkillsDirectory(tempDir, piSkillsPath, fs, projectRoot);
|
|
643
538
|
}
|
|
644
539
|
catch (error) {
|
|
645
540
|
// Clean up temp directory on error
|
|
@@ -676,7 +571,7 @@ async function propagateSkillsForGoose(projectRoot, options) {
|
|
|
676
571
|
// Ensure .agents directory exists
|
|
677
572
|
await fs.mkdir(gooseDir, { recursive: true });
|
|
678
573
|
// Use atomic replace: copy to temp, then rename
|
|
679
|
-
const tempDir =
|
|
574
|
+
const tempDir = await createTempSkillsDir(gooseDir, projectRoot);
|
|
680
575
|
try {
|
|
681
576
|
// Copy to temp directory
|
|
682
577
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -689,7 +584,7 @@ async function propagateSkillsForGoose(projectRoot, options) {
|
|
|
689
584
|
// Target didn't exist, that's fine
|
|
690
585
|
}
|
|
691
586
|
// Rename temp to target
|
|
692
|
-
await
|
|
587
|
+
await replaceSkillsDirectory(tempDir, gooseSkillsPath, fs, projectRoot);
|
|
693
588
|
}
|
|
694
589
|
catch (error) {
|
|
695
590
|
// Clean up temp directory on error
|
|
@@ -726,7 +621,7 @@ async function propagateSkillsForVibe(projectRoot, options) {
|
|
|
726
621
|
// Ensure .vibe directory exists
|
|
727
622
|
await fs.mkdir(vibeDir, { recursive: true });
|
|
728
623
|
// Use atomic replace: copy to temp, then rename
|
|
729
|
-
const tempDir =
|
|
624
|
+
const tempDir = await createTempSkillsDir(vibeDir, projectRoot);
|
|
730
625
|
try {
|
|
731
626
|
// Copy to temp directory
|
|
732
627
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -739,7 +634,7 @@ async function propagateSkillsForVibe(projectRoot, options) {
|
|
|
739
634
|
// Target didn't exist, that's fine
|
|
740
635
|
}
|
|
741
636
|
// Rename temp to target
|
|
742
|
-
await
|
|
637
|
+
await replaceSkillsDirectory(tempDir, vibeSkillsPath, fs, projectRoot);
|
|
743
638
|
}
|
|
744
639
|
catch (error) {
|
|
745
640
|
// Clean up temp directory on error
|
|
@@ -776,7 +671,7 @@ async function propagateSkillsForRoo(projectRoot, options) {
|
|
|
776
671
|
// Ensure .roo directory exists
|
|
777
672
|
await fs.mkdir(rooDir, { recursive: true });
|
|
778
673
|
// Use atomic replace: copy to temp, then rename
|
|
779
|
-
const tempDir =
|
|
674
|
+
const tempDir = await createTempSkillsDir(rooDir, projectRoot);
|
|
780
675
|
try {
|
|
781
676
|
// Copy to temp directory
|
|
782
677
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -789,7 +684,7 @@ async function propagateSkillsForRoo(projectRoot, options) {
|
|
|
789
684
|
// Target didn't exist, that's fine
|
|
790
685
|
}
|
|
791
686
|
// Rename temp to target
|
|
792
|
-
await
|
|
687
|
+
await replaceSkillsDirectory(tempDir, rooSkillsPath, fs, projectRoot);
|
|
793
688
|
}
|
|
794
689
|
catch (error) {
|
|
795
690
|
// Clean up temp directory on error
|
|
@@ -826,7 +721,7 @@ async function propagateSkillsForGemini(projectRoot, options) {
|
|
|
826
721
|
// Ensure .gemini directory exists
|
|
827
722
|
await fs.mkdir(geminiDir, { recursive: true });
|
|
828
723
|
// Use atomic replace: copy to temp, then rename
|
|
829
|
-
const tempDir =
|
|
724
|
+
const tempDir = await createTempSkillsDir(geminiDir, projectRoot);
|
|
830
725
|
try {
|
|
831
726
|
// Copy to temp directory
|
|
832
727
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -839,7 +734,7 @@ async function propagateSkillsForGemini(projectRoot, options) {
|
|
|
839
734
|
// Target didn't exist, that's fine
|
|
840
735
|
}
|
|
841
736
|
// Rename temp to target
|
|
842
|
-
await
|
|
737
|
+
await replaceSkillsDirectory(tempDir, geminiSkillsPath, fs, projectRoot);
|
|
843
738
|
}
|
|
844
739
|
catch (error) {
|
|
845
740
|
// Clean up temp directory on error
|
|
@@ -872,7 +767,7 @@ async function propagateSkillsForJunie(projectRoot, options) {
|
|
|
872
767
|
return [`Copy skills from ${constants_1.RULER_SKILLS_PATH} to ${constants_1.JUNIE_SKILLS_PATH}`];
|
|
873
768
|
}
|
|
874
769
|
await fs.mkdir(junieDir, { recursive: true });
|
|
875
|
-
const tempDir =
|
|
770
|
+
const tempDir = await createTempSkillsDir(junieDir, projectRoot);
|
|
876
771
|
try {
|
|
877
772
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
878
773
|
try {
|
|
@@ -881,7 +776,7 @@ async function propagateSkillsForJunie(projectRoot, options) {
|
|
|
881
776
|
catch {
|
|
882
777
|
// Target didn't exist, that's fine
|
|
883
778
|
}
|
|
884
|
-
await
|
|
779
|
+
await replaceSkillsDirectory(tempDir, junieSkillsPath, fs, projectRoot);
|
|
885
780
|
}
|
|
886
781
|
catch (error) {
|
|
887
782
|
try {
|
|
@@ -917,7 +812,7 @@ async function propagateSkillsForCursor(projectRoot, options) {
|
|
|
917
812
|
// Ensure .cursor directory exists
|
|
918
813
|
await fs.mkdir(cursorDir, { recursive: true });
|
|
919
814
|
// Use atomic replace: copy to temp, then rename
|
|
920
|
-
const tempDir =
|
|
815
|
+
const tempDir = await createTempSkillsDir(cursorDir, projectRoot);
|
|
921
816
|
try {
|
|
922
817
|
// Copy to temp directory
|
|
923
818
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -930,7 +825,7 @@ async function propagateSkillsForCursor(projectRoot, options) {
|
|
|
930
825
|
// Target didn't exist, that's fine
|
|
931
826
|
}
|
|
932
827
|
// Rename temp to target
|
|
933
|
-
await
|
|
828
|
+
await replaceSkillsDirectory(tempDir, cursorSkillsPath, fs, projectRoot);
|
|
934
829
|
}
|
|
935
830
|
catch (error) {
|
|
936
831
|
// Clean up temp directory on error
|
|
@@ -967,7 +862,7 @@ async function propagateSkillsForWindsurf(projectRoot, options) {
|
|
|
967
862
|
// Ensure .windsurf directory exists
|
|
968
863
|
await fs.mkdir(windsurfDir, { recursive: true });
|
|
969
864
|
// Use atomic replace: copy to temp, then rename
|
|
970
|
-
const tempDir =
|
|
865
|
+
const tempDir = await createTempSkillsDir(windsurfDir, projectRoot);
|
|
971
866
|
try {
|
|
972
867
|
// Copy to temp directory
|
|
973
868
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -980,7 +875,7 @@ async function propagateSkillsForWindsurf(projectRoot, options) {
|
|
|
980
875
|
// Target didn't exist, that's fine
|
|
981
876
|
}
|
|
982
877
|
// Rename temp to target
|
|
983
|
-
await
|
|
878
|
+
await replaceSkillsDirectory(tempDir, windsurfSkillsPath, fs, projectRoot);
|
|
984
879
|
}
|
|
985
880
|
catch (error) {
|
|
986
881
|
// Clean up temp directory on error
|
|
@@ -1017,7 +912,7 @@ async function propagateSkillsForFactory(projectRoot, options) {
|
|
|
1017
912
|
// Ensure .factory directory exists
|
|
1018
913
|
await fs.mkdir(factoryDir, { recursive: true });
|
|
1019
914
|
// Use atomic replace: copy to temp, then rename
|
|
1020
|
-
const tempDir =
|
|
915
|
+
const tempDir = await createTempSkillsDir(factoryDir, projectRoot);
|
|
1021
916
|
try {
|
|
1022
917
|
// Copy to temp directory
|
|
1023
918
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -1030,7 +925,7 @@ async function propagateSkillsForFactory(projectRoot, options) {
|
|
|
1030
925
|
// Target didn't exist, that's fine
|
|
1031
926
|
}
|
|
1032
927
|
// Rename temp to target
|
|
1033
|
-
await
|
|
928
|
+
await replaceSkillsDirectory(tempDir, factorySkillsPath, fs, projectRoot);
|
|
1034
929
|
}
|
|
1035
930
|
catch (error) {
|
|
1036
931
|
// Clean up temp directory on error
|
|
@@ -1069,7 +964,7 @@ async function propagateSkillsForAntigravity(projectRoot, options) {
|
|
|
1069
964
|
// Ensure .agent directory exists
|
|
1070
965
|
await fs.mkdir(antigravityDir, { recursive: true });
|
|
1071
966
|
// Use atomic replace: copy to temp, then rename
|
|
1072
|
-
const tempDir =
|
|
967
|
+
const tempDir = await createTempSkillsDir(antigravityDir, projectRoot);
|
|
1073
968
|
try {
|
|
1074
969
|
// Copy to temp directory
|
|
1075
970
|
await (0, SkillsUtils_1.copySkillsDirectory)(skillsDir, tempDir);
|
|
@@ -1082,7 +977,7 @@ async function propagateSkillsForAntigravity(projectRoot, options) {
|
|
|
1082
977
|
// Target didn't exist, that's fine
|
|
1083
978
|
}
|
|
1084
979
|
// Rename temp to target
|
|
1085
|
-
await
|
|
980
|
+
await replaceSkillsDirectory(tempDir, antigravitySkillsPath, fs, projectRoot);
|
|
1086
981
|
}
|
|
1087
982
|
catch (error) {
|
|
1088
983
|
// Clean up temp directory on error
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SkillInfo } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if a directory contains a SKILL.md file.
|
|
4
|
+
*/
|
|
5
|
+
export declare function hasSkillMd(dirPath: string): Promise<boolean>;
|
|
6
|
+
/**
|
|
7
|
+
* Checks if a directory is a grouping directory (contains subdirectories with SKILL.md).
|
|
8
|
+
*/
|
|
9
|
+
export declare function isGroupingDir(dirPath: string): Promise<boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* Walks the skills tree and discovers all skills.
|
|
12
|
+
* Returns skills and any validation warnings.
|
|
13
|
+
*/
|
|
14
|
+
export declare function walkSkillsTree(root: string): Promise<{
|
|
15
|
+
skills: SkillInfo[];
|
|
16
|
+
warnings: string[];
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Formats validation warnings for display.
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatValidationWarnings(warnings: string[]): string;
|
|
22
|
+
/**
|
|
23
|
+
* Copies the skills directory to the destination, preserving structure.
|
|
24
|
+
* Creates the destination directory if it doesn't exist.
|
|
25
|
+
*/
|
|
26
|
+
export declare function copySkillsDirectory(srcDir: string, destDir: string): Promise<void>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { SubagentInfo } from '../types';
|
|
2
|
+
import type { IAgent } from '../agents/IAgent';
|
|
3
|
+
/**
|
|
4
|
+
* Discovers subagent definitions in `.ruler/agents/`.
|
|
5
|
+
* Each `.md` file is parsed for YAML frontmatter (name, description, …).
|
|
6
|
+
* Files that fail validation are dropped from the returned list and
|
|
7
|
+
* reported via warnings.
|
|
8
|
+
*/
|
|
9
|
+
export declare function discoverSubagents(projectRoot: string): Promise<{
|
|
10
|
+
subagents: SubagentInfo[];
|
|
11
|
+
warnings: string[];
|
|
12
|
+
}>;
|
|
13
|
+
type SubagentTarget = 'claude' | 'cursor' | 'codex' | 'copilot';
|
|
14
|
+
/**
|
|
15
|
+
* Returns which native subagent targets are reachable through the supplied
|
|
16
|
+
* agent list. An agent only contributes to a target when it implements
|
|
17
|
+
* `supportsNativeSubagents()` returning true.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getSelectedSubagentTargets(agents: IAgent[]): Set<SubagentTarget>;
|
|
20
|
+
/**
|
|
21
|
+
* Returns absolute paths that subagent propagation may generate, for the
|
|
22
|
+
* supplied agents, used for `.gitignore` integration.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getSubagentsGitignorePaths(projectRoot: string, agents: IAgent[]): Promise<string[]>;
|
|
25
|
+
/**
|
|
26
|
+
* Test-only hook to reset the once-per-process experimental warning state.
|
|
27
|
+
*/
|
|
28
|
+
export declare function _resetExperimentalWarningForTests(): void;
|
|
29
|
+
interface PropagateOptions {
|
|
30
|
+
dryRun: boolean;
|
|
31
|
+
verbose?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare function propagateSubagentsForClaude(projectRoot: string, subagents: SubagentInfo[], options: PropagateOptions): Promise<string[]>;
|
|
34
|
+
export declare function propagateSubagentsForCursor(projectRoot: string, subagents: SubagentInfo[], options: PropagateOptions): Promise<string[]>;
|
|
35
|
+
export declare function propagateSubagentsForCodex(projectRoot: string, subagents: SubagentInfo[], options: PropagateOptions): Promise<string[]>;
|
|
36
|
+
export declare function propagateSubagentsForCopilot(projectRoot: string, subagents: SubagentInfo[], options: PropagateOptions): Promise<string[]>;
|
|
37
|
+
export declare function propagateSubagents(projectRoot: string, agents: IAgent[], subagentsEnabled: boolean, cleanupOrphaned: boolean, verbose: boolean, dryRun: boolean): Promise<void>;
|
|
38
|
+
export {};
|