@intellectronica/ruler 0.3.40 → 0.3.41
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 +20 -19
- package/dist/cli/commands.js +1 -1
- package/dist/cli/handlers.js +1 -1
- package/dist/core/ConfigLoader.js +17 -2
- package/dist/core/SubagentsProcessor.js +68 -22
- package/dist/lib.js +7 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -245,7 +245,7 @@ The `apply` command looks for `.ruler/` in the current directory tree, reading t
|
|
|
245
245
|
| `--no-backup` | Disable creation of `.bak` backup files. |
|
|
246
246
|
| `--skills` | Enable skills support (experimental, default: enabled). |
|
|
247
247
|
| `--no-skills` | Disable skills support. |
|
|
248
|
-
| `--subagents` | Enable subagents support (experimental, default:
|
|
248
|
+
| `--subagents` | Enable subagents support (experimental, default: disabled). |
|
|
249
249
|
| `--no-subagents` | Disable subagents support. |
|
|
250
250
|
| `--dry-run` | Preview changes without writing files. |
|
|
251
251
|
| `--local-only` | Skip `$XDG_CONFIG_HOME` when looking for configuration. |
|
|
@@ -740,18 +740,18 @@ Ruler can distribute named, delegatable **subagents** from a single source of tr
|
|
|
740
740
|
|
|
741
741
|
For agents with a native subagent primitive, Ruler writes one file per subagent into the target directory:
|
|
742
742
|
|
|
743
|
-
| Agent
|
|
744
|
-
|
|
|
745
|
-
| Claude Code
|
|
746
|
-
| Cursor
|
|
747
|
-
| OpenAI Codex CLI
|
|
748
|
-
| GitHub Copilot
|
|
743
|
+
| Agent | Target location | Format |
|
|
744
|
+
| ---------------- | ------------------------------------ | ---------------------------------------- |
|
|
745
|
+
| Claude Code | `.claude/agents/<relative-path>.md` | Markdown + YAML frontmatter |
|
|
746
|
+
| Cursor | `.cursor/agents/<relative-path>.md` | Markdown + YAML frontmatter |
|
|
747
|
+
| OpenAI Codex CLI | `.codex/agents/<relative-path>.toml` | TOML (one self-contained file per agent) |
|
|
748
|
+
| GitHub Copilot | `.github/agents/<relative-path>.md` | Markdown + YAML frontmatter |
|
|
749
749
|
|
|
750
750
|
Other agents (Windsurf, RooCode, Aider, Gemini CLI, …) do not yet have a comparable native subagent primitive and are skipped with a warning. Subagent propagation will be added when those agents ship a comparable file format.
|
|
751
751
|
|
|
752
752
|
### Source Format
|
|
753
753
|
|
|
754
|
-
Author each subagent as `.ruler/agents/<name>.md
|
|
754
|
+
Author each subagent as `.ruler/agents/<name>.md` (nested folders are supported and preserved in outputs):
|
|
755
755
|
|
|
756
756
|
```markdown
|
|
757
757
|
---
|
|
@@ -772,19 +772,19 @@ a structured verdict.
|
|
|
772
772
|
|
|
773
773
|
**Required frontmatter fields:**
|
|
774
774
|
|
|
775
|
-
| Field | Type | Notes
|
|
776
|
-
| ------------- | ------ |
|
|
775
|
+
| Field | Type | Notes |
|
|
776
|
+
| ------------- | ------ | -------------------------------------------------------------------------- |
|
|
777
777
|
| `name` | string | Must match the filename stem (`code-reviewer.md` → `name: code-reviewer`). |
|
|
778
|
-
| `description` | string | When the parent agent should delegate to this subagent.
|
|
778
|
+
| `description` | string | When the parent agent should delegate to this subagent. |
|
|
779
779
|
|
|
780
780
|
**Optional frontmatter fields:**
|
|
781
781
|
|
|
782
|
-
| Field | Type
|
|
783
|
-
| --------------- |
|
|
784
|
-
| `tools` | string[]
|
|
785
|
-
| `model` | string
|
|
786
|
-
| `readonly` | boolean
|
|
787
|
-
| `is_background` | boolean
|
|
782
|
+
| Field | Type | Used by | Default behavior |
|
|
783
|
+
| --------------- | -------- | ------------------------------------------------------------------------------- | -------------------------------------------------- |
|
|
784
|
+
| `tools` | string[] | Claude (verbatim), Copilot (mapped to aliases) | Cursor / Codex ignore; omitted if absent. |
|
|
785
|
+
| `model` | string | All four targets | Cursor defaults to `inherit`; others omit. |
|
|
786
|
+
| `readonly` | boolean | Cursor (verbatim), Codex (`sandbox_mode`), Copilot (`disable-model-invocation`) | Defaults to `false` for Cursor; omitted otherwise. |
|
|
787
|
+
| `is_background` | boolean | Cursor only | Defaults to `false` for Cursor. |
|
|
788
788
|
|
|
789
789
|
For GitHub Copilot, source `tools` (Claude vocabulary: `Read`, `Grep`, `Bash`, …) are translated to Copilot's aliases (`read`, `search`, `execute`, …). Tools that do not have a Copilot equivalent are dropped silently on a normal apply; pass `--verbose` (or use `--dry-run` to preview) to see which tools were dropped.
|
|
790
790
|
|
|
@@ -801,6 +801,7 @@ ruler apply --subagents # enable subagent propagation for one run
|
|
|
801
801
|
[agents]
|
|
802
802
|
enabled = true
|
|
803
803
|
# include_in_rules = true # also append .ruler/agents/*.md into top-level CLAUDE.md / AGENTS.md (default: false)
|
|
804
|
+
# cleanup_orphaned = true # allow ruler to delete stale native subagent dirs (default: false)
|
|
804
805
|
```
|
|
805
806
|
|
|
806
807
|
> **Note:** the previous release used `[subagents]` for these keys. `[subagents]` is still honored as a fallback with a deprecation warning, and will be removed in a future release. Please migrate to `[agents]`.
|
|
@@ -837,7 +838,7 @@ Use `--no-gitignore` to opt out.
|
|
|
837
838
|
|
|
838
839
|
### Cleanup
|
|
839
840
|
|
|
840
|
-
Subagent propagation does **not** currently have explicit `ruler revert` support.
|
|
841
|
+
Subagent propagation does **not** currently have explicit `ruler revert` support. By default, `ruler apply` is non-destructive and leaves existing native subagent directories untouched when subagents are disabled or missing. To allow automatic cleanup of stale generated directories, set `[agents] cleanup_orphaned = true`, then disable subagents (`[agents] enabled = false` or `--no-subagents`) and run `ruler apply`.
|
|
841
842
|
|
|
842
843
|
### Example Workflow
|
|
843
844
|
|
|
@@ -870,7 +871,7 @@ ruler apply
|
|
|
870
871
|
|
|
871
872
|
### Limitations
|
|
872
873
|
|
|
873
|
-
- **No explicit revert command.**
|
|
874
|
+
- **No explicit revert command.** Optional cleanup is available via `[agents] cleanup_orphaned = true` and a subsequent `apply`.
|
|
874
875
|
- **Atomic replace, not merge.** Ruler regenerates each agent's subagent directory from the source on every apply. Manual edits to generated files will be overwritten.
|
|
875
876
|
- **No support yet for agents without a native subagent primitive.** Windsurf, RooCode, Aider, Gemini CLI, and others are skipped with a warning. Propagation will be added when those agents ship a comparable file format.
|
|
876
877
|
|
package/dist/cli/commands.js
CHANGED
|
@@ -80,7 +80,7 @@ function run() {
|
|
|
80
80
|
})
|
|
81
81
|
.option('subagents', {
|
|
82
82
|
type: 'boolean',
|
|
83
|
-
description: 'Enable/disable subagents support (experimental, default:
|
|
83
|
+
description: 'Enable/disable subagents support (experimental, default: disabled)',
|
|
84
84
|
});
|
|
85
85
|
}, handlers_1.applyHandler)
|
|
86
86
|
.command('init', 'Scaffold a .ruler directory with default files', (y) => {
|
package/dist/cli/handlers.js
CHANGED
|
@@ -114,7 +114,7 @@ async function applyHandler(argv) {
|
|
|
114
114
|
else {
|
|
115
115
|
skillsEnabled = undefined; // Let config/default decide
|
|
116
116
|
}
|
|
117
|
-
// Determine subagents preference: CLI > TOML > Default (
|
|
117
|
+
// Determine subagents preference: CLI > TOML > Default (disabled)
|
|
118
118
|
let subagentsEnabled;
|
|
119
119
|
if (argv.subagents !== undefined) {
|
|
120
120
|
subagentsEnabled = argv.subagents;
|
|
@@ -76,13 +76,18 @@ const agentConfigSchema = zod_1.z
|
|
|
76
76
|
// - one nested table per coding-agent integration (`[agents.claude]`, etc.)
|
|
77
77
|
// Reserved keys are validated by the object shape; everything else falls
|
|
78
78
|
// through `catchall` and is treated as a per-agent config record.
|
|
79
|
-
const SUBAGENT_RESERVED_KEYS = new Set([
|
|
79
|
+
const SUBAGENT_RESERVED_KEYS = new Set([
|
|
80
|
+
'enabled',
|
|
81
|
+
'include_in_rules',
|
|
82
|
+
'cleanup_orphaned',
|
|
83
|
+
]);
|
|
80
84
|
const rulerConfigSchema = zod_1.z.object({
|
|
81
85
|
default_agents: zod_1.z.array(zod_1.z.string()).optional(),
|
|
82
86
|
agents: zod_1.z
|
|
83
87
|
.object({
|
|
84
88
|
enabled: zod_1.z.boolean().optional(),
|
|
85
89
|
include_in_rules: zod_1.z.boolean().optional(),
|
|
90
|
+
cleanup_orphaned: zod_1.z.boolean().optional(),
|
|
86
91
|
})
|
|
87
92
|
.catchall(agentConfigSchema)
|
|
88
93
|
.optional(),
|
|
@@ -111,6 +116,7 @@ const rulerConfigSchema = zod_1.z.object({
|
|
|
111
116
|
.object({
|
|
112
117
|
enabled: zod_1.z.boolean().optional(),
|
|
113
118
|
include_in_rules: zod_1.z.boolean().optional(),
|
|
119
|
+
cleanup_orphaned: zod_1.z.boolean().optional(),
|
|
114
120
|
})
|
|
115
121
|
.optional(),
|
|
116
122
|
nested: zod_1.z.boolean().optional(),
|
|
@@ -272,7 +278,8 @@ async function loadConfig(options) {
|
|
|
272
278
|
? raw.subagents
|
|
273
279
|
: {};
|
|
274
280
|
const legacyHasContent = typeof rawLegacySubagentsSection.enabled === 'boolean' ||
|
|
275
|
-
typeof rawLegacySubagentsSection.include_in_rules === 'boolean'
|
|
281
|
+
typeof rawLegacySubagentsSection.include_in_rules === 'boolean' ||
|
|
282
|
+
typeof rawLegacySubagentsSection.cleanup_orphaned === 'boolean';
|
|
276
283
|
if (legacyHasContent) {
|
|
277
284
|
warnLegacySubagentsSection();
|
|
278
285
|
}
|
|
@@ -291,6 +298,14 @@ async function loadConfig(options) {
|
|
|
291
298
|
subagentsConfig.include_in_rules =
|
|
292
299
|
rawLegacySubagentsSection.include_in_rules;
|
|
293
300
|
}
|
|
301
|
+
if (typeof agentsSection.cleanup_orphaned === 'boolean') {
|
|
302
|
+
subagentsConfig.cleanup_orphaned =
|
|
303
|
+
agentsSection.cleanup_orphaned;
|
|
304
|
+
}
|
|
305
|
+
else if (typeof rawLegacySubagentsSection.cleanup_orphaned === 'boolean') {
|
|
306
|
+
subagentsConfig.cleanup_orphaned =
|
|
307
|
+
rawLegacySubagentsSection.cleanup_orphaned;
|
|
308
|
+
}
|
|
294
309
|
const nestedDefined = typeof raw.nested === 'boolean';
|
|
295
310
|
const nested = nestedDefined ? raw.nested : false;
|
|
296
311
|
return {
|
|
@@ -62,16 +62,13 @@ async function discoverSubagents(projectRoot) {
|
|
|
62
62
|
catch {
|
|
63
63
|
return { subagents: [], warnings: [] };
|
|
64
64
|
}
|
|
65
|
-
const
|
|
66
|
-
const mdFiles = entries
|
|
67
|
-
.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
|
|
68
|
-
.map((entry) => path.join(dir, entry.name))
|
|
69
|
-
.sort();
|
|
65
|
+
const mdFiles = await listMarkdownFilesRecursive(dir);
|
|
70
66
|
const subagents = [];
|
|
71
67
|
const warnings = [];
|
|
72
68
|
for (const filePath of mdFiles) {
|
|
73
69
|
const info = await (0, SubagentsUtils_1.loadSubagentFile)(filePath);
|
|
74
70
|
if (info.valid) {
|
|
71
|
+
info.sourceRelativePath = path.relative(dir, filePath);
|
|
75
72
|
subagents.push(info);
|
|
76
73
|
}
|
|
77
74
|
else if (info.error) {
|
|
@@ -80,6 +77,22 @@ async function discoverSubagents(projectRoot) {
|
|
|
80
77
|
}
|
|
81
78
|
return { subagents, warnings };
|
|
82
79
|
}
|
|
80
|
+
async function listMarkdownFilesRecursive(dir) {
|
|
81
|
+
const results = [];
|
|
82
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
const fullPath = path.join(dir, entry.name);
|
|
85
|
+
if (entry.isDirectory()) {
|
|
86
|
+
const nested = await listMarkdownFilesRecursive(fullPath);
|
|
87
|
+
results.push(...nested);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (entry.isFile() && entry.name.endsWith('.md')) {
|
|
91
|
+
results.push(fullPath);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return results.sort();
|
|
95
|
+
}
|
|
83
96
|
const SUBAGENT_TARGET_TO_IDENTIFIERS = new Map([
|
|
84
97
|
['claude', ['claude']],
|
|
85
98
|
['cursor', ['cursor']],
|
|
@@ -167,7 +180,9 @@ async function writeAgentsDirectoryAtomic(targetDir, files) {
|
|
|
167
180
|
await fs.mkdir(tempDir, { recursive: true });
|
|
168
181
|
try {
|
|
169
182
|
for (const { name, content } of files) {
|
|
170
|
-
|
|
183
|
+
const outputPath = path.join(tempDir, name);
|
|
184
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
185
|
+
await fs.writeFile(outputPath, content, 'utf8');
|
|
171
186
|
}
|
|
172
187
|
try {
|
|
173
188
|
await fs.rm(targetDir, { recursive: true, force: true });
|
|
@@ -187,6 +202,19 @@ async function writeAgentsDirectoryAtomic(targetDir, files) {
|
|
|
187
202
|
throw error;
|
|
188
203
|
}
|
|
189
204
|
}
|
|
205
|
+
function getSourceRelativeMdPath(sub) {
|
|
206
|
+
const fromSource = sub.sourceRelativePath;
|
|
207
|
+
if (typeof fromSource === 'string' &&
|
|
208
|
+
fromSource.length > 0 &&
|
|
209
|
+
!path.isAbsolute(fromSource) &&
|
|
210
|
+
!fromSource.startsWith('..')) {
|
|
211
|
+
return fromSource;
|
|
212
|
+
}
|
|
213
|
+
return `${sub.name}.md`;
|
|
214
|
+
}
|
|
215
|
+
function withExtension(filePath, ext) {
|
|
216
|
+
return filePath.replace(/\.md$/i, ext);
|
|
217
|
+
}
|
|
190
218
|
function buildClaudeFile(sub) {
|
|
191
219
|
const fm = sub.frontmatter;
|
|
192
220
|
const meta = {
|
|
@@ -276,10 +304,10 @@ async function propagateSubagentsForClaude(projectRoot, subagents, options) {
|
|
|
276
304
|
return [];
|
|
277
305
|
const targetDir = path.join(projectRoot, constants_1.CLAUDE_SUBAGENTS_PATH);
|
|
278
306
|
if (options.dryRun) {
|
|
279
|
-
return subagents.map((s) => `Write ${path.join(constants_1.CLAUDE_SUBAGENTS_PATH,
|
|
307
|
+
return subagents.map((s) => `Write ${path.join(constants_1.CLAUDE_SUBAGENTS_PATH, getSourceRelativeMdPath(s))}`);
|
|
280
308
|
}
|
|
281
309
|
const files = subagents.map((s) => ({
|
|
282
|
-
name:
|
|
310
|
+
name: getSourceRelativeMdPath(s),
|
|
283
311
|
content: buildClaudeFile(s),
|
|
284
312
|
}));
|
|
285
313
|
await writeAgentsDirectoryAtomic(targetDir, files);
|
|
@@ -290,10 +318,10 @@ async function propagateSubagentsForCursor(projectRoot, subagents, options) {
|
|
|
290
318
|
return [];
|
|
291
319
|
const targetDir = path.join(projectRoot, constants_1.CURSOR_SUBAGENTS_PATH);
|
|
292
320
|
if (options.dryRun) {
|
|
293
|
-
return subagents.map((s) => `Write ${path.join(constants_1.CURSOR_SUBAGENTS_PATH,
|
|
321
|
+
return subagents.map((s) => `Write ${path.join(constants_1.CURSOR_SUBAGENTS_PATH, getSourceRelativeMdPath(s))}`);
|
|
294
322
|
}
|
|
295
323
|
const files = subagents.map((s) => ({
|
|
296
|
-
name:
|
|
324
|
+
name: getSourceRelativeMdPath(s),
|
|
297
325
|
content: buildCursorFile(s),
|
|
298
326
|
}));
|
|
299
327
|
await writeAgentsDirectoryAtomic(targetDir, files);
|
|
@@ -304,10 +332,10 @@ async function propagateSubagentsForCodex(projectRoot, subagents, options) {
|
|
|
304
332
|
return [];
|
|
305
333
|
const targetDir = path.join(projectRoot, constants_1.CODEX_SUBAGENTS_PATH);
|
|
306
334
|
if (options.dryRun) {
|
|
307
|
-
return subagents.map((s) => `Write ${path.join(constants_1.CODEX_SUBAGENTS_PATH,
|
|
335
|
+
return subagents.map((s) => `Write ${path.join(constants_1.CODEX_SUBAGENTS_PATH, withExtension(getSourceRelativeMdPath(s), '.toml'))}`);
|
|
308
336
|
}
|
|
309
337
|
const files = subagents.map((s) => ({
|
|
310
|
-
name:
|
|
338
|
+
name: withExtension(getSourceRelativeMdPath(s), '.toml'),
|
|
311
339
|
content: buildCodexFile(s),
|
|
312
340
|
}));
|
|
313
341
|
await writeAgentsDirectoryAtomic(targetDir, files);
|
|
@@ -325,12 +353,12 @@ async function propagateSubagentsForCopilot(projectRoot, subagents, options) {
|
|
|
325
353
|
// emits when dryRun is true so users previewing a change can see
|
|
326
354
|
// which tools would be dropped before it actually happens.
|
|
327
355
|
buildCopilotFile(s, true, verbose);
|
|
328
|
-
planLines.push(`Write ${path.join(constants_1.COPILOT_SUBAGENTS_PATH,
|
|
356
|
+
planLines.push(`Write ${path.join(constants_1.COPILOT_SUBAGENTS_PATH, getSourceRelativeMdPath(s))}`);
|
|
329
357
|
}
|
|
330
358
|
return planLines;
|
|
331
359
|
}
|
|
332
360
|
const files = subagents.map((s) => ({
|
|
333
|
-
name:
|
|
361
|
+
name: getSourceRelativeMdPath(s),
|
|
334
362
|
content: buildCopilotFile(s, false, verbose).content,
|
|
335
363
|
}));
|
|
336
364
|
await writeAgentsDirectoryAtomic(targetDir, files);
|
|
@@ -363,10 +391,24 @@ async function cleanupAllSubagentsDirectories(projectRoot, dryRun, verbose) {
|
|
|
363
391
|
/* ------------------------------------------------------------------ */
|
|
364
392
|
/* Orchestrator */
|
|
365
393
|
/* ------------------------------------------------------------------ */
|
|
366
|
-
async function propagateSubagents(projectRoot, agents, subagentsEnabled, verbose, dryRun) {
|
|
367
|
-
|
|
368
|
-
(
|
|
394
|
+
async function propagateSubagents(projectRoot, agents, subagentsEnabled, cleanupOrphaned, verbose, dryRun) {
|
|
395
|
+
const maybeCleanupAllSubagentsDirectories = async () => {
|
|
396
|
+
if (!cleanupOrphaned) {
|
|
397
|
+
(0, constants_1.logVerboseInfo)('Subagent cleanup skipped (set [agents] cleanup_orphaned = true to enable directory cleanup)', verbose, dryRun);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
369
400
|
await cleanupAllSubagentsDirectories(projectRoot, dryRun, verbose);
|
|
401
|
+
};
|
|
402
|
+
const maybeCleanupSubagentsDir = async (relPath) => {
|
|
403
|
+
if (!cleanupOrphaned)
|
|
404
|
+
return;
|
|
405
|
+
await cleanupSubagentsDir(projectRoot, relPath, dryRun, verbose);
|
|
406
|
+
};
|
|
407
|
+
if (!subagentsEnabled) {
|
|
408
|
+
(0, constants_1.logVerboseInfo)(cleanupOrphaned
|
|
409
|
+
? 'Subagents support disabled, cleaning up subagent directories'
|
|
410
|
+
: 'Subagents support disabled, leaving existing subagent directories unchanged', verbose, dryRun);
|
|
411
|
+
await maybeCleanupAllSubagentsDirectories();
|
|
370
412
|
return;
|
|
371
413
|
}
|
|
372
414
|
const sourceDir = path.join(projectRoot, constants_1.RULER_SUBAGENTS_PATH);
|
|
@@ -374,16 +416,20 @@ async function propagateSubagents(projectRoot, agents, subagentsEnabled, verbose
|
|
|
374
416
|
await fs.access(sourceDir);
|
|
375
417
|
}
|
|
376
418
|
catch {
|
|
377
|
-
(0, constants_1.logVerboseInfo)(
|
|
378
|
-
|
|
419
|
+
(0, constants_1.logVerboseInfo)(cleanupOrphaned
|
|
420
|
+
? 'No .ruler/agents directory found, cleaning up any stale managed subagent directories'
|
|
421
|
+
: 'No .ruler/agents directory found; leaving existing subagent directories unchanged', verbose, dryRun);
|
|
422
|
+
await maybeCleanupAllSubagentsDirectories();
|
|
379
423
|
return;
|
|
380
424
|
}
|
|
381
425
|
const { subagents, warnings } = await discoverSubagents(projectRoot);
|
|
382
426
|
for (const w of warnings)
|
|
383
427
|
(0, constants_1.logWarn)(w, dryRun);
|
|
384
428
|
if (subagents.length === 0) {
|
|
385
|
-
(0, constants_1.logVerboseInfo)(
|
|
386
|
-
|
|
429
|
+
(0, constants_1.logVerboseInfo)(cleanupOrphaned
|
|
430
|
+
? 'No valid subagents found in .ruler/agents; cleaning up any stale managed subagent directories'
|
|
431
|
+
: 'No valid subagents found in .ruler/agents; leaving existing subagent directories unchanged', verbose, dryRun);
|
|
432
|
+
await maybeCleanupAllSubagentsDirectories();
|
|
387
433
|
return;
|
|
388
434
|
}
|
|
389
435
|
(0, constants_1.logVerboseInfo)(`Discovered ${subagents.length} subagent(s)`, verbose, dryRun);
|
|
@@ -401,7 +447,7 @@ async function propagateSubagents(projectRoot, agents, subagentsEnabled, verbose
|
|
|
401
447
|
const allTargets = ['claude', 'cursor', 'codex', 'copilot'];
|
|
402
448
|
for (const target of allTargets) {
|
|
403
449
|
if (!targets.has(target)) {
|
|
404
|
-
await
|
|
450
|
+
await maybeCleanupSubagentsDir(SUBAGENT_TARGET_PATHS[target]);
|
|
405
451
|
}
|
|
406
452
|
}
|
|
407
453
|
if (supporting.length === 0) {
|
package/dist/lib.js
CHANGED
|
@@ -70,6 +70,9 @@ function resolveSubagentsEnabled(cliFlag, configSetting) {
|
|
|
70
70
|
? configSetting
|
|
71
71
|
: false; // default to disabled — see spec: subagents must opt in
|
|
72
72
|
}
|
|
73
|
+
function resolveSubagentsCleanupOrphaned(configSetting) {
|
|
74
|
+
return configSetting === true;
|
|
75
|
+
}
|
|
73
76
|
/**
|
|
74
77
|
* Applies ruler configurations for all supported AI agents.
|
|
75
78
|
* @param projectRoot Root directory of the project
|
|
@@ -119,12 +122,13 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
|
|
|
119
122
|
}
|
|
120
123
|
// Propagate subagents (mirrors skills handling for nested mode).
|
|
121
124
|
const subagentsEnabledResolved = resolveSubagentsEnabled(subagentsEnabled, rootConfig.subagents?.enabled);
|
|
125
|
+
const subagentsCleanupOrphaned = resolveSubagentsCleanupOrphaned(rootConfig.subagents?.cleanup_orphaned);
|
|
122
126
|
{
|
|
123
127
|
const { propagateSubagents } = await Promise.resolve().then(() => __importStar(require('./core/SubagentsProcessor')));
|
|
124
128
|
for (const configEntry of hierarchicalConfigs) {
|
|
125
129
|
const nestedRoot = path.dirname(configEntry.rulerDir);
|
|
126
130
|
(0, constants_1.logVerbose)(`Propagating subagents for nested directory: ${nestedRoot}`, verbose);
|
|
127
|
-
await propagateSubagents(nestedRoot, selectedAgents, subagentsEnabledResolved, verbose, dryRun);
|
|
131
|
+
await propagateSubagents(nestedRoot, selectedAgents, subagentsEnabledResolved, subagentsCleanupOrphaned, verbose, dryRun);
|
|
128
132
|
}
|
|
129
133
|
}
|
|
130
134
|
generatedPaths = await (0, apply_engine_1.processHierarchicalConfigurations)(selectedAgents, hierarchicalConfigs, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup);
|
|
@@ -146,9 +150,10 @@ async function applyAllAgentConfigs(projectRoot, includedAgents, configPath, cli
|
|
|
146
150
|
}
|
|
147
151
|
// Propagate subagents (mirrors skills handling).
|
|
148
152
|
const subagentsEnabledResolvedSingle = resolveSubagentsEnabled(subagentsEnabled, singleConfig.config.subagents?.enabled);
|
|
153
|
+
const subagentsCleanupOrphanedSingle = resolveSubagentsCleanupOrphaned(singleConfig.config.subagents?.cleanup_orphaned);
|
|
149
154
|
{
|
|
150
155
|
const { propagateSubagents } = await Promise.resolve().then(() => __importStar(require('./core/SubagentsProcessor')));
|
|
151
|
-
await propagateSubagents(projectRoot, selectedAgents, subagentsEnabledResolvedSingle, verbose, dryRun);
|
|
156
|
+
await propagateSubagents(projectRoot, selectedAgents, subagentsEnabledResolvedSingle, subagentsCleanupOrphanedSingle, verbose, dryRun);
|
|
152
157
|
}
|
|
153
158
|
generatedPaths = await (0, apply_engine_1.processSingleConfiguration)(selectedAgents, singleConfig, projectRoot, verbose, dryRun, cliMcpEnabled, cliMcpStrategy, backup);
|
|
154
159
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intellectronica/ruler",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.41",
|
|
4
4
|
"description": "Ruler — apply the same rules to all coding agents",
|
|
5
5
|
"main": "dist/lib.js",
|
|
6
6
|
"scripts": {
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"eslint-config-prettier": "^10.1.8",
|
|
59
59
|
"eslint-plugin-prettier": "^5.5.4",
|
|
60
60
|
"jest": "^29.7.0",
|
|
61
|
+
"jest-util": "^29.7.0",
|
|
61
62
|
"prettier": "^3.6.2",
|
|
62
63
|
"ts-jest": "^29.4.5",
|
|
63
64
|
"typescript": "^5.9.3",
|