rulesync 8.12.0 → 8.14.0
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 +2 -2
- package/dist/{chunk-643VJ2QM.js → chunk-C7PHKGD2.js} +3183 -2292
- package/dist/cli/index.cjs +3540 -2599
- package/dist/cli/index.js +157 -107
- package/dist/index.cjs +3034 -2144
- package/dist/index.d.cts +27 -11
- package/dist/index.d.ts +27 -11
- package/dist/index.js +4 -5
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
stringifyFrontmatter,
|
|
80
80
|
toPosixPath,
|
|
81
81
|
writeFileContent
|
|
82
|
-
} from "../chunk-
|
|
82
|
+
} from "../chunk-C7PHKGD2.js";
|
|
83
83
|
|
|
84
84
|
// src/cli/index.ts
|
|
85
85
|
import { Command } from "commander";
|
|
@@ -658,32 +658,32 @@ async function convertFetchedFilesToRulesync(params) {
|
|
|
658
658
|
{
|
|
659
659
|
feature: "rules",
|
|
660
660
|
getTargets: () => RulesProcessor.getToolTargets({ global: false }),
|
|
661
|
-
createProcessor: () => new RulesProcessor({
|
|
661
|
+
createProcessor: () => new RulesProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger: logger5 })
|
|
662
662
|
},
|
|
663
663
|
{
|
|
664
664
|
feature: "commands",
|
|
665
665
|
getTargets: () => CommandsProcessor.getToolTargets({ global: false, includeSimulated: false }),
|
|
666
|
-
createProcessor: () => new CommandsProcessor({
|
|
666
|
+
createProcessor: () => new CommandsProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger: logger5 })
|
|
667
667
|
},
|
|
668
668
|
{
|
|
669
669
|
feature: "subagents",
|
|
670
670
|
getTargets: () => SubagentsProcessor.getToolTargets({ global: false, includeSimulated: false }),
|
|
671
|
-
createProcessor: () => new SubagentsProcessor({
|
|
671
|
+
createProcessor: () => new SubagentsProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger: logger5 })
|
|
672
672
|
},
|
|
673
673
|
{
|
|
674
674
|
feature: "ignore",
|
|
675
675
|
getTargets: () => IgnoreProcessor.getToolTargets(),
|
|
676
|
-
createProcessor: () => new IgnoreProcessor({
|
|
676
|
+
createProcessor: () => new IgnoreProcessor({ outputRoot: tempDir, toolTarget: target, logger: logger5 })
|
|
677
677
|
},
|
|
678
678
|
{
|
|
679
679
|
feature: "mcp",
|
|
680
680
|
getTargets: () => McpProcessor.getToolTargets({ global: false }),
|
|
681
|
-
createProcessor: () => new McpProcessor({
|
|
681
|
+
createProcessor: () => new McpProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger: logger5 })
|
|
682
682
|
},
|
|
683
683
|
{
|
|
684
684
|
feature: "hooks",
|
|
685
685
|
getTargets: () => HooksProcessor.getToolTargets({ global: false }),
|
|
686
|
-
createProcessor: () => new HooksProcessor({
|
|
686
|
+
createProcessor: () => new HooksProcessor({ outputRoot: tempDir, toolTarget: target, global: false, logger: logger5 })
|
|
687
687
|
}
|
|
688
688
|
];
|
|
689
689
|
for (const config of featureConfigs) {
|
|
@@ -728,7 +728,7 @@ function isNotFoundError(error) {
|
|
|
728
728
|
return false;
|
|
729
729
|
}
|
|
730
730
|
async function fetchFiles(params) {
|
|
731
|
-
const { source, options = {},
|
|
731
|
+
const { source, options = {}, outputRoot = process.cwd(), logger: logger5 } = params;
|
|
732
732
|
const parsed = parseSource(source);
|
|
733
733
|
if (parsed.provider === "gitlab") {
|
|
734
734
|
throw new Error(
|
|
@@ -743,7 +743,7 @@ async function fetchFiles(params) {
|
|
|
743
743
|
const target = options.target ?? "rulesync";
|
|
744
744
|
checkPathTraversal({
|
|
745
745
|
relativePath: outputDir,
|
|
746
|
-
intendedRootDir:
|
|
746
|
+
intendedRootDir: outputRoot
|
|
747
747
|
});
|
|
748
748
|
const token = GitHubClient.resolveToken(options.token);
|
|
749
749
|
const client = new GitHubClient({ token });
|
|
@@ -766,7 +766,7 @@ async function fetchFiles(params) {
|
|
|
766
766
|
enabledFeatures,
|
|
767
767
|
target,
|
|
768
768
|
outputDir,
|
|
769
|
-
|
|
769
|
+
outputRoot,
|
|
770
770
|
conflictStrategy,
|
|
771
771
|
logger: logger5
|
|
772
772
|
});
|
|
@@ -793,7 +793,7 @@ async function fetchFiles(params) {
|
|
|
793
793
|
skipped: 0
|
|
794
794
|
};
|
|
795
795
|
}
|
|
796
|
-
const outputBasePath = join(
|
|
796
|
+
const outputBasePath = join(outputRoot, outputDir);
|
|
797
797
|
for (const { relativePath, size } of filesToFetch) {
|
|
798
798
|
checkPathTraversal({
|
|
799
799
|
relativePath,
|
|
@@ -907,7 +907,7 @@ async function fetchAndConvertToolFiles(params) {
|
|
|
907
907
|
enabledFeatures,
|
|
908
908
|
target,
|
|
909
909
|
outputDir,
|
|
910
|
-
|
|
910
|
+
outputRoot,
|
|
911
911
|
conflictStrategy: _conflictStrategy,
|
|
912
912
|
logger: logger5
|
|
913
913
|
} = params;
|
|
@@ -956,7 +956,7 @@ async function fetchAndConvertToolFiles(params) {
|
|
|
956
956
|
logger5.debug(`Fetched to temp: ${toolRelativePath}`);
|
|
957
957
|
})
|
|
958
958
|
);
|
|
959
|
-
const outputBasePath = join(
|
|
959
|
+
const outputBasePath = join(outputRoot, outputDir);
|
|
960
960
|
const { converted, convertedPaths } = await convertFetchedFilesToRulesync({
|
|
961
961
|
tempDir,
|
|
962
962
|
outputDir: outputBasePath,
|
|
@@ -1110,6 +1110,15 @@ async function fetchCommand(logger5, options) {
|
|
|
1110
1110
|
}
|
|
1111
1111
|
|
|
1112
1112
|
// src/cli/commands/generate.ts
|
|
1113
|
+
function sameDirSets(a, b) {
|
|
1114
|
+
const aSet = new Set(a);
|
|
1115
|
+
const bSet = new Set(b);
|
|
1116
|
+
if (aSet.size !== bSet.size) return false;
|
|
1117
|
+
for (const v of aSet) {
|
|
1118
|
+
if (!bSet.has(v)) return false;
|
|
1119
|
+
}
|
|
1120
|
+
return true;
|
|
1121
|
+
}
|
|
1113
1122
|
function logFeatureResult(logger5, params) {
|
|
1114
1123
|
const { count, paths, featureName, isPreview, modePrefix } = params;
|
|
1115
1124
|
if (count > 0) {
|
|
@@ -1124,18 +1133,33 @@ function logFeatureResult(logger5, params) {
|
|
|
1124
1133
|
}
|
|
1125
1134
|
}
|
|
1126
1135
|
async function generateCommand(logger5, options) {
|
|
1127
|
-
const
|
|
1136
|
+
const { baseDir, outputRoots, ...rest } = options;
|
|
1137
|
+
if (baseDir !== void 0) {
|
|
1138
|
+
logger5.warn(
|
|
1139
|
+
"--base-dir is deprecated; use --output-roots instead. It will be removed in a future major release."
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
const outputRootsResolved = outputRoots !== void 0 && outputRoots.length > 0 ? outputRoots : baseDir;
|
|
1143
|
+
if (baseDir !== void 0 && outputRoots !== void 0 && baseDir.length > 0 && outputRoots.length > 0 && !sameDirSets(outputRoots, baseDir)) {
|
|
1144
|
+
logger5.warn(
|
|
1145
|
+
`Both '--output-roots' and '--base-dir' were provided with differing values; using '--output-roots' (${JSON.stringify(outputRoots)}) and ignoring '--base-dir' (${JSON.stringify(baseDir)}).`
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
const config = await ConfigResolver.resolve(
|
|
1149
|
+
{ ...rest, outputRoots: outputRootsResolved },
|
|
1150
|
+
{ logger: logger5 }
|
|
1151
|
+
);
|
|
1128
1152
|
const check = config.getCheck();
|
|
1129
1153
|
const isPreview = config.isPreviewMode();
|
|
1130
1154
|
const modePrefix = isPreview ? "[DRY RUN]" : "";
|
|
1131
1155
|
logger5.debug("Generating files...");
|
|
1132
|
-
if (!await checkRulesyncDirExists({
|
|
1156
|
+
if (!await checkRulesyncDirExists({ inputRoot: config.getInputRoot() })) {
|
|
1133
1157
|
throw new CLIError(
|
|
1134
1158
|
".rulesync directory not found. Run 'rulesync init' first.",
|
|
1135
1159
|
ErrorCodes.RULESYNC_DIR_NOT_FOUND
|
|
1136
1160
|
);
|
|
1137
1161
|
}
|
|
1138
|
-
logger5.debug(`
|
|
1162
|
+
logger5.debug(`Output roots: ${config.getOutputRoots().join(", ")}`);
|
|
1139
1163
|
const features = config.getFeatures();
|
|
1140
1164
|
if (features.includes("ignore")) {
|
|
1141
1165
|
logger5.debug("Generating ignore files...");
|
|
@@ -1289,6 +1313,12 @@ var GITIGNORE_ENTRY_REGISTRY = [
|
|
|
1289
1313
|
// Cursor
|
|
1290
1314
|
{ target: "cursor", feature: "rules", entry: "**/.cursor/" },
|
|
1291
1315
|
{ target: "cursor", feature: "ignore", entry: "**/.cursorignore" },
|
|
1316
|
+
// .cursor/cli.json (project) and .cursor/cli-config.json (global) are
|
|
1317
|
+
// already covered by the broader **/.cursor/ entry above; the additional
|
|
1318
|
+
// `permissions` and `hooks` tags below register the same prefix under the
|
|
1319
|
+
// matching feature so target+feature filtering still resolves correctly.
|
|
1320
|
+
{ target: "cursor", feature: "permissions", entry: "**/.cursor/" },
|
|
1321
|
+
{ target: "cursor", feature: "hooks", entry: "**/.cursor/" },
|
|
1292
1322
|
// deepagents-cli
|
|
1293
1323
|
{ target: "deepagents", feature: "rules", entry: "**/.deepagents/AGENTS.md" },
|
|
1294
1324
|
{ target: "deepagents", feature: "rules", entry: "**/.deepagents/memories/" },
|
|
@@ -1356,6 +1386,15 @@ var GITIGNORE_ENTRY_REGISTRY = [
|
|
|
1356
1386
|
feature: "mcp",
|
|
1357
1387
|
entry: "**/.copilot/mcp-config.json"
|
|
1358
1388
|
},
|
|
1389
|
+
// Project: <project>/.github/agents/*.agent.md
|
|
1390
|
+
// Global: ~/.copilot/agents/
|
|
1391
|
+
{ target: "copilotcli", feature: "subagents", entry: "**/.github/agents/" },
|
|
1392
|
+
{ target: "copilotcli", feature: "subagents", entry: "**/.copilot/agents/" },
|
|
1393
|
+
// Project: <project>/.github/hooks/copilotcli-hooks.json
|
|
1394
|
+
// Global: ~/.copilot/hooks/copilot-hooks.json (rulesync convention pending
|
|
1395
|
+
// official documentation of a global hooks location)
|
|
1396
|
+
{ target: "copilotcli", feature: "hooks", entry: "**/.github/hooks/" },
|
|
1397
|
+
{ target: "copilotcli", feature: "hooks", entry: "**/.copilot/hooks/" },
|
|
1359
1398
|
// Junie
|
|
1360
1399
|
{ target: "junie", feature: "rules", entry: "**/.junie/guidelines.md" },
|
|
1361
1400
|
{ target: "junie", feature: "mcp", entry: "**/.junie/mcp.json" },
|
|
@@ -1747,7 +1786,7 @@ async function importCommand(logger5, options) {
|
|
|
1747
1786
|
if (options.targets.length > 1) {
|
|
1748
1787
|
throw new CLIError("Only one tool can be imported at a time", ErrorCodes.IMPORT_FAILED);
|
|
1749
1788
|
}
|
|
1750
|
-
const config = await ConfigResolver.resolve(options);
|
|
1789
|
+
const config = await ConfigResolver.resolve(options, { logger: logger5 });
|
|
1751
1790
|
const tool = config.getTargets()[0];
|
|
1752
1791
|
logger5.debug(`Importing files from ${tool}...`);
|
|
1753
1792
|
const result = await importFromTool({ config, tool, logger: logger5 });
|
|
@@ -1805,7 +1844,7 @@ async function createConfigFile() {
|
|
|
1805
1844
|
$schema: RULESYNC_CONFIG_SCHEMA_URL,
|
|
1806
1845
|
targets: ["copilot", "cursor", "claudecode", "codexcli"],
|
|
1807
1846
|
features: ["rules", "ignore", "mcp", "commands", "subagents", "skills", "hooks"],
|
|
1808
|
-
|
|
1847
|
+
outputRoots: ["."],
|
|
1809
1848
|
delete: true,
|
|
1810
1849
|
verbose: false,
|
|
1811
1850
|
silent: false,
|
|
@@ -2087,8 +2126,8 @@ var ApmLockSchema = z4.looseObject({
|
|
|
2087
2126
|
dependencies: z4.array(ApmLockDependencySchema),
|
|
2088
2127
|
mcp_servers: optional(z4.array(z4.string()))
|
|
2089
2128
|
});
|
|
2090
|
-
function getApmLockPath(
|
|
2091
|
-
return join4(
|
|
2129
|
+
function getApmLockPath(projectRoot) {
|
|
2130
|
+
return join4(projectRoot, APM_LOCKFILE_FILE_NAME);
|
|
2092
2131
|
}
|
|
2093
2132
|
function createEmptyApmLock(params) {
|
|
2094
2133
|
const base = params.existingLock ? { ...params.existingLock } : {};
|
|
@@ -2121,8 +2160,8 @@ ${issues}`);
|
|
|
2121
2160
|
}
|
|
2122
2161
|
return parsed.data;
|
|
2123
2162
|
}
|
|
2124
|
-
async function readApmLock(
|
|
2125
|
-
const path2 = getApmLockPath(
|
|
2163
|
+
async function readApmLock(projectRoot) {
|
|
2164
|
+
const path2 = getApmLockPath(projectRoot);
|
|
2126
2165
|
if (!await fileExists(path2)) {
|
|
2127
2166
|
return null;
|
|
2128
2167
|
}
|
|
@@ -2130,7 +2169,7 @@ async function readApmLock(baseDir) {
|
|
|
2130
2169
|
return parseApmLock(content);
|
|
2131
2170
|
}
|
|
2132
2171
|
async function writeApmLock(params) {
|
|
2133
|
-
const path2 = getApmLockPath(params.
|
|
2172
|
+
const path2 = getApmLockPath(params.projectRoot);
|
|
2134
2173
|
const content = serializeApmLock(params.lock);
|
|
2135
2174
|
await writeFileContent(path2, content);
|
|
2136
2175
|
}
|
|
@@ -2164,11 +2203,11 @@ var ApmManifestSchema = z5.looseObject({
|
|
|
2164
2203
|
})
|
|
2165
2204
|
)
|
|
2166
2205
|
});
|
|
2167
|
-
function getApmManifestPath(
|
|
2168
|
-
return join5(
|
|
2206
|
+
function getApmManifestPath(projectRoot) {
|
|
2207
|
+
return join5(projectRoot, APM_MANIFEST_FILE_NAME);
|
|
2169
2208
|
}
|
|
2170
|
-
async function apmManifestExists(
|
|
2171
|
-
return fileExists(getApmManifestPath(
|
|
2209
|
+
async function apmManifestExists(projectRoot) {
|
|
2210
|
+
return fileExists(getApmManifestPath(projectRoot));
|
|
2172
2211
|
}
|
|
2173
2212
|
function parseApmManifest(content) {
|
|
2174
2213
|
const loaded = load2(content);
|
|
@@ -2190,8 +2229,8 @@ function parseApmManifest(content) {
|
|
|
2190
2229
|
dependencies
|
|
2191
2230
|
};
|
|
2192
2231
|
}
|
|
2193
|
-
async function readApmManifest(
|
|
2194
|
-
const path2 = getApmManifestPath(
|
|
2232
|
+
async function readApmManifest(projectRoot) {
|
|
2233
|
+
const path2 = getApmManifestPath(projectRoot);
|
|
2195
2234
|
const content = await readFileContent(path2);
|
|
2196
2235
|
return parseApmManifest(content);
|
|
2197
2236
|
}
|
|
@@ -2344,13 +2383,13 @@ var APM_PRIMITIVES = [
|
|
|
2344
2383
|
}
|
|
2345
2384
|
];
|
|
2346
2385
|
async function installApm(params) {
|
|
2347
|
-
const {
|
|
2348
|
-
const manifest = await readApmManifest(
|
|
2386
|
+
const { projectRoot, options = {}, logger: logger5 } = params;
|
|
2387
|
+
const manifest = await readApmManifest(projectRoot);
|
|
2349
2388
|
if (manifest.dependencies.length === 0) {
|
|
2350
2389
|
logger5.warn("apm.yml has no dependencies.apm entries. Nothing to install.");
|
|
2351
2390
|
return { dependenciesProcessed: 0, deployedFileCount: 0, failedDependencyCount: 0 };
|
|
2352
2391
|
}
|
|
2353
|
-
const existingLock = await readApmLock(
|
|
2392
|
+
const existingLock = await readApmLock(projectRoot);
|
|
2354
2393
|
if (options.frozen) {
|
|
2355
2394
|
if (!existingLock) {
|
|
2356
2395
|
throw new Error(
|
|
@@ -2394,7 +2433,7 @@ async function installApm(params) {
|
|
|
2394
2433
|
dep,
|
|
2395
2434
|
client,
|
|
2396
2435
|
semaphore,
|
|
2397
|
-
|
|
2436
|
+
projectRoot,
|
|
2398
2437
|
existingLock,
|
|
2399
2438
|
frozen,
|
|
2400
2439
|
update: options.update ?? false,
|
|
@@ -2449,19 +2488,19 @@ async function installApm(params) {
|
|
|
2449
2488
|
continue;
|
|
2450
2489
|
}
|
|
2451
2490
|
try {
|
|
2452
|
-
checkPathTraversal({ relativePath, intendedRootDir:
|
|
2491
|
+
checkPathTraversal({ relativePath, intendedRootDir: projectRoot });
|
|
2453
2492
|
} catch {
|
|
2454
|
-
logger5.warn(`Refusing to remove stale apm file outside
|
|
2493
|
+
logger5.warn(`Refusing to remove stale apm file outside projectRoot: "${relativePath}".`);
|
|
2455
2494
|
continue;
|
|
2456
2495
|
}
|
|
2457
|
-
const absolute = join6(
|
|
2496
|
+
const absolute = join6(projectRoot, relativePath);
|
|
2458
2497
|
await removeFile(absolute);
|
|
2459
2498
|
logger5.debug(`Removed stale apm file: ${relativePath}`);
|
|
2460
2499
|
}
|
|
2461
2500
|
}
|
|
2462
2501
|
if (!frozen) {
|
|
2463
2502
|
newLock.generated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2464
|
-
await writeApmLock({
|
|
2503
|
+
await writeApmLock({ projectRoot, lock: newLock });
|
|
2465
2504
|
if (failedCount === 0) {
|
|
2466
2505
|
logger5.debug("rulesync-apm.lock.yaml updated.");
|
|
2467
2506
|
} else {
|
|
@@ -2477,7 +2516,7 @@ async function installApm(params) {
|
|
|
2477
2516
|
};
|
|
2478
2517
|
}
|
|
2479
2518
|
async function installDependency(params) {
|
|
2480
|
-
const { dep, client, semaphore,
|
|
2519
|
+
const { dep, client, semaphore, projectRoot, existingLock, frozen, update, logger: logger5 } = params;
|
|
2481
2520
|
const repoUrl = canonicalRepoUrl(dep);
|
|
2482
2521
|
const locked = existingLock ? findApmLockDependency(existingLock, repoUrl) : void 0;
|
|
2483
2522
|
let resolvedRef;
|
|
@@ -2521,7 +2560,7 @@ async function installDependency(params) {
|
|
|
2521
2560
|
const deployRelative = toPosixPath(join6(primitive.deployDir, relativeToBase));
|
|
2522
2561
|
checkPathTraversal({
|
|
2523
2562
|
relativePath: deployRelative,
|
|
2524
|
-
intendedRootDir:
|
|
2563
|
+
intendedRootDir: projectRoot
|
|
2525
2564
|
});
|
|
2526
2565
|
const content = await withSemaphore(
|
|
2527
2566
|
semaphore,
|
|
@@ -2536,7 +2575,7 @@ async function installDependency(params) {
|
|
|
2536
2575
|
}
|
|
2537
2576
|
deployed.push({ path: deployRelative, content });
|
|
2538
2577
|
if (!frozen) {
|
|
2539
|
-
await writeFileContent(join6(
|
|
2578
|
+
await writeFileContent(join6(projectRoot, deployRelative), content);
|
|
2540
2579
|
}
|
|
2541
2580
|
}
|
|
2542
2581
|
}
|
|
@@ -2558,7 +2597,7 @@ async function installDependency(params) {
|
|
|
2558
2597
|
}
|
|
2559
2598
|
if (frozen) {
|
|
2560
2599
|
for (const { path: deployRelative, content } of deployed) {
|
|
2561
|
-
await writeFileContent(join6(
|
|
2600
|
+
await writeFileContent(join6(projectRoot, deployRelative), content);
|
|
2562
2601
|
}
|
|
2563
2602
|
}
|
|
2564
2603
|
const lockEntry = {
|
|
@@ -2706,8 +2745,8 @@ var GhLockSchema = z6.looseObject({
|
|
|
2706
2745
|
generated_at: z6.string(),
|
|
2707
2746
|
installations: z6.array(GhLockInstallationSchema)
|
|
2708
2747
|
});
|
|
2709
|
-
function getGhLockPath(
|
|
2710
|
-
return join7(
|
|
2748
|
+
function getGhLockPath(projectRoot) {
|
|
2749
|
+
return join7(projectRoot, GH_LOCKFILE_FILE_NAME);
|
|
2711
2750
|
}
|
|
2712
2751
|
function createEmptyGhLock(params) {
|
|
2713
2752
|
const base = params?.existingLock ? { ...params.existingLock } : {};
|
|
@@ -2739,8 +2778,8 @@ ${issues}`);
|
|
|
2739
2778
|
}
|
|
2740
2779
|
return parsed.data;
|
|
2741
2780
|
}
|
|
2742
|
-
async function readGhLock(
|
|
2743
|
-
const path2 = getGhLockPath(
|
|
2781
|
+
async function readGhLock(projectRoot) {
|
|
2782
|
+
const path2 = getGhLockPath(projectRoot);
|
|
2744
2783
|
if (!await fileExists(path2)) {
|
|
2745
2784
|
return null;
|
|
2746
2785
|
}
|
|
@@ -2748,7 +2787,7 @@ async function readGhLock(baseDir) {
|
|
|
2748
2787
|
return parseGhLock(content);
|
|
2749
2788
|
}
|
|
2750
2789
|
async function writeGhLock(params) {
|
|
2751
|
-
const path2 = getGhLockPath(params.
|
|
2790
|
+
const path2 = getGhLockPath(params.projectRoot);
|
|
2752
2791
|
const content = serializeGhLock(params.lock);
|
|
2753
2792
|
await writeFileContent(path2, content);
|
|
2754
2793
|
}
|
|
@@ -2800,7 +2839,7 @@ function relativeInstallDirFor(params) {
|
|
|
2800
2839
|
var SKILLS_REMOTE_DIR = "skills";
|
|
2801
2840
|
var SKILL_FILE_NAME2 = "SKILL.md";
|
|
2802
2841
|
async function installGh(params) {
|
|
2803
|
-
const {
|
|
2842
|
+
const { projectRoot, sources, options = {}, logger: logger5 } = params;
|
|
2804
2843
|
if (sources.length === 0) {
|
|
2805
2844
|
return { sourcesProcessed: 0, installedSkillCount: 0, failedSourceCount: 0 };
|
|
2806
2845
|
}
|
|
@@ -2837,7 +2876,7 @@ async function installGh(params) {
|
|
|
2837
2876
|
scope
|
|
2838
2877
|
};
|
|
2839
2878
|
});
|
|
2840
|
-
const existingLock = await readGhLock(
|
|
2879
|
+
const existingLock = await readGhLock(projectRoot);
|
|
2841
2880
|
const frozen = options.frozen ?? false;
|
|
2842
2881
|
const update = options.update ?? false;
|
|
2843
2882
|
if (frozen && !existingLock) {
|
|
@@ -2888,7 +2927,7 @@ async function installGh(params) {
|
|
|
2888
2927
|
rs,
|
|
2889
2928
|
client,
|
|
2890
2929
|
semaphore,
|
|
2891
|
-
|
|
2930
|
+
projectRoot,
|
|
2892
2931
|
existingLock,
|
|
2893
2932
|
frozen,
|
|
2894
2933
|
update,
|
|
@@ -2951,7 +2990,7 @@ async function installGh(params) {
|
|
|
2951
2990
|
await removeStaleFile({
|
|
2952
2991
|
relativePath: deployed,
|
|
2953
2992
|
scope: prev.scope === "user" ? "user" : "project",
|
|
2954
|
-
|
|
2993
|
+
projectRoot,
|
|
2955
2994
|
logger: logger5
|
|
2956
2995
|
});
|
|
2957
2996
|
}
|
|
@@ -2959,7 +2998,7 @@ async function installGh(params) {
|
|
|
2959
2998
|
}
|
|
2960
2999
|
if (!frozen) {
|
|
2961
3000
|
newLock.generated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
2962
|
-
await writeGhLock({
|
|
3001
|
+
await writeGhLock({ projectRoot, lock: newLock });
|
|
2963
3002
|
if (failedCount === 0) {
|
|
2964
3003
|
logger5.debug("rulesync-gh.lock.yaml updated.");
|
|
2965
3004
|
} else {
|
|
@@ -2975,7 +3014,7 @@ async function installGh(params) {
|
|
|
2975
3014
|
};
|
|
2976
3015
|
}
|
|
2977
3016
|
async function installSource(params) {
|
|
2978
|
-
const { rs, client, semaphore,
|
|
3017
|
+
const { rs, client, semaphore, projectRoot, existingLock, frozen, update, logger: logger5 } = params;
|
|
2979
3018
|
const { entry, owner, repo, agent, scope } = rs;
|
|
2980
3019
|
const sourceKey = entry.source;
|
|
2981
3020
|
let resolvedRef;
|
|
@@ -3050,7 +3089,7 @@ async function installSource(params) {
|
|
|
3050
3089
|
}
|
|
3051
3090
|
const results = [];
|
|
3052
3091
|
const installRelDir = relativeInstallDirFor({ agent, scope });
|
|
3053
|
-
const scopeRoot = scope === "user" ? getHomeDirectory() :
|
|
3092
|
+
const scopeRoot = scope === "user" ? getHomeDirectory() : projectRoot;
|
|
3054
3093
|
const sourceUrl = `https://github.com/${owner}/${repo}`;
|
|
3055
3094
|
const repository = `${owner}/${repo}`;
|
|
3056
3095
|
const provenanceRef = usedTag ? resolvedRef : resolvedSha;
|
|
@@ -3166,12 +3205,12 @@ ${content}`;
|
|
|
3166
3205
|
return results;
|
|
3167
3206
|
}
|
|
3168
3207
|
async function removeStaleFile(params) {
|
|
3169
|
-
const { relativePath, scope,
|
|
3208
|
+
const { relativePath, scope, projectRoot, logger: logger5 } = params;
|
|
3170
3209
|
if (posix3.isAbsolute(relativePath) || relativePath.split(/[/\\]/).includes("..")) {
|
|
3171
3210
|
logger5.warn(`Refusing to remove stale gh file with suspicious path: "${relativePath}".`);
|
|
3172
3211
|
return;
|
|
3173
3212
|
}
|
|
3174
|
-
const scopeRoot = scope === "user" ? getHomeDirectory() :
|
|
3213
|
+
const scopeRoot = scope === "user" ? getHomeDirectory() : projectRoot;
|
|
3175
3214
|
try {
|
|
3176
3215
|
checkPathTraversal({ relativePath, intendedRootDir: scopeRoot });
|
|
3177
3216
|
} catch {
|
|
@@ -3343,7 +3382,7 @@ async function fetchSkillFiles(params) {
|
|
|
3343
3382
|
var MAX_WALK_DEPTH = 20;
|
|
3344
3383
|
var MAX_TOTAL_FILES = 1e4;
|
|
3345
3384
|
var MAX_TOTAL_SIZE = 100 * 1024 * 1024;
|
|
3346
|
-
async function walkDirectory(dir,
|
|
3385
|
+
async function walkDirectory(dir, outputRoot, depth = 0, ctx = { totalFiles: 0, totalSize: 0 }, logger5) {
|
|
3347
3386
|
if (depth > MAX_WALK_DEPTH) {
|
|
3348
3387
|
throw new GitClientError(
|
|
3349
3388
|
`Directory tree exceeds max depth of ${MAX_WALK_DEPTH}: "${dir}". Aborting to prevent resource exhaustion.`
|
|
@@ -3358,7 +3397,7 @@ async function walkDirectory(dir, baseDir, depth = 0, ctx = { totalFiles: 0, tot
|
|
|
3358
3397
|
continue;
|
|
3359
3398
|
}
|
|
3360
3399
|
if (await directoryExists(fullPath)) {
|
|
3361
|
-
results.push(...await walkDirectory(fullPath,
|
|
3400
|
+
results.push(...await walkDirectory(fullPath, outputRoot, depth + 1, ctx, logger5));
|
|
3362
3401
|
} else {
|
|
3363
3402
|
const size = await getFileSize(fullPath);
|
|
3364
3403
|
if (size > MAX_FILE_SIZE) {
|
|
@@ -3380,7 +3419,7 @@ async function walkDirectory(dir, baseDir, depth = 0, ctx = { totalFiles: 0, tot
|
|
|
3380
3419
|
);
|
|
3381
3420
|
}
|
|
3382
3421
|
const content = await readFileContent(fullPath);
|
|
3383
|
-
results.push({ relativePath: relative(
|
|
3422
|
+
results.push({ relativePath: relative(outputRoot, fullPath), content, size });
|
|
3384
3423
|
}
|
|
3385
3424
|
}
|
|
3386
3425
|
return results;
|
|
@@ -3434,7 +3473,7 @@ function createEmptyLock() {
|
|
|
3434
3473
|
}
|
|
3435
3474
|
async function readLockFile(params) {
|
|
3436
3475
|
const { logger: logger5 } = params;
|
|
3437
|
-
const lockPath = join11(params.
|
|
3476
|
+
const lockPath = join11(params.projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);
|
|
3438
3477
|
if (!await fileExists(lockPath)) {
|
|
3439
3478
|
logger5.debug("No sources lockfile found, starting fresh.");
|
|
3440
3479
|
return createEmptyLock();
|
|
@@ -3463,7 +3502,7 @@ async function readLockFile(params) {
|
|
|
3463
3502
|
}
|
|
3464
3503
|
async function writeLockFile(params) {
|
|
3465
3504
|
const { logger: logger5 } = params;
|
|
3466
|
-
const lockPath = join11(params.
|
|
3505
|
+
const lockPath = join11(params.projectRoot, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH);
|
|
3467
3506
|
const content = JSON.stringify(params.lock, null, 2) + "\n";
|
|
3468
3507
|
await writeFileContent(lockPath, content);
|
|
3469
3508
|
logger5.debug(`Wrote sources lockfile to ${lockPath}`);
|
|
@@ -3538,7 +3577,7 @@ function getLockedSkillNames(entry) {
|
|
|
3538
3577
|
|
|
3539
3578
|
// src/lib/sources.ts
|
|
3540
3579
|
async function resolveAndFetchSources(params) {
|
|
3541
|
-
const { sources,
|
|
3580
|
+
const { sources, projectRoot, options = {}, logger: logger5 } = params;
|
|
3542
3581
|
if (sources.length === 0) {
|
|
3543
3582
|
return { fetchedSkillCount: 0, sourcesProcessed: 0 };
|
|
3544
3583
|
}
|
|
@@ -3546,7 +3585,7 @@ async function resolveAndFetchSources(params) {
|
|
|
3546
3585
|
logger5.info("Skipping source fetching.");
|
|
3547
3586
|
return { fetchedSkillCount: 0, sourcesProcessed: 0 };
|
|
3548
3587
|
}
|
|
3549
|
-
let lock = options.updateSources ? createEmptyLock() : await readLockFile({
|
|
3588
|
+
let lock = options.updateSources ? createEmptyLock() : await readLockFile({ projectRoot, logger: logger5 });
|
|
3550
3589
|
if (options.frozen) {
|
|
3551
3590
|
const missingKeys = [];
|
|
3552
3591
|
for (const source of sources) {
|
|
@@ -3564,7 +3603,7 @@ async function resolveAndFetchSources(params) {
|
|
|
3564
3603
|
const originalLockJson = JSON.stringify(lock);
|
|
3565
3604
|
const token = GitHubClient.resolveToken(options.token);
|
|
3566
3605
|
const client = new GitHubClient({ token });
|
|
3567
|
-
const localSkillNames = await getLocalSkillDirNames(
|
|
3606
|
+
const localSkillNames = await getLocalSkillDirNames(projectRoot);
|
|
3568
3607
|
let totalSkillCount = 0;
|
|
3569
3608
|
const allFetchedSkillNames = /* @__PURE__ */ new Set();
|
|
3570
3609
|
for (const sourceEntry of sources) {
|
|
@@ -3574,7 +3613,7 @@ async function resolveAndFetchSources(params) {
|
|
|
3574
3613
|
if (transport === "git") {
|
|
3575
3614
|
result = await fetchSourceViaGit({
|
|
3576
3615
|
sourceEntry,
|
|
3577
|
-
|
|
3616
|
+
projectRoot,
|
|
3578
3617
|
lock,
|
|
3579
3618
|
localSkillNames,
|
|
3580
3619
|
alreadyFetchedSkillNames: allFetchedSkillNames,
|
|
@@ -3586,7 +3625,7 @@ async function resolveAndFetchSources(params) {
|
|
|
3586
3625
|
result = await fetchSource({
|
|
3587
3626
|
sourceEntry,
|
|
3588
3627
|
client,
|
|
3589
|
-
|
|
3628
|
+
projectRoot,
|
|
3590
3629
|
lock,
|
|
3591
3630
|
localSkillNames,
|
|
3592
3631
|
alreadyFetchedSkillNames: allFetchedSkillNames,
|
|
@@ -3620,7 +3659,7 @@ async function resolveAndFetchSources(params) {
|
|
|
3620
3659
|
}
|
|
3621
3660
|
lock = { lockfileVersion: lock.lockfileVersion, sources: prunedSources };
|
|
3622
3661
|
if (!options.frozen && JSON.stringify(lock) !== originalLockJson) {
|
|
3623
|
-
await writeLockFile({
|
|
3662
|
+
await writeLockFile({ projectRoot, lock, logger: logger5 });
|
|
3624
3663
|
} else {
|
|
3625
3664
|
logger5.debug("Lockfile unchanged, skipping write.");
|
|
3626
3665
|
}
|
|
@@ -3771,7 +3810,7 @@ async function fetchSource(params) {
|
|
|
3771
3810
|
const {
|
|
3772
3811
|
sourceEntry,
|
|
3773
3812
|
client,
|
|
3774
|
-
|
|
3813
|
+
projectRoot,
|
|
3775
3814
|
localSkillNames,
|
|
3776
3815
|
alreadyFetchedSkillNames,
|
|
3777
3816
|
updateSources,
|
|
@@ -3800,7 +3839,7 @@ async function fetchSource(params) {
|
|
|
3800
3839
|
ref = resolvedSha;
|
|
3801
3840
|
logger5.debug(`Resolved ${sourceKey} ref "${requestedRef}" to SHA: ${resolvedSha}`);
|
|
3802
3841
|
}
|
|
3803
|
-
const curatedDir = join12(
|
|
3842
|
+
const curatedDir = join12(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);
|
|
3804
3843
|
if (locked && resolvedSha === locked.resolvedRef && !updateSources) {
|
|
3805
3844
|
const allExist = await checkLockedSkillsExist(curatedDir, lockedSkillNames);
|
|
3806
3845
|
if (allExist) {
|
|
@@ -3948,7 +3987,7 @@ async function fetchSource(params) {
|
|
|
3948
3987
|
async function fetchSourceViaGit(params) {
|
|
3949
3988
|
const {
|
|
3950
3989
|
sourceEntry,
|
|
3951
|
-
|
|
3990
|
+
projectRoot,
|
|
3952
3991
|
localSkillNames,
|
|
3953
3992
|
alreadyFetchedSkillNames,
|
|
3954
3993
|
updateSources,
|
|
@@ -3975,7 +4014,7 @@ async function fetchSourceViaGit(params) {
|
|
|
3975
4014
|
requestedRef = def.ref;
|
|
3976
4015
|
resolvedSha = def.sha;
|
|
3977
4016
|
}
|
|
3978
|
-
const curatedDir = join12(
|
|
4017
|
+
const curatedDir = join12(projectRoot, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH);
|
|
3979
4018
|
if (locked && resolvedSha === locked.resolvedRef && !updateSources) {
|
|
3980
4019
|
if (await checkLockedSkillsExist(curatedDir, lockedSkillNames)) {
|
|
3981
4020
|
return { skillCount: 0, fetchedSkillNames: lockedSkillNames, updatedLock: lock };
|
|
@@ -4057,8 +4096,8 @@ async function installCommand(logger5, options) {
|
|
|
4057
4096
|
await runRulesyncInstall(logger5, options);
|
|
4058
4097
|
}
|
|
4059
4098
|
async function runRulesyncInstall(logger5, options) {
|
|
4060
|
-
const
|
|
4061
|
-
const apmExists = await apmManifestExists(
|
|
4099
|
+
const projectRoot = process.cwd();
|
|
4100
|
+
const apmExists = await apmManifestExists(projectRoot);
|
|
4062
4101
|
const config = await ConfigResolver.resolve({
|
|
4063
4102
|
configPath: options.configPath,
|
|
4064
4103
|
verbose: options.verbose,
|
|
@@ -4083,7 +4122,7 @@ async function runRulesyncInstall(logger5, options) {
|
|
|
4083
4122
|
logger5.debug(`Installing skills from ${sources.length} source(s)...`);
|
|
4084
4123
|
const result = await resolveAndFetchSources({
|
|
4085
4124
|
sources,
|
|
4086
|
-
|
|
4125
|
+
projectRoot,
|
|
4087
4126
|
options: {
|
|
4088
4127
|
updateSources: options.update,
|
|
4089
4128
|
frozen: options.frozen,
|
|
@@ -4104,14 +4143,14 @@ async function runRulesyncInstall(logger5, options) {
|
|
|
4104
4143
|
}
|
|
4105
4144
|
}
|
|
4106
4145
|
async function runApmInstall(logger5, options) {
|
|
4107
|
-
const
|
|
4108
|
-
if (!await apmManifestExists(
|
|
4146
|
+
const projectRoot = process.cwd();
|
|
4147
|
+
if (!await apmManifestExists(projectRoot)) {
|
|
4109
4148
|
throw new Error(
|
|
4110
4149
|
"--mode apm requires an apm.yml at the project root. Create one or drop --mode apm to fall back to rulesync mode."
|
|
4111
4150
|
);
|
|
4112
4151
|
}
|
|
4113
4152
|
const result = await installApm({
|
|
4114
|
-
|
|
4153
|
+
projectRoot,
|
|
4115
4154
|
options: {
|
|
4116
4155
|
update: options.update,
|
|
4117
4156
|
frozen: options.frozen,
|
|
@@ -4138,7 +4177,7 @@ async function runApmInstall(logger5, options) {
|
|
|
4138
4177
|
}
|
|
4139
4178
|
}
|
|
4140
4179
|
async function runGhInstall(logger5, options) {
|
|
4141
|
-
const
|
|
4180
|
+
const projectRoot = process.cwd();
|
|
4142
4181
|
const config = await ConfigResolver.resolve({
|
|
4143
4182
|
configPath: options.configPath,
|
|
4144
4183
|
verbose: options.verbose,
|
|
@@ -4150,7 +4189,7 @@ async function runGhInstall(logger5, options) {
|
|
|
4150
4189
|
return;
|
|
4151
4190
|
}
|
|
4152
4191
|
const result = await installGh({
|
|
4153
|
-
|
|
4192
|
+
projectRoot,
|
|
4154
4193
|
sources,
|
|
4155
4194
|
options: {
|
|
4156
4195
|
update: options.update,
|
|
@@ -4273,7 +4312,7 @@ async function putCommand({
|
|
|
4273
4312
|
}
|
|
4274
4313
|
const fileContent = stringifyFrontmatter(body, frontmatter);
|
|
4275
4314
|
const command = new RulesyncCommand({
|
|
4276
|
-
|
|
4315
|
+
outputRoot: process.cwd(),
|
|
4277
4316
|
relativeDirPath: RULESYNC_COMMANDS_RELATIVE_DIR_PATH,
|
|
4278
4317
|
relativeFilePath: filename,
|
|
4279
4318
|
frontmatter,
|
|
@@ -4418,7 +4457,7 @@ async function executeConvert(options) {
|
|
|
4418
4457
|
features: options.features ?? ["*"],
|
|
4419
4458
|
global: options.global,
|
|
4420
4459
|
dryRun: options.dryRun,
|
|
4421
|
-
// Always use default
|
|
4460
|
+
// Always use default outputRoots (process.cwd()) and configPath
|
|
4422
4461
|
// verbose and silent are meaningless in MCP context
|
|
4423
4462
|
verbose: false,
|
|
4424
4463
|
silent: true
|
|
@@ -4486,7 +4525,7 @@ var generateOptionsSchema = z10.object({
|
|
|
4486
4525
|
});
|
|
4487
4526
|
async function executeGenerate(options = {}) {
|
|
4488
4527
|
try {
|
|
4489
|
-
const exists = await checkRulesyncDirExists({
|
|
4528
|
+
const exists = await checkRulesyncDirExists({ inputRoot: process.cwd() });
|
|
4490
4529
|
if (!exists) {
|
|
4491
4530
|
return {
|
|
4492
4531
|
success: false,
|
|
@@ -4503,7 +4542,7 @@ async function executeGenerate(options = {}) {
|
|
|
4503
4542
|
simulateCommands: options.simulateCommands,
|
|
4504
4543
|
simulateSubagents: options.simulateSubagents,
|
|
4505
4544
|
simulateSkills: options.simulateSkills,
|
|
4506
|
-
// Always use default
|
|
4545
|
+
// Always use default outputRoots (process.cwd()) and configPath
|
|
4507
4546
|
// verbose and silent are meaningless in MCP context
|
|
4508
4547
|
verbose: false,
|
|
4509
4548
|
silent: true
|
|
@@ -4603,19 +4642,19 @@ async function putHooksFile({ content }) {
|
|
|
4603
4642
|
);
|
|
4604
4643
|
}
|
|
4605
4644
|
try {
|
|
4606
|
-
const
|
|
4645
|
+
const outputRoot = process.cwd();
|
|
4607
4646
|
const paths = RulesyncHooks.getSettablePaths();
|
|
4608
4647
|
const relativeDirPath = paths.relativeDirPath;
|
|
4609
4648
|
const relativeFilePath = paths.relativeFilePath;
|
|
4610
|
-
const fullPath = join14(
|
|
4649
|
+
const fullPath = join14(outputRoot, relativeDirPath, relativeFilePath);
|
|
4611
4650
|
const rulesyncHooks = new RulesyncHooks({
|
|
4612
|
-
|
|
4651
|
+
outputRoot,
|
|
4613
4652
|
relativeDirPath,
|
|
4614
4653
|
relativeFilePath,
|
|
4615
4654
|
fileContent: content,
|
|
4616
4655
|
validate: true
|
|
4617
4656
|
});
|
|
4618
|
-
await ensureDir(join14(
|
|
4657
|
+
await ensureDir(join14(outputRoot, relativeDirPath));
|
|
4619
4658
|
await writeFileContent(fullPath, content);
|
|
4620
4659
|
const relativePathFromCwd = join14(relativeDirPath, relativeFilePath);
|
|
4621
4660
|
return {
|
|
@@ -4633,9 +4672,9 @@ async function putHooksFile({ content }) {
|
|
|
4633
4672
|
}
|
|
4634
4673
|
async function deleteHooksFile() {
|
|
4635
4674
|
try {
|
|
4636
|
-
const
|
|
4675
|
+
const outputRoot = process.cwd();
|
|
4637
4676
|
const paths = RulesyncHooks.getSettablePaths();
|
|
4638
|
-
const filePath = join14(
|
|
4677
|
+
const filePath = join14(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
4639
4678
|
await removeFile(filePath);
|
|
4640
4679
|
const relativePathFromCwd = join14(paths.relativeDirPath, paths.relativeFilePath);
|
|
4641
4680
|
return {
|
|
@@ -4809,7 +4848,7 @@ async function executeImport(options) {
|
|
|
4809
4848
|
// eslint-disable-next-line no-type-assertion/no-type-assertion
|
|
4810
4849
|
features: options.features,
|
|
4811
4850
|
global: options.global,
|
|
4812
|
-
// Always use default
|
|
4851
|
+
// Always use default outputRoots (process.cwd()) and configPath
|
|
4813
4852
|
// verbose and silent are meaningless in MCP context
|
|
4814
4853
|
verbose: false,
|
|
4815
4854
|
silent: true
|
|
@@ -4906,19 +4945,19 @@ async function putMcpFile({ content }) {
|
|
|
4906
4945
|
);
|
|
4907
4946
|
}
|
|
4908
4947
|
try {
|
|
4909
|
-
const
|
|
4948
|
+
const outputRoot = process.cwd();
|
|
4910
4949
|
const paths = RulesyncMcp.getSettablePaths();
|
|
4911
4950
|
const relativeDirPath = paths.recommended.relativeDirPath;
|
|
4912
4951
|
const relativeFilePath = paths.recommended.relativeFilePath;
|
|
4913
|
-
const fullPath = join16(
|
|
4952
|
+
const fullPath = join16(outputRoot, relativeDirPath, relativeFilePath);
|
|
4914
4953
|
const rulesyncMcp = new RulesyncMcp({
|
|
4915
|
-
|
|
4954
|
+
outputRoot,
|
|
4916
4955
|
relativeDirPath,
|
|
4917
4956
|
relativeFilePath,
|
|
4918
4957
|
fileContent: content,
|
|
4919
4958
|
validate: true
|
|
4920
4959
|
});
|
|
4921
|
-
await ensureDir(join16(
|
|
4960
|
+
await ensureDir(join16(outputRoot, relativeDirPath));
|
|
4922
4961
|
await writeFileContent(fullPath, content);
|
|
4923
4962
|
const relativePathFromCwd = join16(relativeDirPath, relativeFilePath);
|
|
4924
4963
|
return {
|
|
@@ -4936,14 +4975,18 @@ async function putMcpFile({ content }) {
|
|
|
4936
4975
|
}
|
|
4937
4976
|
async function deleteMcpFile() {
|
|
4938
4977
|
try {
|
|
4939
|
-
const
|
|
4978
|
+
const outputRoot = process.cwd();
|
|
4940
4979
|
const paths = RulesyncMcp.getSettablePaths();
|
|
4941
4980
|
const recommendedPath = join16(
|
|
4942
|
-
|
|
4981
|
+
outputRoot,
|
|
4943
4982
|
paths.recommended.relativeDirPath,
|
|
4944
4983
|
paths.recommended.relativeFilePath
|
|
4945
4984
|
);
|
|
4946
|
-
const legacyPath = join16(
|
|
4985
|
+
const legacyPath = join16(
|
|
4986
|
+
outputRoot,
|
|
4987
|
+
paths.legacy.relativeDirPath,
|
|
4988
|
+
paths.legacy.relativeFilePath
|
|
4989
|
+
);
|
|
4947
4990
|
await removeFile(recommendedPath);
|
|
4948
4991
|
await removeFile(legacyPath);
|
|
4949
4992
|
const relativePathFromCwd = join16(
|
|
@@ -5042,19 +5085,19 @@ async function putPermissionsFile({ content }) {
|
|
|
5042
5085
|
);
|
|
5043
5086
|
}
|
|
5044
5087
|
try {
|
|
5045
|
-
const
|
|
5088
|
+
const outputRoot = process.cwd();
|
|
5046
5089
|
const paths = RulesyncPermissions.getSettablePaths();
|
|
5047
5090
|
const relativeDirPath = paths.relativeDirPath;
|
|
5048
5091
|
const relativeFilePath = paths.relativeFilePath;
|
|
5049
|
-
const fullPath = join17(
|
|
5092
|
+
const fullPath = join17(outputRoot, relativeDirPath, relativeFilePath);
|
|
5050
5093
|
const rulesyncPermissions = new RulesyncPermissions({
|
|
5051
|
-
|
|
5094
|
+
outputRoot,
|
|
5052
5095
|
relativeDirPath,
|
|
5053
5096
|
relativeFilePath,
|
|
5054
5097
|
fileContent: content,
|
|
5055
5098
|
validate: true
|
|
5056
5099
|
});
|
|
5057
|
-
await ensureDir(join17(
|
|
5100
|
+
await ensureDir(join17(outputRoot, relativeDirPath));
|
|
5058
5101
|
await writeFileContent(fullPath, content);
|
|
5059
5102
|
const relativePathFromCwd = join17(relativeDirPath, relativeFilePath);
|
|
5060
5103
|
return {
|
|
@@ -5072,9 +5115,9 @@ async function putPermissionsFile({ content }) {
|
|
|
5072
5115
|
}
|
|
5073
5116
|
async function deletePermissionsFile() {
|
|
5074
5117
|
try {
|
|
5075
|
-
const
|
|
5118
|
+
const outputRoot = process.cwd();
|
|
5076
5119
|
const paths = RulesyncPermissions.getSettablePaths();
|
|
5077
|
-
const filePath = join17(
|
|
5120
|
+
const filePath = join17(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
5078
5121
|
await removeFile(filePath);
|
|
5079
5122
|
const relativePathFromCwd = join17(paths.relativeDirPath, paths.relativeFilePath);
|
|
5080
5123
|
return {
|
|
@@ -5212,7 +5255,7 @@ async function putRule({
|
|
|
5212
5255
|
);
|
|
5213
5256
|
}
|
|
5214
5257
|
const rule = new RulesyncRule({
|
|
5215
|
-
|
|
5258
|
+
outputRoot: process.cwd(),
|
|
5216
5259
|
relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
|
|
5217
5260
|
relativeFilePath: filename,
|
|
5218
5261
|
frontmatter,
|
|
@@ -5419,7 +5462,7 @@ async function putSkill({
|
|
|
5419
5462
|
}
|
|
5420
5463
|
const aiDirFiles = otherFiles.map(mcpSkillFileToAiDirFile);
|
|
5421
5464
|
const skill = new RulesyncSkill({
|
|
5422
|
-
|
|
5465
|
+
outputRoot: process.cwd(),
|
|
5423
5466
|
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
5424
5467
|
dirName,
|
|
5425
5468
|
frontmatter,
|
|
@@ -5636,7 +5679,7 @@ async function putSubagent({
|
|
|
5636
5679
|
);
|
|
5637
5680
|
}
|
|
5638
5681
|
const subagent = new RulesyncSubagent({
|
|
5639
|
-
|
|
5682
|
+
outputRoot: process.cwd(),
|
|
5640
5683
|
relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
|
|
5641
5684
|
relativeFilePath: filename,
|
|
5642
5685
|
frontmatter,
|
|
@@ -6435,7 +6478,7 @@ function wrapCommand({
|
|
|
6435
6478
|
}
|
|
6436
6479
|
|
|
6437
6480
|
// src/cli/index.ts
|
|
6438
|
-
var getVersion = () => "8.
|
|
6481
|
+
var getVersion = () => "8.14.0";
|
|
6439
6482
|
function wrapCommand2(name, errorCode, handler) {
|
|
6440
6483
|
return wrapCommand({ name, errorCode, handler, getVersion });
|
|
6441
6484
|
}
|
|
@@ -6552,8 +6595,12 @@ var main = async () => {
|
|
|
6552
6595
|
`Comma-separated list of features to generate (${ALL_FEATURES.join(",")}) or '*' for all`,
|
|
6553
6596
|
parseCommaSeparatedList
|
|
6554
6597
|
).option("--delete", "Delete all existing files in output directories before generating").option(
|
|
6598
|
+
"-o, --output-roots <paths>",
|
|
6599
|
+
"Output root directories to generate files into (comma-separated for multiple paths)",
|
|
6600
|
+
parseCommaSeparatedList
|
|
6601
|
+
).option(
|
|
6555
6602
|
"-b, --base-dir <paths>",
|
|
6556
|
-
"
|
|
6603
|
+
"[Deprecated] Use --output-roots instead. Output root directories (comma-separated for multiple paths)",
|
|
6557
6604
|
parseCommaSeparatedList
|
|
6558
6605
|
).option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").option("-c, --config <path>", "Path to configuration file").option("-g, --global", "Generate for global(user scope) configuration files").option(
|
|
6559
6606
|
"--simulate-commands",
|
|
@@ -6564,6 +6611,9 @@ var main = async () => {
|
|
|
6564
6611
|
).option(
|
|
6565
6612
|
"--simulate-skills",
|
|
6566
6613
|
"Generate simulated skills. This feature is only available for copilot, cursor and codexcli."
|
|
6614
|
+
).option(
|
|
6615
|
+
"--input-root <path>",
|
|
6616
|
+
"Path to the directory containing .rulesync/ (parent of .rulesync/)"
|
|
6567
6617
|
).option("--dry-run", "Dry run: show changes without writing files").option("--check", "Check if files are up to date (exits with code 1 if changes needed)").action(
|
|
6568
6618
|
wrapCommand2("generate", "GENERATION_FAILED", async (logger5, options) => {
|
|
6569
6619
|
await generateCommand(logger5, options);
|