rulesync 9.4.0 → 9.6.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 +1 -1
- package/dist/cli/index.cjs +33 -8
- package/dist/cli/index.js +34 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-BxqZVTKb.js → import-B6nwZmGl.js} +729 -103
- package/dist/import-B6nwZmGl.js.map +1 -0
- package/dist/{import-DVGMvuhx.cjs → import-CiS5ckUo.cjs} +743 -111
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -3
- package/dist/import-BxqZVTKb.js.map +0 -1
|
@@ -31,11 +31,11 @@ node_os = __toESM(node_os, 1);
|
|
|
31
31
|
let es_toolkit = require("es-toolkit");
|
|
32
32
|
let globby = require("globby");
|
|
33
33
|
let node_util = require("node:util");
|
|
34
|
-
let js_yaml = require("js-yaml");
|
|
35
34
|
let smol_toml = require("smol-toml");
|
|
36
35
|
smol_toml = __toESM(smol_toml, 1);
|
|
37
36
|
let gray_matter = require("gray-matter");
|
|
38
37
|
gray_matter = __toESM(gray_matter, 1);
|
|
38
|
+
let js_yaml = require("js-yaml");
|
|
39
39
|
let es_toolkit_object = require("es-toolkit/object");
|
|
40
40
|
let _toon_format_toon = require("@toon-format/toon");
|
|
41
41
|
//#region src/types/features.ts
|
|
@@ -114,22 +114,22 @@ function formatError(error) {
|
|
|
114
114
|
}
|
|
115
115
|
//#endregion
|
|
116
116
|
//#region src/constants/rulesync-paths.ts
|
|
117
|
-
const { join: join$
|
|
117
|
+
const { join: join$249 } = node_path.posix;
|
|
118
118
|
const RULESYNC_CONFIG_RELATIVE_FILE_PATH = "rulesync.jsonc";
|
|
119
119
|
const RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH = "rulesync.local.jsonc";
|
|
120
120
|
const RULESYNC_RELATIVE_DIR_PATH = ".rulesync";
|
|
121
|
-
const RULESYNC_RULES_RELATIVE_DIR_PATH = join$
|
|
122
|
-
const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$
|
|
123
|
-
const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$
|
|
124
|
-
const RULESYNC_MCP_RELATIVE_FILE_PATH = join$
|
|
125
|
-
const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$
|
|
126
|
-
const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$
|
|
121
|
+
const RULESYNC_RULES_RELATIVE_DIR_PATH = join$249(RULESYNC_RELATIVE_DIR_PATH, "rules");
|
|
122
|
+
const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$249(RULESYNC_RELATIVE_DIR_PATH, "commands");
|
|
123
|
+
const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$249(RULESYNC_RELATIVE_DIR_PATH, "subagents");
|
|
124
|
+
const RULESYNC_MCP_RELATIVE_FILE_PATH = join$249(RULESYNC_RELATIVE_DIR_PATH, "mcp.json");
|
|
125
|
+
const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$249(RULESYNC_RELATIVE_DIR_PATH, "hooks.json");
|
|
126
|
+
const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$249(RULESYNC_RELATIVE_DIR_PATH, "permissions.json");
|
|
127
127
|
const RULESYNC_AIIGNORE_FILE_NAME = ".aiignore";
|
|
128
|
-
const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$
|
|
128
|
+
const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$249(RULESYNC_RELATIVE_DIR_PATH, ".aiignore");
|
|
129
129
|
const RULESYNC_IGNORE_RELATIVE_FILE_PATH = ".rulesyncignore";
|
|
130
130
|
const RULESYNC_OVERVIEW_FILE_NAME = "overview.md";
|
|
131
|
-
const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$
|
|
132
|
-
const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$
|
|
131
|
+
const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$249(RULESYNC_RELATIVE_DIR_PATH, "skills");
|
|
132
|
+
const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$249(RULESYNC_SKILLS_RELATIVE_DIR_PATH, ".curated");
|
|
133
133
|
const RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync.lock";
|
|
134
134
|
const RULESYNC_MCP_FILE_NAME = "mcp.json";
|
|
135
135
|
const RULESYNC_HOOKS_FILE_NAME = "hooks.json";
|
|
@@ -167,6 +167,7 @@ const rulesProcessorToolTargetTuple = [
|
|
|
167
167
|
"opencode",
|
|
168
168
|
"pi",
|
|
169
169
|
"qwencode",
|
|
170
|
+
"reasonix",
|
|
170
171
|
"replit",
|
|
171
172
|
"roo",
|
|
172
173
|
"rovodev",
|
|
@@ -314,6 +315,7 @@ const skillsProcessorToolTargetTuple = [
|
|
|
314
315
|
"opencode",
|
|
315
316
|
"pi",
|
|
316
317
|
"qwencode",
|
|
318
|
+
"reasonix",
|
|
317
319
|
"replit",
|
|
318
320
|
"roo",
|
|
319
321
|
"rovodev",
|
|
@@ -878,7 +880,7 @@ const SourceEntrySchema = zod_mini.z.object({
|
|
|
878
880
|
scope: (0, zod_mini.optional)(zod_mini.z.enum(["project", "user"]))
|
|
879
881
|
});
|
|
880
882
|
const ConfigParamsSchema = zod_mini.z.object({
|
|
881
|
-
outputRoots: zod_mini.z.array(zod_mini.z.string()),
|
|
883
|
+
outputRoots: zod_mini.z.union([zod_mini.z.array(zod_mini.z.string()), zod_mini.z.record(zod_mini.z.string(), zod_mini.z.union([zod_mini.z.string(), zod_mini.z.array(zod_mini.z.string())]))]),
|
|
882
884
|
targets: RulesyncConfigTargetsSchema,
|
|
883
885
|
features: RulesyncFeaturesSchema,
|
|
884
886
|
verbose: zod_mini.z.boolean(),
|
|
@@ -983,6 +985,7 @@ var Config = class Config {
|
|
|
983
985
|
const resolvedTargets = targets ?? [];
|
|
984
986
|
const resolvedFeatures = features ?? [];
|
|
985
987
|
this.validateObjectFormTargetKeys(resolvedTargets);
|
|
988
|
+
this.validateObjectFormOutputRootKeys(outputRoots);
|
|
986
989
|
this.validateConflictingTargets(resolvedTargets);
|
|
987
990
|
if (dryRun && check) throw new Error("--dry-run and --check cannot be used together");
|
|
988
991
|
this.outputRoots = outputRoots;
|
|
@@ -1020,6 +1023,11 @@ var Config = class Config {
|
|
|
1020
1023
|
if (!validTargets.has(key)) throw new Error(`Unknown target '${key}'. Valid targets: ${ALL_TOOL_TARGETS.join(", ")}.`);
|
|
1021
1024
|
}
|
|
1022
1025
|
}
|
|
1026
|
+
validateObjectFormOutputRootKeys(outputRoots) {
|
|
1027
|
+
if (Array.isArray(outputRoots)) return;
|
|
1028
|
+
const validTargets = new Set(ALL_TOOL_TARGETS);
|
|
1029
|
+
for (const key of Object.keys(outputRoots)) if (!validTargets.has(key)) throw new Error(`Unknown outputRoots target '${key}'. Valid targets: ${ALL_TOOL_TARGETS.join(", ")}.`);
|
|
1030
|
+
}
|
|
1023
1031
|
validateConflictingTargets(targets) {
|
|
1024
1032
|
const has = (target) => {
|
|
1025
1033
|
if (Array.isArray(targets)) return targets.includes(target);
|
|
@@ -1027,8 +1035,19 @@ var Config = class Config {
|
|
|
1027
1035
|
};
|
|
1028
1036
|
for (const [target1, target2] of CONFLICTING_TARGET_PAIRS) if (has(target1) && has(target2)) throw new Error(`Conflicting targets: '${target1}' and '${target2}' cannot be used together. Please choose one.`);
|
|
1029
1037
|
}
|
|
1030
|
-
getOutputRoots() {
|
|
1031
|
-
return this.outputRoots;
|
|
1038
|
+
getOutputRoots(target) {
|
|
1039
|
+
if (Array.isArray(this.outputRoots)) return this.outputRoots;
|
|
1040
|
+
if (target) {
|
|
1041
|
+
const targetOutputRoots = this.outputRoots[target];
|
|
1042
|
+
if (targetOutputRoots === void 0) return [];
|
|
1043
|
+
return Array.isArray(targetOutputRoots) ? targetOutputRoots : [targetOutputRoots];
|
|
1044
|
+
}
|
|
1045
|
+
const allRoots = [];
|
|
1046
|
+
for (const value of Object.values(this.outputRoots)) {
|
|
1047
|
+
if (value === void 0) continue;
|
|
1048
|
+
allRoots.push(...Array.isArray(value) ? value : [value]);
|
|
1049
|
+
}
|
|
1050
|
+
return [...new Set(allRoots)];
|
|
1032
1051
|
}
|
|
1033
1052
|
/**
|
|
1034
1053
|
* Filter an arbitrary string-key list down to the known `ToolTarget` set,
|
|
@@ -1390,10 +1409,21 @@ var ConfigResolver = class {
|
|
|
1390
1409
|
};
|
|
1391
1410
|
function getOutputRootsInLightOfGlobal({ outputRoots, global }) {
|
|
1392
1411
|
if (global) return [getHomeDirectory()];
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1412
|
+
if (Array.isArray(outputRoots)) {
|
|
1413
|
+
outputRoots.forEach((outputRoot) => {
|
|
1414
|
+
validateOutputRoot(outputRoot);
|
|
1415
|
+
});
|
|
1416
|
+
return outputRoots.map((outputRoot) => (0, node_path.resolve)(outputRoot));
|
|
1417
|
+
}
|
|
1418
|
+
const resolvedOutputRoots = {};
|
|
1419
|
+
for (const [target, targetOutputRoots] of Object.entries(outputRoots)) {
|
|
1420
|
+
const roots = Array.isArray(targetOutputRoots) ? targetOutputRoots : [targetOutputRoots];
|
|
1421
|
+
roots.forEach((outputRoot) => {
|
|
1422
|
+
validateOutputRoot(outputRoot);
|
|
1423
|
+
});
|
|
1424
|
+
resolvedOutputRoots[target] = Array.isArray(targetOutputRoots) ? roots.map((outputRoot) => (0, node_path.resolve)(outputRoot)) : (0, node_path.resolve)(targetOutputRoots);
|
|
1425
|
+
}
|
|
1426
|
+
return resolvedOutputRoots;
|
|
1397
1427
|
}
|
|
1398
1428
|
function extractConfigFileTargets(targets) {
|
|
1399
1429
|
if (targets === void 0) return void 0;
|
|
@@ -1422,7 +1452,7 @@ function isRecord(value) {
|
|
|
1422
1452
|
* conversion, etc.) should reject inputs whose prototype could carry
|
|
1423
1453
|
* malicious accessor descriptors.
|
|
1424
1454
|
*/
|
|
1425
|
-
function isPlainObject(value) {
|
|
1455
|
+
function isPlainObject$1(value) {
|
|
1426
1456
|
if (!isRecord(value)) return false;
|
|
1427
1457
|
const proto = Object.getPrototypeOf(value);
|
|
1428
1458
|
return proto === null || proto === Object.prototype;
|
|
@@ -1434,11 +1464,43 @@ function isStringArray(value) {
|
|
|
1434
1464
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1435
1465
|
}
|
|
1436
1466
|
//#endregion
|
|
1467
|
+
//#region src/utils/yaml.ts
|
|
1468
|
+
/**
|
|
1469
|
+
* js-yaml v5's `reason` for an empty document. `load("")` — and any
|
|
1470
|
+
* whitespace/comment-only input — throws a `YAMLException` with this reason
|
|
1471
|
+
* instead of returning `undefined` the way v4 did.
|
|
1472
|
+
*/
|
|
1473
|
+
const EMPTY_INPUT_REASON = "expected a document, but the input is empty";
|
|
1474
|
+
/**
|
|
1475
|
+
* Load YAML content while preserving js-yaml v4's behavior of returning
|
|
1476
|
+
* `undefined` for empty documents (empty, whitespace-only, or comment-only
|
|
1477
|
+
* input).
|
|
1478
|
+
*
|
|
1479
|
+
* js-yaml v5 changed `load("")` to **throw** ("expected a document, but the
|
|
1480
|
+
* input is empty") instead of returning `undefined`. Many call sites here parse
|
|
1481
|
+
* config/lock/frontmatter files that can legitimately be empty and rely on the
|
|
1482
|
+
* old `undefined` sentinel (`load(input) ?? {}`, `if (loaded === undefined)`,
|
|
1483
|
+
* "an empty file parses to undefined/null"). Routing every load through this
|
|
1484
|
+
* wrapper keeps that contract in one place so a future js-yaml bump cannot
|
|
1485
|
+
* reintroduce the empty-input regression. Genuine parse errors still propagate.
|
|
1486
|
+
*
|
|
1487
|
+
* @see https://github.com/nodeca/js-yaml/blob/master/docs/migrate_v4_to_v5.md#empty-input-throws
|
|
1488
|
+
*/
|
|
1489
|
+
function loadYaml(content) {
|
|
1490
|
+
if (content.trim() === "") return;
|
|
1491
|
+
try {
|
|
1492
|
+
return (0, js_yaml.load)(content);
|
|
1493
|
+
} catch (error) {
|
|
1494
|
+
if (error instanceof js_yaml.YAMLException && error.reason === EMPTY_INPUT_REASON) return;
|
|
1495
|
+
throw error;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
//#endregion
|
|
1437
1499
|
//#region src/utils/frontmatter.ts
|
|
1438
1500
|
function deepRemoveNullishValue(value) {
|
|
1439
1501
|
if (value === null || value === void 0) return;
|
|
1440
1502
|
if (Array.isArray(value)) return value.map((item) => deepRemoveNullishValue(item)).filter((item) => item !== void 0);
|
|
1441
|
-
if (isPlainObject(value)) {
|
|
1503
|
+
if (isPlainObject$1(value)) {
|
|
1442
1504
|
const result = {};
|
|
1443
1505
|
for (const [key, val] of Object.entries(value)) {
|
|
1444
1506
|
const cleaned = deepRemoveNullishValue(val);
|
|
@@ -1461,7 +1523,7 @@ function deepFlattenStringsValue(value) {
|
|
|
1461
1523
|
if (value === null || value === void 0) return;
|
|
1462
1524
|
if (typeof value === "string") return value.replace(/\n+/g, " ").trim();
|
|
1463
1525
|
if (Array.isArray(value)) return value.map((item) => deepFlattenStringsValue(item)).filter((item) => item !== void 0);
|
|
1464
|
-
if (isPlainObject(value)) {
|
|
1526
|
+
if (isPlainObject$1(value)) {
|
|
1465
1527
|
const result = {};
|
|
1466
1528
|
for (const [key, val] of Object.entries(value)) {
|
|
1467
1529
|
const cleaned = deepFlattenStringsValue(val);
|
|
@@ -1484,7 +1546,7 @@ function stringifyFrontmatter(body, frontmatter, options) {
|
|
|
1484
1546
|
const { avoidBlockScalars = false } = options ?? {};
|
|
1485
1547
|
const cleanFrontmatter = avoidBlockScalars ? deepFlattenStringsObject(frontmatter) : deepRemoveNullishObject(frontmatter);
|
|
1486
1548
|
if (avoidBlockScalars) return gray_matter.default.stringify(body, cleanFrontmatter, { engines: { yaml: {
|
|
1487
|
-
parse: (input) => (
|
|
1549
|
+
parse: (input) => loadYaml(input) ?? {},
|
|
1488
1550
|
stringify: (data) => (0, js_yaml.dump)(data, { lineWidth: -1 })
|
|
1489
1551
|
} } });
|
|
1490
1552
|
return gray_matter.default.stringify(body, cleanFrontmatter);
|
|
@@ -1523,7 +1585,7 @@ function tryJsonEquivalent(a, b) {
|
|
|
1523
1585
|
}
|
|
1524
1586
|
function tryYamlEquivalent(a, b) {
|
|
1525
1587
|
try {
|
|
1526
|
-
return (0, node_util.isDeepStrictEqual)((
|
|
1588
|
+
return (0, node_util.isDeepStrictEqual)(loadYaml(a), loadYaml(b));
|
|
1527
1589
|
} catch {
|
|
1528
1590
|
return;
|
|
1529
1591
|
}
|
|
@@ -3330,7 +3392,7 @@ var GooseCommand = class GooseCommand extends ToolCommand {
|
|
|
3330
3392
|
const where = (0, node_path.join)(this.relativeDirPath, this.relativeFilePath);
|
|
3331
3393
|
let parsed;
|
|
3332
3394
|
try {
|
|
3333
|
-
parsed = (
|
|
3395
|
+
parsed = loadYaml(content);
|
|
3334
3396
|
} catch (error) {
|
|
3335
3397
|
throw new Error(`Failed to parse Goose recipe (${where}): ${formatError(error)}`, { cause: error });
|
|
3336
3398
|
}
|
|
@@ -3984,7 +4046,7 @@ function omitPrototypePollutionKeys(record) {
|
|
|
3984
4046
|
//#region src/features/shared/shared-config-gateway.ts
|
|
3985
4047
|
function sanitizeSharedConfigValue(value) {
|
|
3986
4048
|
if (Array.isArray(value)) return value.map(sanitizeSharedConfigValue);
|
|
3987
|
-
if (!isPlainObject(value)) return value;
|
|
4049
|
+
if (!isPlainObject$1(value)) return value;
|
|
3988
4050
|
const result = {};
|
|
3989
4051
|
for (const [key, nested] of Object.entries(omitPrototypePollutionKeys(value))) result[key] = sanitizeSharedConfigValue(nested);
|
|
3990
4052
|
return result;
|
|
@@ -4000,14 +4062,14 @@ function parseSharedConfig({ format, fileContent, filePath, invalidRootPolicy =
|
|
|
4000
4062
|
const at = filePath === void 0 ? "" : ` at ${filePath}`;
|
|
4001
4063
|
let parsed;
|
|
4002
4064
|
try {
|
|
4003
|
-
if (format === "yaml") parsed = (
|
|
4065
|
+
if (format === "yaml") parsed = loadYaml(fileContent);
|
|
4004
4066
|
else if (format === "json") parsed = JSON.parse(fileContent);
|
|
4005
4067
|
else parsed = (0, jsonc_parser.parse)(fileContent);
|
|
4006
4068
|
} catch (error) {
|
|
4007
4069
|
throw new Error(`Failed to parse shared config${at}: ${formatError(error)}`, { cause: error });
|
|
4008
4070
|
}
|
|
4009
4071
|
if (parsed === void 0 || parsed === null) return {};
|
|
4010
|
-
if (!isPlainObject(parsed)) {
|
|
4072
|
+
if (!isPlainObject$1(parsed)) {
|
|
4011
4073
|
if (invalidRootPolicy === "error") throw new Error(`Failed to parse shared config${at}: expected a mapping at the root`);
|
|
4012
4074
|
return {};
|
|
4013
4075
|
}
|
|
@@ -4050,7 +4112,7 @@ function mergeSharedConfigDeep({ base, patch }) {
|
|
|
4050
4112
|
for (const [key, patchValue] of Object.entries(patch)) {
|
|
4051
4113
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
4052
4114
|
const baseValue = result[key];
|
|
4053
|
-
if (isPlainObject(baseValue) && isPlainObject(patchValue)) result[key] = mergeSharedConfigDeep({
|
|
4115
|
+
if (isPlainObject$1(baseValue) && isPlainObject$1(patchValue)) result[key] = mergeSharedConfigDeep({
|
|
4054
4116
|
base: baseValue,
|
|
4055
4117
|
patch: patchValue
|
|
4056
4118
|
});
|
|
@@ -4684,6 +4746,8 @@ const REASONIX_GLOBAL_PERMISSIONS_FILE_NAME = REASONIX_GLOBAL_MCP_FILE_NAME;
|
|
|
4684
4746
|
const REASONIX_DIR = REASONIX_GLOBAL_DIR;
|
|
4685
4747
|
const REASONIX_SETTINGS_FILE_NAME = "settings.json";
|
|
4686
4748
|
const REASONIX_COMMANDS_DIR_PATH = (0, node_path.join)(REASONIX_DIR, "commands");
|
|
4749
|
+
const REASONIX_RULE_FILE_NAME = "REASONIX.md";
|
|
4750
|
+
const REASONIX_SKILLS_DIR_PATH = (0, node_path.join)(REASONIX_DIR, "skills");
|
|
4687
4751
|
//#endregion
|
|
4688
4752
|
//#region src/features/commands/reasonix-command.ts
|
|
4689
4753
|
/**
|
|
@@ -5089,8 +5153,8 @@ var RovodevCommand = class RovodevCommand extends ToolCommand {
|
|
|
5089
5153
|
const existingContent = await readFileContentOrNull((0, node_path.join)(outputRoot, ROVODEV_DIR, ROVODEV_PROMPTS_FILE_NAME));
|
|
5090
5154
|
let existing = {};
|
|
5091
5155
|
if (existingContent) try {
|
|
5092
|
-
const parsed = (
|
|
5093
|
-
if (isPlainObject(parsed)) existing = parsed;
|
|
5156
|
+
const parsed = loadYaml(existingContent);
|
|
5157
|
+
if (isPlainObject$1(parsed)) existing = parsed;
|
|
5094
5158
|
} catch {}
|
|
5095
5159
|
const prompts = rovodevCommands.map((command) => ({
|
|
5096
5160
|
name: command.getName(),
|
|
@@ -5120,11 +5184,11 @@ async function lookupPromptDescription({ outputRoot, relativeFilePath, name }) {
|
|
|
5120
5184
|
if (!manifestContent) return "";
|
|
5121
5185
|
let parsed;
|
|
5122
5186
|
try {
|
|
5123
|
-
parsed = (
|
|
5187
|
+
parsed = loadYaml(manifestContent);
|
|
5124
5188
|
} catch {
|
|
5125
5189
|
return "";
|
|
5126
5190
|
}
|
|
5127
|
-
if (!isPlainObject(parsed) || !Array.isArray(parsed.prompts)) return "";
|
|
5191
|
+
if (!isPlainObject$1(parsed) || !Array.isArray(parsed.prompts)) return "";
|
|
5128
5192
|
const expectedContentFile = toPosixPath((0, node_path.join)("prompts", relativeFilePath));
|
|
5129
5193
|
const entry = parsed.prompts.find((candidate) => isRecord(candidate) && (candidate.content_file === expectedContentFile || candidate.name === name));
|
|
5130
5194
|
return isRecord(entry) && typeof entry.description === "string" ? entry.description : "";
|
|
@@ -6162,18 +6226,25 @@ const QWENCODE_HOOK_EVENTS = [
|
|
|
6162
6226
|
* Reasonix's `.reasonix/settings.json` (project) / `~/.reasonix/settings.json`
|
|
6163
6227
|
* (global) documents a ten-event surface (`PreToolUse`, `PostToolUse`,
|
|
6164
6228
|
* `UserPromptSubmit`, `Stop`, `PostLLMCall`, `SessionStart`, `SessionEnd`,
|
|
6165
|
-
* `SubagentStop`, `Notification`, `PreCompact`)
|
|
6166
|
-
*
|
|
6167
|
-
* `UserPromptSubmit` ← `beforeSubmitPrompt`,
|
|
6168
|
-
*
|
|
6169
|
-
*
|
|
6229
|
+
* `SubagentStop`, `Notification`, `PreCompact`). The eight events with a clean
|
|
6230
|
+
* canonical equivalent are mapped: `PreToolUse`, `PostToolUse`,
|
|
6231
|
+
* `UserPromptSubmit` ← `beforeSubmitPrompt`, `Stop`, `SessionStart`,
|
|
6232
|
+
* `SessionEnd`, `SubagentStop`, and `PostLLMCall` ← `postModelInvocation`.
|
|
6233
|
+
* (`Notification` and `PreCompact` have no canonical event and are left out.)
|
|
6234
|
+
* `match` (Reasonix's matcher field name) is honored only on
|
|
6235
|
+
* `PreToolUse`/`PostToolUse`, matching the canonical `matcher` field's
|
|
6236
|
+
* tool-event scoping used by other adapters.
|
|
6170
6237
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
6171
6238
|
*/
|
|
6172
6239
|
const REASONIX_HOOK_EVENTS = [
|
|
6173
6240
|
"preToolUse",
|
|
6174
6241
|
"postToolUse",
|
|
6175
6242
|
"beforeSubmitPrompt",
|
|
6176
|
-
"stop"
|
|
6243
|
+
"stop",
|
|
6244
|
+
"sessionStart",
|
|
6245
|
+
"sessionEnd",
|
|
6246
|
+
"subagentStop",
|
|
6247
|
+
"postModelInvocation"
|
|
6177
6248
|
];
|
|
6178
6249
|
/**
|
|
6179
6250
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
@@ -6609,14 +6680,18 @@ const QWENCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
|
|
|
6609
6680
|
/**
|
|
6610
6681
|
* Map canonical camelCase event names to Reasonix PascalCase.
|
|
6611
6682
|
* Reasonix explicitly mirrors Claude Code's hooks model, so it reuses the same
|
|
6612
|
-
* PascalCase names for the
|
|
6683
|
+
* PascalCase names for the events rulesync maps.
|
|
6613
6684
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
6614
6685
|
*/
|
|
6615
6686
|
const CANONICAL_TO_REASONIX_EVENT_NAMES = {
|
|
6616
6687
|
preToolUse: "PreToolUse",
|
|
6617
6688
|
postToolUse: "PostToolUse",
|
|
6618
6689
|
beforeSubmitPrompt: "UserPromptSubmit",
|
|
6619
|
-
stop: "Stop"
|
|
6690
|
+
stop: "Stop",
|
|
6691
|
+
sessionStart: "SessionStart",
|
|
6692
|
+
sessionEnd: "SessionEnd",
|
|
6693
|
+
subagentStop: "SubagentStop",
|
|
6694
|
+
postModelInvocation: "PostLLMCall"
|
|
6620
6695
|
};
|
|
6621
6696
|
/**
|
|
6622
6697
|
* Map Reasonix PascalCase event names to canonical camelCase.
|
|
@@ -6665,6 +6740,21 @@ function applyCommandPrefix({ def, converterConfig }) {
|
|
|
6665
6740
|
return converterConfig.projectDirVar !== "" && typeof trimmedCommand === "string" && !trimmedCommand.startsWith("$") && (!converterConfig.prefixDotRelativeCommandsOnly || trimmedCommand.startsWith(".")) && typeof trimmedCommand === "string" ? `"${converterConfig.projectDirVar}"/${trimmedCommand.replace(/^\.\//, "")}` : def.command;
|
|
6666
6741
|
}
|
|
6667
6742
|
/**
|
|
6743
|
+
* Emit the configured boolean passthrough fields on the tool side, mapping each
|
|
6744
|
+
* canonical field name to its (possibly renamed) tool field name. Only boolean
|
|
6745
|
+
* values are carried through.
|
|
6746
|
+
*/
|
|
6747
|
+
function emitBooleanPassthroughFields({ def, converterConfig }) {
|
|
6748
|
+
return Object.fromEntries((converterConfig.booleanPassthroughFields ?? []).filter(({ canonical }) => typeof def[canonical] === "boolean").map(({ canonical, tool }) => [tool, def[canonical]]));
|
|
6749
|
+
}
|
|
6750
|
+
/**
|
|
6751
|
+
* Import the configured boolean passthrough fields back into canonical fields,
|
|
6752
|
+
* reversing {@link emitBooleanPassthroughFields}. Only boolean values are read.
|
|
6753
|
+
*/
|
|
6754
|
+
function importBooleanPassthroughFields({ h, converterConfig }) {
|
|
6755
|
+
return Object.fromEntries((converterConfig.booleanPassthroughFields ?? []).filter(({ tool }) => typeof h[tool] === "boolean").map(({ canonical, tool }) => [canonical, h[tool]]));
|
|
6756
|
+
}
|
|
6757
|
+
/**
|
|
6668
6758
|
* Convert the definitions of a single matcher group into tool hook entries,
|
|
6669
6759
|
* honoring supported hook types and passthrough fields.
|
|
6670
6760
|
*/
|
|
@@ -6678,6 +6768,10 @@ function buildToolHooks({ defs, converterConfig }) {
|
|
|
6678
6768
|
converterConfig
|
|
6679
6769
|
});
|
|
6680
6770
|
hooks.push({
|
|
6771
|
+
...emitBooleanPassthroughFields({
|
|
6772
|
+
def,
|
|
6773
|
+
converterConfig
|
|
6774
|
+
}),
|
|
6681
6775
|
type: hookType,
|
|
6682
6776
|
...command !== void 0 && command !== null && { command },
|
|
6683
6777
|
...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
|
|
@@ -6765,6 +6859,10 @@ function toolHookToCanonical({ h, rawEntry, converterConfig }) {
|
|
|
6765
6859
|
...prompt !== void 0 && prompt !== null && { prompt },
|
|
6766
6860
|
...converterConfig.passthroughFields?.includes("name") && typeof h.name === "string" && { name: h.name },
|
|
6767
6861
|
...converterConfig.passthroughFields?.includes("description") && typeof h.description === "string" && { description: h.description },
|
|
6862
|
+
...importBooleanPassthroughFields({
|
|
6863
|
+
h,
|
|
6864
|
+
converterConfig
|
|
6865
|
+
}),
|
|
6768
6866
|
...rawEntry.matcher !== void 0 && rawEntry.matcher !== null && rawEntry.matcher !== "" && { matcher: rawEntry.matcher }
|
|
6769
6867
|
};
|
|
6770
6868
|
}
|
|
@@ -7059,7 +7157,7 @@ function combineAugmentSettings(base, local) {
|
|
|
7059
7157
|
const baseValue = result[key];
|
|
7060
7158
|
if (AUGMENTCODE_REPLACE_KEYS.has(key)) result[key] = localValue;
|
|
7061
7159
|
else if (Array.isArray(localValue) && Array.isArray(baseValue)) result[key] = [...localValue, ...baseValue];
|
|
7062
|
-
else if (isPlainObject(localValue) && isPlainObject(baseValue)) result[key] = combineAugmentSettings(baseValue, localValue);
|
|
7160
|
+
else if (isPlainObject$1(localValue) && isPlainObject$1(baseValue)) result[key] = combineAugmentSettings(baseValue, localValue);
|
|
7063
7161
|
else result[key] = localValue;
|
|
7064
7162
|
}
|
|
7065
7163
|
return result;
|
|
@@ -7103,14 +7201,14 @@ async function readAugmentcodeSettingsWithLocalOverlay({ outputRoot, relativeDir
|
|
|
7103
7201
|
} catch (error) {
|
|
7104
7202
|
throw new Error(`Failed to parse AugmentCode settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
7105
7203
|
}
|
|
7106
|
-
if (!isPlainObject(localParsed)) throw new Error(`Failed to parse AugmentCode settings at ${configPath}: expected a JSON object`);
|
|
7204
|
+
if (!isPlainObject$1(localParsed)) throw new Error(`Failed to parse AugmentCode settings at ${configPath}: expected a JSON object`);
|
|
7107
7205
|
let baseParsed;
|
|
7108
7206
|
try {
|
|
7109
7207
|
baseParsed = JSON.parse(baseContent);
|
|
7110
7208
|
} catch {
|
|
7111
7209
|
return baseContent;
|
|
7112
7210
|
}
|
|
7113
|
-
const merged = combineAugmentSettings(isPlainObject(baseParsed) ? baseParsed : {}, localParsed);
|
|
7211
|
+
const merged = combineAugmentSettings(isPlainObject$1(baseParsed) ? baseParsed : {}, localParsed);
|
|
7114
7212
|
return JSON.stringify(merged, null, 2);
|
|
7115
7213
|
}
|
|
7116
7214
|
//#endregion
|
|
@@ -8695,7 +8793,14 @@ const JUNIE_CONVERTER_CONFIG = {
|
|
|
8695
8793
|
toolToCanonicalEventNames: JUNIE_TO_CANONICAL_EVENT_NAMES,
|
|
8696
8794
|
projectDirVar: "",
|
|
8697
8795
|
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
8698
|
-
noMatcherEvents: /* @__PURE__ */ new Set(["beforeSubmitPrompt", "stop"])
|
|
8796
|
+
noMatcherEvents: /* @__PURE__ */ new Set(["beforeSubmitPrompt", "stop"]),
|
|
8797
|
+
booleanPassthroughFields: [{
|
|
8798
|
+
canonical: "failClosed",
|
|
8799
|
+
tool: "blockOnError"
|
|
8800
|
+
}, {
|
|
8801
|
+
canonical: "async",
|
|
8802
|
+
tool: "async"
|
|
8803
|
+
}]
|
|
8699
8804
|
};
|
|
8700
8805
|
var JunieHooks = class JunieHooks extends ToolHooks {
|
|
8701
8806
|
constructor(params) {
|
|
@@ -9731,8 +9836,9 @@ function reasonixHooksToCanonical(hooks) {
|
|
|
9731
9836
|
* Reasonix hooks live in a Claude-Code-style but standalone JSON file —
|
|
9732
9837
|
* `.reasonix/settings.json` (project) or `~/.reasonix/settings.json`
|
|
9733
9838
|
* (global) — separate from the `[permissions]`/`[[plugins]]` TOML config.
|
|
9734
|
-
*
|
|
9735
|
-
* PreToolUse/PostToolUse/UserPromptSubmit/Stop
|
|
9839
|
+
* The eight upstream events with a clean canonical equivalent are mapped:
|
|
9840
|
+
* PreToolUse/PostToolUse/UserPromptSubmit/Stop plus SessionStart/SessionEnd/
|
|
9841
|
+
* SubagentStop/PostLLMCall (see REASONIX_HOOK_EVENTS).
|
|
9736
9842
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
9737
9843
|
*/
|
|
9738
9844
|
var ReasonixHooks = class ReasonixHooks extends ToolHooks {
|
|
@@ -11811,7 +11917,7 @@ function parseAmpSettingsJsonc(fileContent) {
|
|
|
11811
11917
|
const details = errors.map((error) => `${(0, jsonc_parser.printParseErrorCode)(error.error)} at offset ${error.offset}`).join(", ");
|
|
11812
11918
|
throw new Error(`Failed to parse Amp settings: ${details}`);
|
|
11813
11919
|
}
|
|
11814
|
-
if (!isPlainObject(parsed)) throw new Error("Amp settings must be a JSON object");
|
|
11920
|
+
if (!isPlainObject$1(parsed)) throw new Error("Amp settings must be a JSON object");
|
|
11815
11921
|
return parsed;
|
|
11816
11922
|
}
|
|
11817
11923
|
function filterMcpServers(mcpServers) {
|
|
@@ -12133,7 +12239,7 @@ function parseAugmentcodeSettings(fileContent, relativeDirPath, relativeFilePath
|
|
|
12133
12239
|
} catch (error) {
|
|
12134
12240
|
throw new Error(`Failed to parse AugmentCode settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
12135
12241
|
}
|
|
12136
|
-
if (!isPlainObject(parsed)) throw new Error(`Failed to parse AugmentCode settings at ${configPath}: expected a JSON object`);
|
|
12242
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse AugmentCode settings at ${configPath}: expected a JSON object`);
|
|
12137
12243
|
return parsed;
|
|
12138
12244
|
}
|
|
12139
12245
|
/**
|
|
@@ -12359,7 +12465,7 @@ function parseClineSettings(fileContent, relativeDirPath, relativeFilePath) {
|
|
|
12359
12465
|
} catch (error) {
|
|
12360
12466
|
throw new Error(`Failed to parse Cline MCP settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
12361
12467
|
}
|
|
12362
|
-
if (!isPlainObject(parsed)) throw new Error(`Failed to parse Cline MCP settings at ${configPath}: expected a JSON object`);
|
|
12468
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Cline MCP settings at ${configPath}: expected a JSON object`);
|
|
12363
12469
|
return parsed;
|
|
12364
12470
|
}
|
|
12365
12471
|
/**
|
|
@@ -12630,7 +12736,7 @@ var CodexcliMcp = class CodexcliMcp extends ToolMcp {
|
|
|
12630
12736
|
for (const [key, value] of Object.entries(obj)) {
|
|
12631
12737
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
12632
12738
|
if (value === null) continue;
|
|
12633
|
-
if (isPlainObject(value)) {
|
|
12739
|
+
if (isPlainObject$1(value)) {
|
|
12634
12740
|
const cleaned = this.removeEmptyEntries(value, depth + 1);
|
|
12635
12741
|
if (Object.keys(cleaned).length === 0) continue;
|
|
12636
12742
|
filtered[key] = cleaned;
|
|
@@ -13271,12 +13377,12 @@ function parseGooseConfig(fileContent, relativeDirPath, relativeFilePath) {
|
|
|
13271
13377
|
const configPath = (0, node_path.join)(relativeDirPath, relativeFilePath);
|
|
13272
13378
|
let parsed;
|
|
13273
13379
|
try {
|
|
13274
|
-
parsed = (
|
|
13380
|
+
parsed = loadYaml(fileContent);
|
|
13275
13381
|
} catch (error) {
|
|
13276
13382
|
throw new Error(`Failed to parse Goose config at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
13277
13383
|
}
|
|
13278
13384
|
if (parsed === void 0 || parsed === null) return {};
|
|
13279
|
-
if (!isPlainObject(parsed)) throw new Error(`Failed to parse Goose config at ${configPath}: expected a YAML mapping`);
|
|
13385
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Goose config at ${configPath}: expected a YAML mapping`);
|
|
13280
13386
|
return parsed;
|
|
13281
13387
|
}
|
|
13282
13388
|
/**
|
|
@@ -13321,7 +13427,7 @@ function applyGooseStdioFields(ext, config) {
|
|
|
13321
13427
|
ext.cmd = command;
|
|
13322
13428
|
if (isStringArray(config.args)) ext.args = config.args;
|
|
13323
13429
|
}
|
|
13324
|
-
if (isPlainObject(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
13430
|
+
if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
13325
13431
|
}
|
|
13326
13432
|
/**
|
|
13327
13433
|
* Converts a single rulesync canonical MCP server into a Goose `extensions:` entry.
|
|
@@ -13336,7 +13442,7 @@ function convertServerToGooseExtension(name, config) {
|
|
|
13336
13442
|
if (gooseType === "stdio") applyGooseStdioFields(ext, config);
|
|
13337
13443
|
else if (gooseType === "sse" || gooseType === "streamable_http") {
|
|
13338
13444
|
if (url !== void 0) ext.uri = url;
|
|
13339
|
-
if (isPlainObject(config.headers)) ext.headers = omitPrototypePollutionKeys(config.headers);
|
|
13445
|
+
if (isPlainObject$1(config.headers)) ext.headers = omitPrototypePollutionKeys(config.headers);
|
|
13340
13446
|
}
|
|
13341
13447
|
ext.enabled = config.disabled !== true;
|
|
13342
13448
|
const timeout = resolveGooseTimeout(config);
|
|
@@ -13378,9 +13484,9 @@ function convertFromGooseFormat(extensions) {
|
|
|
13378
13484
|
else if (type === "stdio") server.type = "stdio";
|
|
13379
13485
|
if (typeof ext.cmd === "string") server.command = ext.cmd;
|
|
13380
13486
|
if (isStringArray(ext.args)) server.args = ext.args;
|
|
13381
|
-
if (isPlainObject(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
13487
|
+
if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
13382
13488
|
if (typeof ext.uri === "string") server.url = ext.uri;
|
|
13383
|
-
if (isPlainObject(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
|
|
13489
|
+
if (isPlainObject$1(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
|
|
13384
13490
|
if (ext.enabled === false) server.disabled = true;
|
|
13385
13491
|
if (typeof ext.timeout === "number") server.timeout = ext.timeout;
|
|
13386
13492
|
result[name] = server;
|
|
@@ -13403,7 +13509,7 @@ function buildGoosePluginStdioServer(config) {
|
|
|
13403
13509
|
server.command = command;
|
|
13404
13510
|
if (isStringArray(config.args)) server.args = config.args;
|
|
13405
13511
|
}
|
|
13406
|
-
if (isPlainObject(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13512
|
+
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13407
13513
|
if (typeof config.cwd === "string") server.cwd = config.cwd;
|
|
13408
13514
|
return server;
|
|
13409
13515
|
}
|
|
@@ -13703,7 +13809,7 @@ var GrokcliMcp = class GrokcliMcp extends ToolMcp {
|
|
|
13703
13809
|
for (const [key, value] of Object.entries(obj)) {
|
|
13704
13810
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
13705
13811
|
if (value === null) continue;
|
|
13706
|
-
if (isPlainObject(value)) {
|
|
13812
|
+
if (isPlainObject$1(value)) {
|
|
13707
13813
|
const cleaned = this.removeEmptyEntries(value, depth + 1);
|
|
13708
13814
|
if (Object.keys(cleaned).length === 0) continue;
|
|
13709
13815
|
filtered[key] = cleaned;
|
|
@@ -13765,10 +13871,10 @@ function convertServerToHermes(config) {
|
|
|
13765
13871
|
out.command = command;
|
|
13766
13872
|
if (isStringArray(config.args)) out.args = config.args;
|
|
13767
13873
|
}
|
|
13768
|
-
if (isPlainObject(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
13874
|
+
if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
13769
13875
|
} else if (url !== void 0) {
|
|
13770
13876
|
out.url = url;
|
|
13771
|
-
if (isPlainObject(config.headers)) out.headers = omitPrototypePollutionKeys(config.headers);
|
|
13877
|
+
if (isPlainObject$1(config.headers)) out.headers = omitPrototypePollutionKeys(config.headers);
|
|
13772
13878
|
}
|
|
13773
13879
|
if (config.disabled === true) out.enabled = false;
|
|
13774
13880
|
const timeout = resolveHermesTimeout(config);
|
|
@@ -13813,9 +13919,9 @@ function convertFromHermesFormat(mcpServers) {
|
|
|
13813
13919
|
const server = {};
|
|
13814
13920
|
if (typeof config.command === "string") server.command = config.command;
|
|
13815
13921
|
if (isStringArray(config.args)) server.args = config.args;
|
|
13816
|
-
if (isPlainObject(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13922
|
+
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13817
13923
|
if (typeof config.url === "string") server.url = config.url;
|
|
13818
|
-
if (isPlainObject(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
13924
|
+
if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
13819
13925
|
if (config.enabled === false) server.disabled = true;
|
|
13820
13926
|
if (typeof config.timeout === "number") server.networkTimeout = config.timeout;
|
|
13821
13927
|
if (isRecord(config.tools)) {
|
|
@@ -14752,7 +14858,9 @@ const REASONIX_PLUGIN_FIELDS = [
|
|
|
14752
14858
|
"env",
|
|
14753
14859
|
"url",
|
|
14754
14860
|
"headers",
|
|
14755
|
-
"trusted_read_only_tools"
|
|
14861
|
+
"trusted_read_only_tools",
|
|
14862
|
+
"call_timeout_seconds",
|
|
14863
|
+
"tool_timeout_seconds"
|
|
14756
14864
|
];
|
|
14757
14865
|
var ReasonixMcp = class ReasonixMcp extends ToolMcp {
|
|
14758
14866
|
toml;
|
|
@@ -14984,7 +15092,7 @@ function parseRovodevMcpJson(fileContent, relativeDirPath, relativeFilePath) {
|
|
|
14984
15092
|
} catch (error) {
|
|
14985
15093
|
throw new Error(`Failed to parse Rovodev MCP config at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
14986
15094
|
}
|
|
14987
|
-
if (!isPlainObject(parsed)) throw new Error(`Failed to parse Rovodev MCP config at ${configPath}: expected a JSON object`);
|
|
15095
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Rovodev MCP config at ${configPath}: expected a JSON object`);
|
|
14988
15096
|
return parsed;
|
|
14989
15097
|
}
|
|
14990
15098
|
/**
|
|
@@ -16255,15 +16363,123 @@ const AntigravityCliPermissionsOverrideSchema = zod_mini.z.looseObject({
|
|
|
16255
16363
|
enableTerminalSandbox: zod_mini.z.optional(zod_mini.z.boolean())
|
|
16256
16364
|
});
|
|
16257
16365
|
/**
|
|
16366
|
+
* Tool-scoped override block for AugmentCode. AugmentCode's `toolPermissions[]`
|
|
16367
|
+
* array supports "custom policy" entries the canonical allow/ask/deny model
|
|
16368
|
+
* cannot express: `permission.type` of `webhook-policy` / `script-policy`
|
|
16369
|
+
* (delegating the decision to a `webhookUrl` / `script`) and an `eventType` of
|
|
16370
|
+
* `tool-response` (a post-execution check rather than the default pre-execution
|
|
16371
|
+
* `tool-call`). These are authored here as verbatim `toolPermissions` entries
|
|
16372
|
+
* and prepended — ahead of the canonical-generated basic rules — into the shared
|
|
16373
|
+
* `.augment/settings.json`, so a webhook/script gate or tool-response check is
|
|
16374
|
+
* never shadowed by a regenerated allow/deny/ask entry under AugmentCode's
|
|
16375
|
+
* first-match-wins evaluation. The shared `permission` block continues to drive
|
|
16376
|
+
* the basic `allow` / `deny` / `ask-user` entries. Kept `looseObject` (verbatim
|
|
16377
|
+
* passthrough) so `shellInputRegex`, `eventType`, `webhookUrl`, `script`, and
|
|
16378
|
+
* any future policy field survive untouched. Both project and global scope are
|
|
16379
|
+
* supported.
|
|
16380
|
+
*
|
|
16381
|
+
* @example
|
|
16382
|
+
* { "toolPermissions": [
|
|
16383
|
+
* { "toolName": "github-api",
|
|
16384
|
+
* "permission": { "type": "webhook-policy", "webhookUrl": "https://api.example.com/validate" } },
|
|
16385
|
+
* { "toolName": "view", "eventType": "tool-response", "permission": { "type": "allow" } } ] }
|
|
16386
|
+
*/
|
|
16387
|
+
const AugmentcodePermissionsOverrideSchema = zod_mini.z.looseObject({ toolPermissions: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.looseObject({
|
|
16388
|
+
toolName: zod_mini.z.string(),
|
|
16389
|
+
permission: zod_mini.z.looseObject({ type: zod_mini.z.string() })
|
|
16390
|
+
}))) });
|
|
16391
|
+
/**
|
|
16392
|
+
* Tool-scoped override block for Kiro. Kiro's agent config (`.kiro/agents/<name>.json`)
|
|
16393
|
+
* exposes per-tool `toolsSettings` knobs with no canonical allow/ask/deny
|
|
16394
|
+
* category: the shell auto-trust flags `shell.autoAllowReadonly` /
|
|
16395
|
+
* `shell.denyByDefault`, the `aws` built-in tool's `allowedServices` /
|
|
16396
|
+
* `deniedServices` (+ `autoAllowReadonly`), and the `web_fetch` domain trust
|
|
16397
|
+
* arrays `trusted` / `blocked` (regex host patterns; documented for `web_fetch`
|
|
16398
|
+
* only — `web_search` has no such surface). Fields placed here are deep-merged
|
|
16399
|
+
* (per `toolsSettings` key, override wins at the leaf) into the shared agent
|
|
16400
|
+
* config, while the canonical `permission` block continues to drive
|
|
16401
|
+
* `shell.{allowed,denied}Commands`, `read`/`write`/`grep`/`glob` paths, and the
|
|
16402
|
+
* `web_fetch`/`web_search` `allowedTools` toggles. Kept `looseObject` at every
|
|
16403
|
+
* level (verbatim passthrough) so future Kiro `toolsSettings` fields survive.
|
|
16404
|
+
*
|
|
16405
|
+
* Kiro's MCP `autoApprove` / `disabledTools` lists are intentionally NOT modeled
|
|
16406
|
+
* here: they live in a SEPARATE file (`.kiro/settings/mcp.json`, under
|
|
16407
|
+
* `mcpServers.<name>`), not the agent config this permissions translator writes,
|
|
16408
|
+
* and reconciling them with the canonical `mcp__*` model is a distinct design
|
|
16409
|
+
* question left out of scope.
|
|
16410
|
+
*
|
|
16411
|
+
* @example
|
|
16412
|
+
* { "toolsSettings": { "shell": { "autoAllowReadonly": true },
|
|
16413
|
+
* "aws": { "allowedServices": ["s3"], "deniedServices": ["eks"] },
|
|
16414
|
+
* "web_fetch": { "trusted": [".*github\\.com.*"] } } }
|
|
16415
|
+
*/
|
|
16416
|
+
const KiroPermissionsOverrideSchema = zod_mini.z.looseObject({ toolsSettings: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
16417
|
+
shell: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
16418
|
+
autoAllowReadonly: zod_mini.z.optional(zod_mini.z.boolean()),
|
|
16419
|
+
denyByDefault: zod_mini.z.optional(zod_mini.z.boolean())
|
|
16420
|
+
})),
|
|
16421
|
+
aws: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
16422
|
+
allowedServices: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())),
|
|
16423
|
+
deniedServices: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())),
|
|
16424
|
+
autoAllowReadonly: zod_mini.z.optional(zod_mini.z.boolean())
|
|
16425
|
+
})),
|
|
16426
|
+
web_fetch: zod_mini.z.optional(zod_mini.z.looseObject({
|
|
16427
|
+
trusted: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())),
|
|
16428
|
+
blocked: zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string()))
|
|
16429
|
+
}))
|
|
16430
|
+
})) });
|
|
16431
|
+
/**
|
|
16432
|
+
* Codex CLI-scoped permission override.
|
|
16433
|
+
*
|
|
16434
|
+
* Codex CLI's permission surface is richer than the canonical allow/ask/deny
|
|
16435
|
+
* model: its approval workflow, classic sandbox system, and per-app tool gating
|
|
16436
|
+
* have no canonical category. Author them through a tool-scoped `codexcli`
|
|
16437
|
+
* override whose fields are written verbatim as top-level `.codex/config.toml`
|
|
16438
|
+
* keys (the override wins per key; existing sibling keys the user set directly
|
|
16439
|
+
* are preserved):
|
|
16440
|
+
* - `approval_policy` — `untrusted` | `on-request` | `never`, or a
|
|
16441
|
+
* `{ granular = { … } }` table (kept verbatim; the granular schema has
|
|
16442
|
+
* required fields that are brittle to model as typed keys).
|
|
16443
|
+
* - `sandbox_mode` — `read-only` | `workspace-write` | `danger-full-access`,
|
|
16444
|
+
* with the sibling `sandbox_workspace_write` table (`network_access`,
|
|
16445
|
+
* `writable_roots`, …).
|
|
16446
|
+
* - `apps` — per-app tool gating (`apps.<id>.tools.<tool>.approval_mode` /
|
|
16447
|
+
* `.enabled`, `apps.<id>.default_tools_approval_mode`).
|
|
16448
|
+
* - `approvals_reviewer` — the reviewer-approval surface.
|
|
16449
|
+
*
|
|
16450
|
+
* Two surfaces are deliberately NOT authorable here so the override can never
|
|
16451
|
+
* clobber a feature-owned key: `mcp_servers.*` per-MCP gating is owned by the
|
|
16452
|
+
* MCP feature (`codexcli-mcp.ts` already writes the `mcp_servers` tables in the
|
|
16453
|
+
* same `config.toml`), and `permissions` / `default_permissions` are owned by
|
|
16454
|
+
* the canonical model. Any such key placed in the override is skipped with a
|
|
16455
|
+
* warning. Kept `looseObject` (verbatim passthrough) so future top-level Codex
|
|
16456
|
+
* config keys can be authored without Rulesync modeling each one.
|
|
16457
|
+
*
|
|
16458
|
+
* @see https://developers.openai.com/codex/config-reference
|
|
16459
|
+
* @see https://developers.openai.com/codex/permissions
|
|
16460
|
+
*
|
|
16461
|
+
* @example
|
|
16462
|
+
* { "approval_policy": "on-request", "sandbox_mode": "workspace-write",
|
|
16463
|
+
* "sandbox_workspace_write": { "network_access": true } }
|
|
16464
|
+
*/
|
|
16465
|
+
const CodexcliPermissionsOverrideSchema = zod_mini.z.looseObject({
|
|
16466
|
+
approval_policy: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.looseObject({})])),
|
|
16467
|
+
sandbox_mode: zod_mini.z.optional(zod_mini.z.string()),
|
|
16468
|
+
sandbox_workspace_write: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
16469
|
+
apps: zod_mini.z.optional(zod_mini.z.looseObject({})),
|
|
16470
|
+
approvals_reviewer: zod_mini.z.optional(zod_mini.z.union([zod_mini.z.string(), zod_mini.z.looseObject({})]))
|
|
16471
|
+
});
|
|
16472
|
+
/**
|
|
16258
16473
|
* Permissions configuration.
|
|
16259
16474
|
* Keys are tool category names (e.g., "bash", "edit", "read", "webfetch").
|
|
16260
16475
|
* Values are pattern-to-action mappings for that tool category.
|
|
16261
16476
|
*
|
|
16262
16477
|
* The optional `opencode`/`hermes`/`cline`/`kilo`/`claudecode`/`vibe`/`cursor`/
|
|
16263
16478
|
* `qwencode`/`reasonix`/`factorydroid`/`warp`/`junie`/`takt`/`amp`/
|
|
16264
|
-
* `antigravity-cli` keys are tool-scoped
|
|
16265
|
-
* respective translator (see the matching
|
|
16266
|
-
* other tool reads the shared `permission`
|
|
16479
|
+
* `antigravity-cli`/`augmentcode`/`kiro`/`codexcli` keys are tool-scoped
|
|
16480
|
+
* overrides consumed only by their respective translator (see the matching
|
|
16481
|
+
* `*PermissionsOverrideSchema`); every other tool reads the shared `permission`
|
|
16482
|
+
* block and ignores them.
|
|
16267
16483
|
*
|
|
16268
16484
|
* @example
|
|
16269
16485
|
* {
|
|
@@ -16287,7 +16503,10 @@ const PermissionsConfigSchema = zod_mini.z.looseObject({
|
|
|
16287
16503
|
junie: zod_mini.z.optional(JuniePermissionsOverrideSchema),
|
|
16288
16504
|
takt: zod_mini.z.optional(TaktPermissionsOverrideSchema),
|
|
16289
16505
|
amp: zod_mini.z.optional(AmpPermissionsOverrideSchema),
|
|
16290
|
-
"antigravity-cli": zod_mini.z.optional(AntigravityCliPermissionsOverrideSchema)
|
|
16506
|
+
"antigravity-cli": zod_mini.z.optional(AntigravityCliPermissionsOverrideSchema),
|
|
16507
|
+
augmentcode: zod_mini.z.optional(AugmentcodePermissionsOverrideSchema),
|
|
16508
|
+
kiro: zod_mini.z.optional(KiroPermissionsOverrideSchema),
|
|
16509
|
+
codexcli: zod_mini.z.optional(CodexcliPermissionsOverrideSchema)
|
|
16291
16510
|
});
|
|
16292
16511
|
/**
|
|
16293
16512
|
* Full permissions file schema including optional $schema field.
|
|
@@ -16439,7 +16658,7 @@ function parseAmpSettings(fileContent) {
|
|
|
16439
16658
|
const details = errors.map((error) => `${(0, jsonc_parser.printParseErrorCode)(error.error)} at offset ${error.offset}`).join(", ");
|
|
16440
16659
|
throw new Error(`Failed to parse Amp settings: ${details}`);
|
|
16441
16660
|
}
|
|
16442
|
-
if (!isPlainObject(parsed)) throw new Error("Amp settings must be a JSON object");
|
|
16661
|
+
if (!isPlainObject$1(parsed)) throw new Error("Amp settings must be a JSON object");
|
|
16443
16662
|
return parsed;
|
|
16444
16663
|
}
|
|
16445
16664
|
function toDisableList(value) {
|
|
@@ -16466,7 +16685,7 @@ function toPermissionsList(value) {
|
|
|
16466
16685
|
if (!Array.isArray(value)) return [];
|
|
16467
16686
|
const entries = [];
|
|
16468
16687
|
for (const raw of value) {
|
|
16469
|
-
if (!isPlainObject(raw)) continue;
|
|
16688
|
+
if (!isPlainObject$1(raw)) continue;
|
|
16470
16689
|
const { tool, action } = raw;
|
|
16471
16690
|
if (typeof tool !== "string" || typeof action !== "string") continue;
|
|
16472
16691
|
if (action !== "allow" && action !== "reject" && action !== "ask" && action !== "delegate") continue;
|
|
@@ -16474,7 +16693,7 @@ function toPermissionsList(value) {
|
|
|
16474
16693
|
...raw,
|
|
16475
16694
|
tool,
|
|
16476
16695
|
action,
|
|
16477
|
-
...isPlainObject(raw.matches) ? { matches: raw.matches } : {}
|
|
16696
|
+
...isPlainObject$1(raw.matches) ? { matches: raw.matches } : {}
|
|
16478
16697
|
});
|
|
16479
16698
|
}
|
|
16480
16699
|
return entries;
|
|
@@ -17334,6 +17553,55 @@ function isSpecialEntry(entry) {
|
|
|
17334
17553
|
return false;
|
|
17335
17554
|
}
|
|
17336
17555
|
/**
|
|
17556
|
+
* A composite identity for a special entry, used to de-duplicate authored
|
|
17557
|
+
* (`augmentcode` override) and preserved (existing-file) special entries so the
|
|
17558
|
+
* same policy is not emitted twice into `toolPermissions`. Every field that can
|
|
17559
|
+
* distinguish two special entries is included.
|
|
17560
|
+
*/
|
|
17561
|
+
function specialEntryKey(entry) {
|
|
17562
|
+
return JSON.stringify([
|
|
17563
|
+
entry.toolName,
|
|
17564
|
+
entry.shellInputRegex ?? "",
|
|
17565
|
+
entry.eventType ?? "",
|
|
17566
|
+
entry.permission.type,
|
|
17567
|
+
entry.permission.webhookUrl ?? "",
|
|
17568
|
+
entry.permission.script ?? ""
|
|
17569
|
+
]);
|
|
17570
|
+
}
|
|
17571
|
+
/**
|
|
17572
|
+
* Stable de-duplication of special entries by {@link specialEntryKey}, keeping
|
|
17573
|
+
* the first occurrence (authored entries lead, so an authored policy wins over
|
|
17574
|
+
* an identical preserved one).
|
|
17575
|
+
*/
|
|
17576
|
+
function dedupeSpecialEntries(entries) {
|
|
17577
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17578
|
+
const out = [];
|
|
17579
|
+
for (const entry of entries) {
|
|
17580
|
+
const key = specialEntryKey(entry);
|
|
17581
|
+
if (seen.has(key)) continue;
|
|
17582
|
+
seen.add(key);
|
|
17583
|
+
out.push(entry);
|
|
17584
|
+
}
|
|
17585
|
+
return out;
|
|
17586
|
+
}
|
|
17587
|
+
/**
|
|
17588
|
+
* Validate/normalize the `augmentcode` override's `toolPermissions` array into
|
|
17589
|
+
* typed `AugmentToolPermission` entries, dropping any malformed item (with a
|
|
17590
|
+
* warning so a typo is not lost silently). The override schema is intentionally
|
|
17591
|
+
* loose (verbatim passthrough), so entries are re-parsed through the full entry
|
|
17592
|
+
* schema here to guarantee shape before they are written into the shared
|
|
17593
|
+
* settings file.
|
|
17594
|
+
*/
|
|
17595
|
+
function coerceAuthoredEntries(entries, logger) {
|
|
17596
|
+
const out = [];
|
|
17597
|
+
for (const item of entries) {
|
|
17598
|
+
const parsed = AugmentToolPermissionSchema.safeParse(item);
|
|
17599
|
+
if (parsed.success) out.push(parsed.data);
|
|
17600
|
+
else logger?.warn(`AugmentCode permissions: dropping malformed 'augmentcode.toolPermissions' override entry: ${formatError(parsed.error)}.`);
|
|
17601
|
+
}
|
|
17602
|
+
return out;
|
|
17603
|
+
}
|
|
17604
|
+
/**
|
|
17337
17605
|
* Convert a glob-like pattern into a regex string for AugmentCode's `shellInputRegex`.
|
|
17338
17606
|
* Maps glob `*` to `.*`, `?` to `.`, escapes other regex metacharacters, and anchors at both ends.
|
|
17339
17607
|
*/
|
|
@@ -17464,12 +17732,18 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
17464
17732
|
} catch (error) {
|
|
17465
17733
|
throw new Error(`Failed to parse existing AugmentCode settings at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
17466
17734
|
}
|
|
17735
|
+
const config = rulesyncPermissions.getJson();
|
|
17467
17736
|
const generated = convertRulesyncToAugmentEntries({
|
|
17468
|
-
config
|
|
17737
|
+
config,
|
|
17469
17738
|
logger
|
|
17470
17739
|
});
|
|
17471
17740
|
const existingEntries = settings.toolPermissions ?? [];
|
|
17472
|
-
const
|
|
17741
|
+
const override = config.augmentcode;
|
|
17742
|
+
const authoredEntries = override?.toolPermissions ? coerceAuthoredEntries(override.toolPermissions, logger) : [];
|
|
17743
|
+
const authoredSpecials = authoredEntries.filter((entry) => isSpecialEntry(entry));
|
|
17744
|
+
const authoredBasics = authoredEntries.filter((entry) => !isSpecialEntry(entry));
|
|
17745
|
+
const preservedSpecials = override?.toolPermissions ? [] : existingEntries.filter((entry) => isSpecialEntry(entry));
|
|
17746
|
+
const specialEntries = dedupeSpecialEntries([...authoredSpecials, ...preservedSpecials]);
|
|
17473
17747
|
const basicExistingEntries = existingEntries.filter((entry) => !isSpecialEntry(entry));
|
|
17474
17748
|
const generatedKeys = new Set(generated.map((e) => `${e.toolName}|${e.shellInputRegex ?? ""}|${e.permission.type}`));
|
|
17475
17749
|
const preservedBasicEntries = basicExistingEntries.filter((entry) => {
|
|
@@ -17480,7 +17754,11 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
17480
17754
|
}
|
|
17481
17755
|
return false;
|
|
17482
17756
|
});
|
|
17483
|
-
const sortedBasic = sortAugmentEntries([
|
|
17757
|
+
const sortedBasic = sortAugmentEntries([
|
|
17758
|
+
...generated,
|
|
17759
|
+
...preservedBasicEntries,
|
|
17760
|
+
...authoredBasics
|
|
17761
|
+
]);
|
|
17484
17762
|
const merged = {
|
|
17485
17763
|
...settings,
|
|
17486
17764
|
toolPermissions: [...specialEntries, ...sortedBasic]
|
|
@@ -17504,11 +17782,14 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
17504
17782
|
} catch (error) {
|
|
17505
17783
|
throw new Error(`Failed to parse AugmentCode permissions content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
17506
17784
|
}
|
|
17507
|
-
const
|
|
17508
|
-
|
|
17785
|
+
const allEntries = settings.toolPermissions ?? [];
|
|
17786
|
+
const specialEntries = allEntries.filter((entry) => isSpecialEntry(entry));
|
|
17787
|
+
const result = { ...convertAugmentToRulesyncPermissions({
|
|
17788
|
+
entries: allEntries.filter((entry) => !isSpecialEntry(entry)),
|
|
17509
17789
|
logger: moduleLogger$1
|
|
17510
|
-
});
|
|
17511
|
-
|
|
17790
|
+
}) };
|
|
17791
|
+
if (specialEntries.length > 0) result.augmentcode = { toolPermissions: specialEntries };
|
|
17792
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
17512
17793
|
}
|
|
17513
17794
|
validate() {
|
|
17514
17795
|
try {
|
|
@@ -17642,10 +17923,7 @@ function convertAugmentToRulesyncPermissions({ entries, logger }) {
|
|
|
17642
17923
|
]);
|
|
17643
17924
|
const permission = {};
|
|
17644
17925
|
for (const entry of entries) {
|
|
17645
|
-
if (isSpecialEntry(entry))
|
|
17646
|
-
logger?.warn(`AugmentCode permissions: skipping advanced entry for tool '${entry.toolName}' (type '${entry.permission.type}'${entry.eventType !== void 0 ? `, eventType '${entry.eventType}'` : ""}) on import; rulesync's permission model cannot represent custom policies, eventType, webhookUrl, or script. Such entries are preserved on generate but not imported.`);
|
|
17647
|
-
continue;
|
|
17648
|
-
}
|
|
17926
|
+
if (isSpecialEntry(entry)) continue;
|
|
17649
17927
|
const type = entry.permission.type;
|
|
17650
17928
|
if (!isBasicAugmentType(type)) continue;
|
|
17651
17929
|
const canonical = toCanonicalToolName$5(entry.toolName);
|
|
@@ -18077,6 +18355,13 @@ const WORKSPACE_WIDE_WRITE_PATTERNS = /* @__PURE__ */ new Set([
|
|
|
18077
18355
|
]);
|
|
18078
18356
|
const CODEX_MINIMAL_KEY = ":minimal";
|
|
18079
18357
|
const GLOBAL_WILDCARD_DOMAIN = "*";
|
|
18358
|
+
const CODEXCLI_OVERRIDE_KEYS = [
|
|
18359
|
+
"approval_policy",
|
|
18360
|
+
"sandbox_mode",
|
|
18361
|
+
"sandbox_workspace_write",
|
|
18362
|
+
"apps",
|
|
18363
|
+
"approvals_reviewer"
|
|
18364
|
+
];
|
|
18080
18365
|
var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
18081
18366
|
static getSettablePaths(_options = {}) {
|
|
18082
18367
|
return {
|
|
@@ -18118,6 +18403,11 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
|
18118
18403
|
});
|
|
18119
18404
|
parsed.permissions = permissionsTable;
|
|
18120
18405
|
parsed.default_permissions = RULESYNC_PROFILE_NAME;
|
|
18406
|
+
applyCodexcliOverride({
|
|
18407
|
+
parsed,
|
|
18408
|
+
override: rulesyncPermissions.getJson().codexcli,
|
|
18409
|
+
logger
|
|
18410
|
+
});
|
|
18121
18411
|
return new CodexcliPermissions({
|
|
18122
18412
|
outputRoot,
|
|
18123
18413
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -18142,7 +18432,12 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
|
18142
18432
|
profile,
|
|
18143
18433
|
domainsHadUnknown
|
|
18144
18434
|
});
|
|
18145
|
-
|
|
18435
|
+
const override = extractCodexcliOverride(table);
|
|
18436
|
+
const result = Object.keys(override).length > 0 ? {
|
|
18437
|
+
...config,
|
|
18438
|
+
codexcli: override
|
|
18439
|
+
} : config;
|
|
18440
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
18146
18441
|
}
|
|
18147
18442
|
validate() {
|
|
18148
18443
|
return {
|
|
@@ -18334,6 +18629,30 @@ function toMutableTable(value) {
|
|
|
18334
18629
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
18335
18630
|
return { ...value };
|
|
18336
18631
|
}
|
|
18632
|
+
function isPlainObject(value) {
|
|
18633
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18634
|
+
}
|
|
18635
|
+
function applyCodexcliOverride({ parsed, override, logger }) {
|
|
18636
|
+
if (!override) return;
|
|
18637
|
+
const allowed = new Set(CODEXCLI_OVERRIDE_KEYS);
|
|
18638
|
+
for (const [key, value] of Object.entries(override)) {
|
|
18639
|
+
if (!allowed.has(key)) {
|
|
18640
|
+
logger?.warn(`Codex CLI permission override key "${key}" is not managed and was skipped. "permissions"/"default_permissions" are owned by the canonical permission model and "mcp_servers" gating by the MCP feature.`);
|
|
18641
|
+
continue;
|
|
18642
|
+
}
|
|
18643
|
+
if (value === void 0) continue;
|
|
18644
|
+
const existing = parsed[key];
|
|
18645
|
+
parsed[key] = isPlainObject(existing) && isPlainObject(value) ? {
|
|
18646
|
+
...existing,
|
|
18647
|
+
...value
|
|
18648
|
+
} : value;
|
|
18649
|
+
}
|
|
18650
|
+
}
|
|
18651
|
+
function extractCodexcliOverride(table) {
|
|
18652
|
+
const override = {};
|
|
18653
|
+
for (const key of CODEXCLI_OVERRIDE_KEYS) if (table[key] !== void 0) override[key] = table[key];
|
|
18654
|
+
return override;
|
|
18655
|
+
}
|
|
18337
18656
|
function toFilesystemRecord(value) {
|
|
18338
18657
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
18339
18658
|
const result = {};
|
|
@@ -19248,7 +19567,7 @@ var GoosePermissions = class GoosePermissions extends ToolPermissions {
|
|
|
19248
19567
|
const existingContent = await readFileContentOrNull(filePath) ?? "";
|
|
19249
19568
|
let parsed;
|
|
19250
19569
|
try {
|
|
19251
|
-
parsed = existingContent.trim() === "" ? {} : (
|
|
19570
|
+
parsed = existingContent.trim() === "" ? {} : loadYaml(existingContent);
|
|
19252
19571
|
} catch (error) {
|
|
19253
19572
|
throw new Error(`Failed to parse existing Goose permission.yaml at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
19254
19573
|
}
|
|
@@ -19270,7 +19589,7 @@ var GoosePermissions = class GoosePermissions extends ToolPermissions {
|
|
|
19270
19589
|
let parsed;
|
|
19271
19590
|
try {
|
|
19272
19591
|
const content = this.getFileContent();
|
|
19273
|
-
parsed = content.trim() === "" ? {} : (
|
|
19592
|
+
parsed = content.trim() === "" ? {} : loadYaml(content);
|
|
19274
19593
|
} catch (error) {
|
|
19275
19594
|
throw new Error(`Failed to parse Goose permissions content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
19276
19595
|
}
|
|
@@ -20177,6 +20496,13 @@ const KiroAgentSchema = zod_mini.z.looseObject({
|
|
|
20177
20496
|
toolsSettings: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), zod_mini.z.unknown()))
|
|
20178
20497
|
});
|
|
20179
20498
|
const UnknownRecordSchema = zod_mini.z.record(zod_mini.z.string(), zod_mini.z.unknown());
|
|
20499
|
+
const CANONICAL_SHELL_KEYS = /* @__PURE__ */ new Set(["allowedCommands", "deniedCommands"]);
|
|
20500
|
+
const CANONICAL_TOOL_SETTINGS_KEYS = /* @__PURE__ */ new Set([
|
|
20501
|
+
"read",
|
|
20502
|
+
"write",
|
|
20503
|
+
"grep",
|
|
20504
|
+
"glob"
|
|
20505
|
+
]);
|
|
20180
20506
|
var KiroPermissions = class KiroPermissions extends ToolPermissions {
|
|
20181
20507
|
static getSettablePaths(_options = {}) {
|
|
20182
20508
|
return {
|
|
@@ -20240,7 +20566,10 @@ var KiroPermissions = class KiroPermissions extends ToolPermissions {
|
|
|
20240
20566
|
const allowedTools = new Set(parsed.allowedTools ?? []);
|
|
20241
20567
|
if (allowedTools.has("web_fetch")) permission.webfetch = { "*": "allow" };
|
|
20242
20568
|
if (allowedTools.has("web_search")) permission.websearch = { "*": "allow" };
|
|
20243
|
-
|
|
20569
|
+
const kiroOverride = extractKiroOverride(toolsSettings);
|
|
20570
|
+
const result = { permission };
|
|
20571
|
+
if (kiroOverride !== void 0) result.kiro = kiroOverride;
|
|
20572
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
20244
20573
|
}
|
|
20245
20574
|
validate() {
|
|
20246
20575
|
return {
|
|
@@ -20290,19 +20619,71 @@ function buildKiroPermissionsFromRulesync({ config, logger, existing }) {
|
|
|
20290
20619
|
});
|
|
20291
20620
|
else logger?.warn(`Kiro permissions do not support category: ${category}. Skipping.`);
|
|
20292
20621
|
}
|
|
20293
|
-
nextToolsSettings.shell =
|
|
20622
|
+
nextToolsSettings.shell = {
|
|
20623
|
+
...preservedShellFlags(existing),
|
|
20624
|
+
...shell
|
|
20625
|
+
};
|
|
20294
20626
|
nextToolsSettings.read = pathTable(pathBuckets.read);
|
|
20295
20627
|
nextToolsSettings.write = pathTable(pathBuckets.write);
|
|
20296
20628
|
for (const key of ["grep", "glob"]) {
|
|
20297
20629
|
const bucket = pathBuckets[key];
|
|
20298
20630
|
if (bucket && (bucket.allow.length > 0 || bucket.deny.length > 0)) nextToolsSettings[key] = pathTable(bucket);
|
|
20299
20631
|
}
|
|
20632
|
+
applyKiroOverride({
|
|
20633
|
+
override: config.kiro,
|
|
20634
|
+
nextToolsSettings,
|
|
20635
|
+
logger
|
|
20636
|
+
});
|
|
20300
20637
|
return {
|
|
20301
20638
|
...existing,
|
|
20302
20639
|
allowedTools: [...nextAllowedTools].toSorted(),
|
|
20303
20640
|
toolsSettings: nextToolsSettings
|
|
20304
20641
|
};
|
|
20305
20642
|
}
|
|
20643
|
+
/**
|
|
20644
|
+
* Non-canonical `toolsSettings.shell` keys already present in the existing agent
|
|
20645
|
+
* config (everything except the canonical `allowed`/`deniedCommands`), so a
|
|
20646
|
+
* regenerate does not silently drop a hand-set `autoAllowReadonly` /
|
|
20647
|
+
* `denyByDefault` when no `kiro` override re-authors it.
|
|
20648
|
+
*/
|
|
20649
|
+
function preservedShellFlags(existing) {
|
|
20650
|
+
const existingShell = asRecord$1(asRecord$1(existing.toolsSettings).shell);
|
|
20651
|
+
const flags = {};
|
|
20652
|
+
for (const [key, value] of Object.entries(existingShell)) if (!CANONICAL_SHELL_KEYS.has(key)) flags[key] = value;
|
|
20653
|
+
return flags;
|
|
20654
|
+
}
|
|
20655
|
+
/**
|
|
20656
|
+
* Deep-merge the `kiro` override's `toolsSettings` block into the generated
|
|
20657
|
+
* settings, one `toolsSettings` key at a time so the override's leaf fields win
|
|
20658
|
+
* without clobbering canonical-generated siblings.
|
|
20659
|
+
*
|
|
20660
|
+
* Guards, so the override can only author the non-canonical surfaces it is meant
|
|
20661
|
+
* for and can never weaken a canonical-generated deny:
|
|
20662
|
+
* - prototype-pollution keys are skipped before being used as object keys;
|
|
20663
|
+
* - fully-canonical `toolsSettings` keys (`read`/`write`/`grep`/`glob`) are
|
|
20664
|
+
* rejected outright with a warning (their paths are owned by the canonical
|
|
20665
|
+
* `permission` block);
|
|
20666
|
+
* - for `shell` (partly canonical), the canonical command-list leaves
|
|
20667
|
+
* (`allowed`/`deniedCommands`) are stripped from the override value so only the
|
|
20668
|
+
* auto-trust flags merge.
|
|
20669
|
+
*/
|
|
20670
|
+
function applyKiroOverride({ override, nextToolsSettings, logger }) {
|
|
20671
|
+
const overrideToolsSettings = override?.toolsSettings;
|
|
20672
|
+
if (!isPlainObject$1(overrideToolsSettings)) return;
|
|
20673
|
+
for (const [key, value] of Object.entries(overrideToolsSettings)) {
|
|
20674
|
+
if (isPrototypePollutionKey(key)) continue;
|
|
20675
|
+
if (!isPlainObject$1(value)) continue;
|
|
20676
|
+
if (CANONICAL_TOOL_SETTINGS_KEYS.has(key)) {
|
|
20677
|
+
logger?.warn(`Kiro permissions: ignoring 'kiro.toolsSettings.${key}' override; '${key}' paths are driven by the canonical permission block.`);
|
|
20678
|
+
continue;
|
|
20679
|
+
}
|
|
20680
|
+
const mergeValue = key === "shell" ? Object.fromEntries(Object.entries(value).filter(([leaf]) => !CANONICAL_SHELL_KEYS.has(leaf))) : value;
|
|
20681
|
+
nextToolsSettings[key] = {
|
|
20682
|
+
...asRecord$1(nextToolsSettings[key]),
|
|
20683
|
+
...mergeValue
|
|
20684
|
+
};
|
|
20685
|
+
}
|
|
20686
|
+
}
|
|
20306
20687
|
function pathTable(bucket) {
|
|
20307
20688
|
return {
|
|
20308
20689
|
allowedPaths: bucket?.allow ?? [],
|
|
@@ -20322,6 +20703,31 @@ function asRecord$1(value) {
|
|
|
20322
20703
|
const result = UnknownRecordSchema.safeParse(value);
|
|
20323
20704
|
return result.success ? result.data : {};
|
|
20324
20705
|
}
|
|
20706
|
+
/**
|
|
20707
|
+
* Build the `kiro` permissions override from a parsed agent config's
|
|
20708
|
+
* `toolsSettings`, lifting the Kiro-specific knobs with no canonical category:
|
|
20709
|
+
* - `shell.*` flags other than the canonical `allowed`/`deniedCommands`
|
|
20710
|
+
* (e.g. `autoAllowReadonly`, `denyByDefault`), verbatim.
|
|
20711
|
+
* - the whole `aws` object (`allowedServices` / `deniedServices` / …), verbatim.
|
|
20712
|
+
* - the whole `web_fetch` object (`trusted` / `blocked`), verbatim.
|
|
20713
|
+
*
|
|
20714
|
+
* Returns `undefined` when none are present so the override key is omitted.
|
|
20715
|
+
*/
|
|
20716
|
+
function extractKiroOverride(toolsSettings) {
|
|
20717
|
+
const overrideToolsSettings = {};
|
|
20718
|
+
const shellFlags = {};
|
|
20719
|
+
for (const [key, value] of Object.entries(asRecord$1(toolsSettings.shell))) {
|
|
20720
|
+
if (isPrototypePollutionKey(key)) continue;
|
|
20721
|
+
if (!CANONICAL_SHELL_KEYS.has(key)) shellFlags[key] = value;
|
|
20722
|
+
}
|
|
20723
|
+
if (Object.keys(shellFlags).length > 0) overrideToolsSettings.shell = shellFlags;
|
|
20724
|
+
const aws = asRecord$1(toolsSettings.aws);
|
|
20725
|
+
if (Object.keys(aws).length > 0) overrideToolsSettings.aws = aws;
|
|
20726
|
+
const webFetch = asRecord$1(toolsSettings.web_fetch);
|
|
20727
|
+
if (Object.keys(webFetch).length > 0) overrideToolsSettings.web_fetch = webFetch;
|
|
20728
|
+
if (Object.keys(overrideToolsSettings).length === 0) return void 0;
|
|
20729
|
+
return { toolsSettings: overrideToolsSettings };
|
|
20730
|
+
}
|
|
20325
20731
|
function asStringArray(value) {
|
|
20326
20732
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
20327
20733
|
}
|
|
@@ -21134,7 +21540,7 @@ var RovodevPermissions = class RovodevPermissions extends ToolPermissions {
|
|
|
21134
21540
|
const existingContent = await readFileContentOrNull(filePath) ?? "";
|
|
21135
21541
|
let parsed;
|
|
21136
21542
|
try {
|
|
21137
|
-
parsed = existingContent.trim() === "" ? {} : (
|
|
21543
|
+
parsed = existingContent.trim() === "" ? {} : loadYaml(existingContent);
|
|
21138
21544
|
} catch (error) {
|
|
21139
21545
|
throw new Error(`Failed to parse existing Rovodev config at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
21140
21546
|
}
|
|
@@ -21160,7 +21566,7 @@ var RovodevPermissions = class RovodevPermissions extends ToolPermissions {
|
|
|
21160
21566
|
let parsed;
|
|
21161
21567
|
try {
|
|
21162
21568
|
const content = this.getFileContent();
|
|
21163
|
-
parsed = content.trim() === "" ? {} : (
|
|
21569
|
+
parsed = content.trim() === "" ? {} : loadYaml(content);
|
|
21164
21570
|
} catch (error) {
|
|
21165
21571
|
throw new Error(`Failed to parse Rovodev permissions content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
21166
21572
|
}
|
|
@@ -21355,9 +21761,9 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
|
|
|
21355
21761
|
const rulesyncJson = rulesyncPermissions.getJson();
|
|
21356
21762
|
const provider = resolveActiveProvider(config);
|
|
21357
21763
|
const mode = deriveTaktPermissionMode(rulesyncJson);
|
|
21358
|
-
const override = isPlainObject(rulesyncJson.takt) ? rulesyncJson.takt : void 0;
|
|
21359
|
-
const stepOverrides = isPlainObject(override?.[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? override[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21360
|
-
const overrideProviderOptions = isPlainObject(override?.[TAKT_PROVIDER_OPTIONS_KEY]) ? override[TAKT_PROVIDER_OPTIONS_KEY] : void 0;
|
|
21764
|
+
const override = isPlainObject$1(rulesyncJson.takt) ? rulesyncJson.takt : void 0;
|
|
21765
|
+
const stepOverrides = isPlainObject$1(override?.[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? override[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21766
|
+
const overrideProviderOptions = isPlainObject$1(override?.[TAKT_PROVIDER_OPTIONS_KEY]) ? override[TAKT_PROVIDER_OPTIONS_KEY] : void 0;
|
|
21361
21767
|
const patch = {
|
|
21362
21768
|
[TAKT_PROVIDER_PROFILES_KEY]: { [provider]: {
|
|
21363
21769
|
[TAKT_DEFAULT_PERMISSION_MODE_KEY]: mode,
|
|
@@ -21388,12 +21794,12 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
|
|
|
21388
21794
|
invalidRootPolicy: "error"
|
|
21389
21795
|
});
|
|
21390
21796
|
const provider = resolveActiveProvider(config);
|
|
21391
|
-
const profiles = isPlainObject(config[TAKT_PROVIDER_PROFILES_KEY]) ? config[TAKT_PROVIDER_PROFILES_KEY] : {};
|
|
21392
|
-
const profile = isPlainObject(profiles[provider]) ? profiles[provider] : {};
|
|
21797
|
+
const profiles = isPlainObject$1(config[TAKT_PROVIDER_PROFILES_KEY]) ? config[TAKT_PROVIDER_PROFILES_KEY] : {};
|
|
21798
|
+
const profile = isPlainObject$1(profiles[provider]) ? profiles[provider] : {};
|
|
21393
21799
|
const mode = profile[TAKT_DEFAULT_PERMISSION_MODE_KEY];
|
|
21394
21800
|
const rulesyncConfig = taktModeToRulesyncConfig(mode);
|
|
21395
|
-
const stepOverrides = isPlainObject(profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21396
|
-
const providerOptions = isPlainObject(config[TAKT_PROVIDER_OPTIONS_KEY]) ? config[TAKT_PROVIDER_OPTIONS_KEY] : void 0;
|
|
21801
|
+
const stepOverrides = isPlainObject$1(profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21802
|
+
const providerOptions = isPlainObject$1(config[TAKT_PROVIDER_OPTIONS_KEY]) ? config[TAKT_PROVIDER_OPTIONS_KEY] : void 0;
|
|
21397
21803
|
const taktOverride = {};
|
|
21398
21804
|
if (stepOverrides && Object.keys(stepOverrides).length > 0) taktOverride[TAKT_STEP_PERMISSION_OVERRIDES_KEY] = stepOverrides;
|
|
21399
21805
|
if (providerOptions && Object.keys(providerOptions).length > 0) taktOverride[TAKT_PROVIDER_OPTIONS_KEY] = providerOptions;
|
|
@@ -21425,7 +21831,7 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
|
|
|
21425
21831
|
function resolveActiveProvider(config) {
|
|
21426
21832
|
if (typeof config[TAKT_PROVIDER_KEY] === "string" && config[TAKT_PROVIDER_KEY].trim() !== "") return config[TAKT_PROVIDER_KEY];
|
|
21427
21833
|
const profiles = config[TAKT_PROVIDER_PROFILES_KEY];
|
|
21428
|
-
if (isPlainObject(profiles)) {
|
|
21834
|
+
if (isPlainObject$1(profiles)) {
|
|
21429
21835
|
const keys = Object.keys(profiles);
|
|
21430
21836
|
if (keys.length === 1) return keys[0];
|
|
21431
21837
|
}
|
|
@@ -24414,7 +24820,7 @@ function extractOpenaiYamlFile(otherFiles) {
|
|
|
24414
24820
|
const rest = [];
|
|
24415
24821
|
for (const file of otherFiles) {
|
|
24416
24822
|
if (toPosixPath(file.relativeFilePathToDirPath) === target) try {
|
|
24417
|
-
const loaded = (
|
|
24823
|
+
const loaded = loadYaml(file.fileBuffer.toString("utf-8"));
|
|
24418
24824
|
if (loaded !== null && typeof loaded === "object" && !Array.isArray(loaded)) {
|
|
24419
24825
|
parsed = loaded;
|
|
24420
24826
|
continue;
|
|
@@ -26665,6 +27071,139 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
|
|
|
26665
27071
|
}
|
|
26666
27072
|
};
|
|
26667
27073
|
//#endregion
|
|
27074
|
+
//#region src/features/skills/reasonix-skill.ts
|
|
27075
|
+
const ReasonixSkillFrontmatterSchema = zod_mini.z.looseObject({
|
|
27076
|
+
name: zod_mini.z.string(),
|
|
27077
|
+
description: zod_mini.z.string()
|
|
27078
|
+
});
|
|
27079
|
+
/**
|
|
27080
|
+
* Represents a DeepSeek-Reasonix skill directory.
|
|
27081
|
+
*
|
|
27082
|
+
* Reasonix discovers directory-layout skills (`<name>/SKILL.md`) under
|
|
27083
|
+
* `.reasonix/skills/` (project) and `~/.reasonix/skills/` (global); the global
|
|
27084
|
+
* scope is served by the processor supplying the home directory as outputRoot.
|
|
27085
|
+
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/GUIDE.md
|
|
27086
|
+
*/
|
|
27087
|
+
var ReasonixSkill = class ReasonixSkill extends ToolSkill {
|
|
27088
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = REASONIX_SKILLS_DIR_PATH, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
27089
|
+
super({
|
|
27090
|
+
outputRoot,
|
|
27091
|
+
relativeDirPath,
|
|
27092
|
+
dirName,
|
|
27093
|
+
mainFile: {
|
|
27094
|
+
name: SKILL_FILE_NAME,
|
|
27095
|
+
body,
|
|
27096
|
+
frontmatter: { ...frontmatter }
|
|
27097
|
+
},
|
|
27098
|
+
otherFiles,
|
|
27099
|
+
global
|
|
27100
|
+
});
|
|
27101
|
+
if (validate) {
|
|
27102
|
+
const result = this.validate();
|
|
27103
|
+
if (!result.success) throw result.error;
|
|
27104
|
+
}
|
|
27105
|
+
}
|
|
27106
|
+
static getSettablePaths({ global: _global = false } = {}) {
|
|
27107
|
+
return { relativeDirPath: REASONIX_SKILLS_DIR_PATH };
|
|
27108
|
+
}
|
|
27109
|
+
getFrontmatter() {
|
|
27110
|
+
return ReasonixSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
27111
|
+
}
|
|
27112
|
+
getBody() {
|
|
27113
|
+
return this.mainFile?.body ?? "";
|
|
27114
|
+
}
|
|
27115
|
+
validate() {
|
|
27116
|
+
if (this.mainFile === void 0) return {
|
|
27117
|
+
success: false,
|
|
27118
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
27119
|
+
};
|
|
27120
|
+
const result = ReasonixSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
27121
|
+
if (!result.success) return {
|
|
27122
|
+
success: false,
|
|
27123
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
27124
|
+
};
|
|
27125
|
+
return {
|
|
27126
|
+
success: true,
|
|
27127
|
+
error: null
|
|
27128
|
+
};
|
|
27129
|
+
}
|
|
27130
|
+
toRulesyncSkill() {
|
|
27131
|
+
const frontmatter = this.getFrontmatter();
|
|
27132
|
+
const rulesyncFrontmatter = {
|
|
27133
|
+
name: frontmatter.name,
|
|
27134
|
+
description: frontmatter.description,
|
|
27135
|
+
targets: ["*"]
|
|
27136
|
+
};
|
|
27137
|
+
return new RulesyncSkill({
|
|
27138
|
+
outputRoot: this.outputRoot,
|
|
27139
|
+
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
27140
|
+
dirName: this.getDirName(),
|
|
27141
|
+
frontmatter: rulesyncFrontmatter,
|
|
27142
|
+
body: this.getBody(),
|
|
27143
|
+
otherFiles: this.getOtherFiles(),
|
|
27144
|
+
validate: true,
|
|
27145
|
+
global: this.global
|
|
27146
|
+
});
|
|
27147
|
+
}
|
|
27148
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
27149
|
+
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
27150
|
+
const reasonixFrontmatter = {
|
|
27151
|
+
name: rulesyncFrontmatter.name,
|
|
27152
|
+
description: rulesyncFrontmatter.description
|
|
27153
|
+
};
|
|
27154
|
+
return new ReasonixSkill({
|
|
27155
|
+
outputRoot,
|
|
27156
|
+
relativeDirPath: ReasonixSkill.getSettablePaths({ global }).relativeDirPath,
|
|
27157
|
+
dirName: rulesyncSkill.getDirName(),
|
|
27158
|
+
frontmatter: reasonixFrontmatter,
|
|
27159
|
+
body: rulesyncSkill.getBody(),
|
|
27160
|
+
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
27161
|
+
validate,
|
|
27162
|
+
global
|
|
27163
|
+
});
|
|
27164
|
+
}
|
|
27165
|
+
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
27166
|
+
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
27167
|
+
return targets.includes("*") || targets.includes("reasonix");
|
|
27168
|
+
}
|
|
27169
|
+
static async fromDir(params) {
|
|
27170
|
+
const loaded = await this.loadSkillDirContent({
|
|
27171
|
+
...params,
|
|
27172
|
+
getSettablePaths: ReasonixSkill.getSettablePaths
|
|
27173
|
+
});
|
|
27174
|
+
const result = ReasonixSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
27175
|
+
if (!result.success) {
|
|
27176
|
+
const skillDirPath = (0, node_path.join)(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
27177
|
+
throw new Error(`Invalid frontmatter in ${(0, node_path.join)(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
27178
|
+
}
|
|
27179
|
+
return new ReasonixSkill({
|
|
27180
|
+
outputRoot: loaded.outputRoot,
|
|
27181
|
+
relativeDirPath: loaded.relativeDirPath,
|
|
27182
|
+
dirName: loaded.dirName,
|
|
27183
|
+
frontmatter: result.data,
|
|
27184
|
+
body: loaded.body,
|
|
27185
|
+
otherFiles: loaded.otherFiles,
|
|
27186
|
+
validate: true,
|
|
27187
|
+
global: loaded.global
|
|
27188
|
+
});
|
|
27189
|
+
}
|
|
27190
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
27191
|
+
return new ReasonixSkill({
|
|
27192
|
+
outputRoot,
|
|
27193
|
+
relativeDirPath,
|
|
27194
|
+
dirName,
|
|
27195
|
+
frontmatter: {
|
|
27196
|
+
name: "",
|
|
27197
|
+
description: ""
|
|
27198
|
+
},
|
|
27199
|
+
body: "",
|
|
27200
|
+
otherFiles: [],
|
|
27201
|
+
validate: false,
|
|
27202
|
+
global
|
|
27203
|
+
});
|
|
27204
|
+
}
|
|
27205
|
+
};
|
|
27206
|
+
//#endregion
|
|
26668
27207
|
//#region src/constants/replit-paths.ts
|
|
26669
27208
|
const REPLIT_RULE_FILE_NAME = "replit.md";
|
|
26670
27209
|
const REPLIT_SKILLS_DIR_PATH = (0, node_path.join)(".agents", "skills");
|
|
@@ -27759,6 +28298,14 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
27759
28298
|
supportsGlobal: true
|
|
27760
28299
|
}
|
|
27761
28300
|
}],
|
|
28301
|
+
["reasonix", {
|
|
28302
|
+
class: ReasonixSkill,
|
|
28303
|
+
meta: {
|
|
28304
|
+
supportsProject: true,
|
|
28305
|
+
supportsSimulated: false,
|
|
28306
|
+
supportsGlobal: true
|
|
28307
|
+
}
|
|
28308
|
+
}],
|
|
27762
28309
|
["replit", {
|
|
27763
28310
|
class: ReplitSkill,
|
|
27764
28311
|
meta: {
|
|
@@ -30042,7 +30589,7 @@ var GooseSubagent = class GooseSubagent extends ToolSubagent {
|
|
|
30042
30589
|
const fileContent = await readFileContent(filePath);
|
|
30043
30590
|
let parsed;
|
|
30044
30591
|
try {
|
|
30045
|
-
parsed = (
|
|
30592
|
+
parsed = loadYaml(fileContent);
|
|
30046
30593
|
} catch (error) {
|
|
30047
30594
|
throw new Error(`Failed to parse Goose recipe (${filePath}): ${formatError(error)}`, { cause: error });
|
|
30048
30595
|
}
|
|
@@ -31430,7 +31977,7 @@ var RooSubagent = class RooSubagent extends ToolSubagent {
|
|
|
31430
31977
|
const fileContent = await readFileContent(filePath);
|
|
31431
31978
|
let parsed;
|
|
31432
31979
|
try {
|
|
31433
|
-
parsed = (
|
|
31980
|
+
parsed = loadYaml(fileContent);
|
|
31434
31981
|
} catch (error) {
|
|
31435
31982
|
throw new Error(`Failed to parse .roomodes (${filePath}): ${formatError(error)}`, { cause: error });
|
|
31436
31983
|
}
|
|
@@ -35623,6 +36170,72 @@ var QwencodeRule = class QwencodeRule extends ToolRule {
|
|
|
35623
36170
|
}
|
|
35624
36171
|
};
|
|
35625
36172
|
//#endregion
|
|
36173
|
+
//#region src/features/rules/reasonix-rule.ts
|
|
36174
|
+
var ReasonixRule = class ReasonixRule extends ToolRule {
|
|
36175
|
+
constructor({ fileContent, root, ...rest }) {
|
|
36176
|
+
super({
|
|
36177
|
+
...rest,
|
|
36178
|
+
fileContent,
|
|
36179
|
+
root: root ?? false
|
|
36180
|
+
});
|
|
36181
|
+
}
|
|
36182
|
+
static getSettablePaths({ global = false } = {}) {
|
|
36183
|
+
return { root: {
|
|
36184
|
+
relativeDirPath: global ? REASONIX_GLOBAL_DIR : ".",
|
|
36185
|
+
relativeFilePath: REASONIX_RULE_FILE_NAME
|
|
36186
|
+
} };
|
|
36187
|
+
}
|
|
36188
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, validate = true, global = false }) {
|
|
36189
|
+
const { root } = this.getSettablePaths({ global });
|
|
36190
|
+
const fileContent = await readFileContent((0, node_path.join)(outputRoot, (0, node_path.join)(root.relativeDirPath, root.relativeFilePath)));
|
|
36191
|
+
return new ReasonixRule({
|
|
36192
|
+
outputRoot,
|
|
36193
|
+
relativeDirPath: root.relativeDirPath,
|
|
36194
|
+
relativeFilePath: root.relativeFilePath,
|
|
36195
|
+
fileContent,
|
|
36196
|
+
validate,
|
|
36197
|
+
root: true
|
|
36198
|
+
});
|
|
36199
|
+
}
|
|
36200
|
+
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
36201
|
+
const { root } = this.getSettablePaths({ global });
|
|
36202
|
+
const isRoot = rulesyncRule.getFrontmatter().root ?? false;
|
|
36203
|
+
return new ReasonixRule({
|
|
36204
|
+
outputRoot,
|
|
36205
|
+
relativeDirPath: root.relativeDirPath,
|
|
36206
|
+
relativeFilePath: root.relativeFilePath,
|
|
36207
|
+
fileContent: rulesyncRule.getBody(),
|
|
36208
|
+
validate,
|
|
36209
|
+
root: isRoot
|
|
36210
|
+
});
|
|
36211
|
+
}
|
|
36212
|
+
toRulesyncRule() {
|
|
36213
|
+
return this.toRulesyncRuleDefault();
|
|
36214
|
+
}
|
|
36215
|
+
validate() {
|
|
36216
|
+
return {
|
|
36217
|
+
success: true,
|
|
36218
|
+
error: null
|
|
36219
|
+
};
|
|
36220
|
+
}
|
|
36221
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
36222
|
+
return new ReasonixRule({
|
|
36223
|
+
outputRoot,
|
|
36224
|
+
relativeDirPath,
|
|
36225
|
+
relativeFilePath,
|
|
36226
|
+
fileContent: "",
|
|
36227
|
+
validate: false,
|
|
36228
|
+
root: relativeFilePath === "REASONIX.md" && (relativeDirPath === "." || relativeDirPath === ".reasonix")
|
|
36229
|
+
});
|
|
36230
|
+
}
|
|
36231
|
+
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
36232
|
+
return this.isTargetedByRulesyncRuleDefault({
|
|
36233
|
+
rulesyncRule,
|
|
36234
|
+
toolTarget: "reasonix"
|
|
36235
|
+
});
|
|
36236
|
+
}
|
|
36237
|
+
};
|
|
36238
|
+
//#endregion
|
|
35626
36239
|
//#region src/features/rules/replit-rule.ts
|
|
35627
36240
|
/**
|
|
35628
36241
|
* Rule generator for Replit Agent
|
|
@@ -36535,6 +37148,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
36535
37148
|
additionalConventions: { subagents: { subagentClass: QwencodeSubagent } }
|
|
36536
37149
|
}
|
|
36537
37150
|
}],
|
|
37151
|
+
["reasonix", {
|
|
37152
|
+
class: ReasonixRule,
|
|
37153
|
+
meta: {
|
|
37154
|
+
extension: "md",
|
|
37155
|
+
supportsGlobal: true,
|
|
37156
|
+
ruleDiscoveryMode: "auto",
|
|
37157
|
+
foldsNonRootIntoRoot: true
|
|
37158
|
+
}
|
|
37159
|
+
}],
|
|
36538
37160
|
["replit", {
|
|
36539
37161
|
class: ReplitRule,
|
|
36540
37162
|
meta: {
|
|
@@ -37420,7 +38042,6 @@ const SHARED_WRITE_FEATURE_ORDER = [
|
|
|
37420
38042
|
"permissions",
|
|
37421
38043
|
"rules"
|
|
37422
38044
|
];
|
|
37423
|
-
const SHARED_WRITE_FEATURES = new Set(SHARED_WRITE_FEATURE_ORDER);
|
|
37424
38045
|
const TARGETS_NOT_DERIVED = /* @__PURE__ */ new Set(["augmentcode-legacy", "claudecode-legacy"]);
|
|
37425
38046
|
const sharedFileKey = (path) => {
|
|
37426
38047
|
const dir = path.relativeDirPath.replace(/\\/g, "/").replace(/\/$/, "");
|
|
@@ -37443,7 +38064,13 @@ const settablePathsForScope = (cls, global) => {
|
|
|
37443
38064
|
paths.push(settable.root);
|
|
37444
38065
|
for (const alt of settable.alternativeRoots ?? []) paths.push(alt);
|
|
37445
38066
|
}
|
|
37446
|
-
|
|
38067
|
+
let extra;
|
|
38068
|
+
try {
|
|
38069
|
+
extra = cls.getExtraSharedWritePaths?.({ global }) ?? [];
|
|
38070
|
+
} catch {
|
|
38071
|
+
return paths;
|
|
38072
|
+
}
|
|
38073
|
+
for (const path of extra) if (path.relativeFilePath) paths.push(path);
|
|
37447
38074
|
return paths;
|
|
37448
38075
|
};
|
|
37449
38076
|
const collectFactoryPaths = (factory) => [...settablePathsForScope(factory.class, false), ...settablePathsForScope(factory.class, true)];
|
|
@@ -37456,7 +38083,6 @@ const deriveSharedFileWriters = () => {
|
|
|
37456
38083
|
const byKey = /* @__PURE__ */ new Map();
|
|
37457
38084
|
const pathByKey = /* @__PURE__ */ new Map();
|
|
37458
38085
|
for (const entry of PROCESSOR_REGISTRY) {
|
|
37459
|
-
if (!SHARED_WRITE_FEATURES.has(entry.feature)) continue;
|
|
37460
38086
|
const factories = entry.factory;
|
|
37461
38087
|
for (const [tool, factory] of factories) {
|
|
37462
38088
|
if (TARGETS_NOT_DERIVED.has(tool)) continue;
|
|
@@ -37845,7 +38471,7 @@ async function generateRulesCore(params) {
|
|
|
37845
38471
|
targets: config.getConfigFileTargets(),
|
|
37846
38472
|
global: config.getGlobal()
|
|
37847
38473
|
}) : /* @__PURE__ */ new Map();
|
|
37848
|
-
for (const
|
|
38474
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
37849
38475
|
if (!config.getFeatures(toolTarget).includes("rules")) continue;
|
|
37850
38476
|
const processor = new RulesProcessor({
|
|
37851
38477
|
outputRoot,
|
|
@@ -37900,7 +38526,7 @@ async function generateIgnoreCore(params) {
|
|
|
37900
38526
|
let hasDiff = false;
|
|
37901
38527
|
for (const toolTarget of (0, es_toolkit.intersection)(config.getTargets(), supportedIgnoreTargets)) {
|
|
37902
38528
|
if (!config.getFeatures(toolTarget).includes("ignore")) continue;
|
|
37903
|
-
for (const outputRoot of config.getOutputRoots()) try {
|
|
38529
|
+
for (const outputRoot of config.getOutputRoots(toolTarget)) try {
|
|
37904
38530
|
const processor = new IgnoreProcessor({
|
|
37905
38531
|
outputRoot,
|
|
37906
38532
|
inputRoot: config.getInputRoot(),
|
|
@@ -37941,7 +38567,7 @@ async function generateMcpCore(params) {
|
|
|
37941
38567
|
featureName: "mcp",
|
|
37942
38568
|
logger
|
|
37943
38569
|
});
|
|
37944
|
-
for (const
|
|
38570
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
37945
38571
|
if (!config.getFeatures(toolTarget).includes("mcp")) continue;
|
|
37946
38572
|
const processor = new McpProcessor({
|
|
37947
38573
|
outputRoot,
|
|
@@ -37983,7 +38609,7 @@ async function generateCommandsCore(params) {
|
|
|
37983
38609
|
featureName: "commands",
|
|
37984
38610
|
logger
|
|
37985
38611
|
});
|
|
37986
|
-
for (const
|
|
38612
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
37987
38613
|
if (!config.getFeatures(toolTarget).includes("commands")) continue;
|
|
37988
38614
|
const processor = new CommandsProcessor({
|
|
37989
38615
|
outputRoot,
|
|
@@ -38025,7 +38651,7 @@ async function generateSubagentsCore(params) {
|
|
|
38025
38651
|
featureName: "subagents",
|
|
38026
38652
|
logger
|
|
38027
38653
|
});
|
|
38028
|
-
for (const
|
|
38654
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
38029
38655
|
if (!config.getFeatures(toolTarget).includes("subagents")) continue;
|
|
38030
38656
|
const processor = new SubagentsProcessor({
|
|
38031
38657
|
outputRoot,
|
|
@@ -38068,7 +38694,7 @@ async function generateSkillsCore(params) {
|
|
|
38068
38694
|
featureName: "skills",
|
|
38069
38695
|
logger
|
|
38070
38696
|
});
|
|
38071
|
-
for (const
|
|
38697
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
38072
38698
|
if (!config.getFeatures(toolTarget).includes("skills")) continue;
|
|
38073
38699
|
const processor = new SkillsProcessor({
|
|
38074
38700
|
outputRoot,
|
|
@@ -38109,7 +38735,7 @@ async function generateHooksCore(params) {
|
|
|
38109
38735
|
featureName: "hooks",
|
|
38110
38736
|
logger
|
|
38111
38737
|
});
|
|
38112
|
-
for (const
|
|
38738
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
38113
38739
|
if (!config.getFeatures(toolTarget).includes("hooks")) continue;
|
|
38114
38740
|
const processor = new HooksProcessor({
|
|
38115
38741
|
outputRoot,
|
|
@@ -38146,7 +38772,7 @@ async function generatePermissionsCore(params) {
|
|
|
38146
38772
|
let totalCount = 0;
|
|
38147
38773
|
const allPaths = [];
|
|
38148
38774
|
let hasDiff = false;
|
|
38149
|
-
for (const
|
|
38775
|
+
for (const toolTarget of (0, es_toolkit.intersection)(config.getTargets(), supportedPermissionsTargets)) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
38150
38776
|
if (!config.getFeatures(toolTarget).includes("permissions")) continue;
|
|
38151
38777
|
try {
|
|
38152
38778
|
const processor = new PermissionsProcessor({
|
|
@@ -38878,6 +39504,12 @@ Object.defineProperty(exports, "listDirectoryFiles", {
|
|
|
38878
39504
|
return listDirectoryFiles;
|
|
38879
39505
|
}
|
|
38880
39506
|
});
|
|
39507
|
+
Object.defineProperty(exports, "loadYaml", {
|
|
39508
|
+
enumerable: true,
|
|
39509
|
+
get: function() {
|
|
39510
|
+
return loadYaml;
|
|
39511
|
+
}
|
|
39512
|
+
});
|
|
38881
39513
|
Object.defineProperty(exports, "readFileContent", {
|
|
38882
39514
|
enumerable: true,
|
|
38883
39515
|
get: function() {
|