rulesync 9.5.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-BdJG1fyb.js → import-B6nwZmGl.js} +686 -88
- package/dist/import-B6nwZmGl.js.map +1 -0
- package/dist/{import-DngV1i49.cjs → import-CiS5ckUo.cjs} +700 -96
- 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-BdJG1fyb.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",
|
|
@@ -1450,7 +1452,7 @@ function isRecord(value) {
|
|
|
1450
1452
|
* conversion, etc.) should reject inputs whose prototype could carry
|
|
1451
1453
|
* malicious accessor descriptors.
|
|
1452
1454
|
*/
|
|
1453
|
-
function isPlainObject(value) {
|
|
1455
|
+
function isPlainObject$1(value) {
|
|
1454
1456
|
if (!isRecord(value)) return false;
|
|
1455
1457
|
const proto = Object.getPrototypeOf(value);
|
|
1456
1458
|
return proto === null || proto === Object.prototype;
|
|
@@ -1462,11 +1464,43 @@ function isStringArray(value) {
|
|
|
1462
1464
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1463
1465
|
}
|
|
1464
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
|
|
1465
1499
|
//#region src/utils/frontmatter.ts
|
|
1466
1500
|
function deepRemoveNullishValue(value) {
|
|
1467
1501
|
if (value === null || value === void 0) return;
|
|
1468
1502
|
if (Array.isArray(value)) return value.map((item) => deepRemoveNullishValue(item)).filter((item) => item !== void 0);
|
|
1469
|
-
if (isPlainObject(value)) {
|
|
1503
|
+
if (isPlainObject$1(value)) {
|
|
1470
1504
|
const result = {};
|
|
1471
1505
|
for (const [key, val] of Object.entries(value)) {
|
|
1472
1506
|
const cleaned = deepRemoveNullishValue(val);
|
|
@@ -1489,7 +1523,7 @@ function deepFlattenStringsValue(value) {
|
|
|
1489
1523
|
if (value === null || value === void 0) return;
|
|
1490
1524
|
if (typeof value === "string") return value.replace(/\n+/g, " ").trim();
|
|
1491
1525
|
if (Array.isArray(value)) return value.map((item) => deepFlattenStringsValue(item)).filter((item) => item !== void 0);
|
|
1492
|
-
if (isPlainObject(value)) {
|
|
1526
|
+
if (isPlainObject$1(value)) {
|
|
1493
1527
|
const result = {};
|
|
1494
1528
|
for (const [key, val] of Object.entries(value)) {
|
|
1495
1529
|
const cleaned = deepFlattenStringsValue(val);
|
|
@@ -1512,7 +1546,7 @@ function stringifyFrontmatter(body, frontmatter, options) {
|
|
|
1512
1546
|
const { avoidBlockScalars = false } = options ?? {};
|
|
1513
1547
|
const cleanFrontmatter = avoidBlockScalars ? deepFlattenStringsObject(frontmatter) : deepRemoveNullishObject(frontmatter);
|
|
1514
1548
|
if (avoidBlockScalars) return gray_matter.default.stringify(body, cleanFrontmatter, { engines: { yaml: {
|
|
1515
|
-
parse: (input) => (
|
|
1549
|
+
parse: (input) => loadYaml(input) ?? {},
|
|
1516
1550
|
stringify: (data) => (0, js_yaml.dump)(data, { lineWidth: -1 })
|
|
1517
1551
|
} } });
|
|
1518
1552
|
return gray_matter.default.stringify(body, cleanFrontmatter);
|
|
@@ -1551,7 +1585,7 @@ function tryJsonEquivalent(a, b) {
|
|
|
1551
1585
|
}
|
|
1552
1586
|
function tryYamlEquivalent(a, b) {
|
|
1553
1587
|
try {
|
|
1554
|
-
return (0, node_util.isDeepStrictEqual)((
|
|
1588
|
+
return (0, node_util.isDeepStrictEqual)(loadYaml(a), loadYaml(b));
|
|
1555
1589
|
} catch {
|
|
1556
1590
|
return;
|
|
1557
1591
|
}
|
|
@@ -3358,7 +3392,7 @@ var GooseCommand = class GooseCommand extends ToolCommand {
|
|
|
3358
3392
|
const where = (0, node_path.join)(this.relativeDirPath, this.relativeFilePath);
|
|
3359
3393
|
let parsed;
|
|
3360
3394
|
try {
|
|
3361
|
-
parsed = (
|
|
3395
|
+
parsed = loadYaml(content);
|
|
3362
3396
|
} catch (error) {
|
|
3363
3397
|
throw new Error(`Failed to parse Goose recipe (${where}): ${formatError(error)}`, { cause: error });
|
|
3364
3398
|
}
|
|
@@ -4012,7 +4046,7 @@ function omitPrototypePollutionKeys(record) {
|
|
|
4012
4046
|
//#region src/features/shared/shared-config-gateway.ts
|
|
4013
4047
|
function sanitizeSharedConfigValue(value) {
|
|
4014
4048
|
if (Array.isArray(value)) return value.map(sanitizeSharedConfigValue);
|
|
4015
|
-
if (!isPlainObject(value)) return value;
|
|
4049
|
+
if (!isPlainObject$1(value)) return value;
|
|
4016
4050
|
const result = {};
|
|
4017
4051
|
for (const [key, nested] of Object.entries(omitPrototypePollutionKeys(value))) result[key] = sanitizeSharedConfigValue(nested);
|
|
4018
4052
|
return result;
|
|
@@ -4028,14 +4062,14 @@ function parseSharedConfig({ format, fileContent, filePath, invalidRootPolicy =
|
|
|
4028
4062
|
const at = filePath === void 0 ? "" : ` at ${filePath}`;
|
|
4029
4063
|
let parsed;
|
|
4030
4064
|
try {
|
|
4031
|
-
if (format === "yaml") parsed = (
|
|
4065
|
+
if (format === "yaml") parsed = loadYaml(fileContent);
|
|
4032
4066
|
else if (format === "json") parsed = JSON.parse(fileContent);
|
|
4033
4067
|
else parsed = (0, jsonc_parser.parse)(fileContent);
|
|
4034
4068
|
} catch (error) {
|
|
4035
4069
|
throw new Error(`Failed to parse shared config${at}: ${formatError(error)}`, { cause: error });
|
|
4036
4070
|
}
|
|
4037
4071
|
if (parsed === void 0 || parsed === null) return {};
|
|
4038
|
-
if (!isPlainObject(parsed)) {
|
|
4072
|
+
if (!isPlainObject$1(parsed)) {
|
|
4039
4073
|
if (invalidRootPolicy === "error") throw new Error(`Failed to parse shared config${at}: expected a mapping at the root`);
|
|
4040
4074
|
return {};
|
|
4041
4075
|
}
|
|
@@ -4078,7 +4112,7 @@ function mergeSharedConfigDeep({ base, patch }) {
|
|
|
4078
4112
|
for (const [key, patchValue] of Object.entries(patch)) {
|
|
4079
4113
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
4080
4114
|
const baseValue = result[key];
|
|
4081
|
-
if (isPlainObject(baseValue) && isPlainObject(patchValue)) result[key] = mergeSharedConfigDeep({
|
|
4115
|
+
if (isPlainObject$1(baseValue) && isPlainObject$1(patchValue)) result[key] = mergeSharedConfigDeep({
|
|
4082
4116
|
base: baseValue,
|
|
4083
4117
|
patch: patchValue
|
|
4084
4118
|
});
|
|
@@ -4712,6 +4746,8 @@ const REASONIX_GLOBAL_PERMISSIONS_FILE_NAME = REASONIX_GLOBAL_MCP_FILE_NAME;
|
|
|
4712
4746
|
const REASONIX_DIR = REASONIX_GLOBAL_DIR;
|
|
4713
4747
|
const REASONIX_SETTINGS_FILE_NAME = "settings.json";
|
|
4714
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");
|
|
4715
4751
|
//#endregion
|
|
4716
4752
|
//#region src/features/commands/reasonix-command.ts
|
|
4717
4753
|
/**
|
|
@@ -5117,8 +5153,8 @@ var RovodevCommand = class RovodevCommand extends ToolCommand {
|
|
|
5117
5153
|
const existingContent = await readFileContentOrNull((0, node_path.join)(outputRoot, ROVODEV_DIR, ROVODEV_PROMPTS_FILE_NAME));
|
|
5118
5154
|
let existing = {};
|
|
5119
5155
|
if (existingContent) try {
|
|
5120
|
-
const parsed = (
|
|
5121
|
-
if (isPlainObject(parsed)) existing = parsed;
|
|
5156
|
+
const parsed = loadYaml(existingContent);
|
|
5157
|
+
if (isPlainObject$1(parsed)) existing = parsed;
|
|
5122
5158
|
} catch {}
|
|
5123
5159
|
const prompts = rovodevCommands.map((command) => ({
|
|
5124
5160
|
name: command.getName(),
|
|
@@ -5148,11 +5184,11 @@ async function lookupPromptDescription({ outputRoot, relativeFilePath, name }) {
|
|
|
5148
5184
|
if (!manifestContent) return "";
|
|
5149
5185
|
let parsed;
|
|
5150
5186
|
try {
|
|
5151
|
-
parsed = (
|
|
5187
|
+
parsed = loadYaml(manifestContent);
|
|
5152
5188
|
} catch {
|
|
5153
5189
|
return "";
|
|
5154
5190
|
}
|
|
5155
|
-
if (!isPlainObject(parsed) || !Array.isArray(parsed.prompts)) return "";
|
|
5191
|
+
if (!isPlainObject$1(parsed) || !Array.isArray(parsed.prompts)) return "";
|
|
5156
5192
|
const expectedContentFile = toPosixPath((0, node_path.join)("prompts", relativeFilePath));
|
|
5157
5193
|
const entry = parsed.prompts.find((candidate) => isRecord(candidate) && (candidate.content_file === expectedContentFile || candidate.name === name));
|
|
5158
5194
|
return isRecord(entry) && typeof entry.description === "string" ? entry.description : "";
|
|
@@ -6190,18 +6226,25 @@ const QWENCODE_HOOK_EVENTS = [
|
|
|
6190
6226
|
* Reasonix's `.reasonix/settings.json` (project) / `~/.reasonix/settings.json`
|
|
6191
6227
|
* (global) documents a ten-event surface (`PreToolUse`, `PostToolUse`,
|
|
6192
6228
|
* `UserPromptSubmit`, `Stop`, `PostLLMCall`, `SessionStart`, `SessionEnd`,
|
|
6193
|
-
* `SubagentStop`, `Notification`, `PreCompact`)
|
|
6194
|
-
*
|
|
6195
|
-
* `UserPromptSubmit` ← `beforeSubmitPrompt`,
|
|
6196
|
-
*
|
|
6197
|
-
*
|
|
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.
|
|
6198
6237
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
6199
6238
|
*/
|
|
6200
6239
|
const REASONIX_HOOK_EVENTS = [
|
|
6201
6240
|
"preToolUse",
|
|
6202
6241
|
"postToolUse",
|
|
6203
6242
|
"beforeSubmitPrompt",
|
|
6204
|
-
"stop"
|
|
6243
|
+
"stop",
|
|
6244
|
+
"sessionStart",
|
|
6245
|
+
"sessionEnd",
|
|
6246
|
+
"subagentStop",
|
|
6247
|
+
"postModelInvocation"
|
|
6205
6248
|
];
|
|
6206
6249
|
/**
|
|
6207
6250
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
@@ -6637,14 +6680,18 @@ const QWENCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
|
|
|
6637
6680
|
/**
|
|
6638
6681
|
* Map canonical camelCase event names to Reasonix PascalCase.
|
|
6639
6682
|
* Reasonix explicitly mirrors Claude Code's hooks model, so it reuses the same
|
|
6640
|
-
* PascalCase names for the
|
|
6683
|
+
* PascalCase names for the events rulesync maps.
|
|
6641
6684
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
6642
6685
|
*/
|
|
6643
6686
|
const CANONICAL_TO_REASONIX_EVENT_NAMES = {
|
|
6644
6687
|
preToolUse: "PreToolUse",
|
|
6645
6688
|
postToolUse: "PostToolUse",
|
|
6646
6689
|
beforeSubmitPrompt: "UserPromptSubmit",
|
|
6647
|
-
stop: "Stop"
|
|
6690
|
+
stop: "Stop",
|
|
6691
|
+
sessionStart: "SessionStart",
|
|
6692
|
+
sessionEnd: "SessionEnd",
|
|
6693
|
+
subagentStop: "SubagentStop",
|
|
6694
|
+
postModelInvocation: "PostLLMCall"
|
|
6648
6695
|
};
|
|
6649
6696
|
/**
|
|
6650
6697
|
* Map Reasonix PascalCase event names to canonical camelCase.
|
|
@@ -6693,6 +6740,21 @@ function applyCommandPrefix({ def, converterConfig }) {
|
|
|
6693
6740
|
return converterConfig.projectDirVar !== "" && typeof trimmedCommand === "string" && !trimmedCommand.startsWith("$") && (!converterConfig.prefixDotRelativeCommandsOnly || trimmedCommand.startsWith(".")) && typeof trimmedCommand === "string" ? `"${converterConfig.projectDirVar}"/${trimmedCommand.replace(/^\.\//, "")}` : def.command;
|
|
6694
6741
|
}
|
|
6695
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
|
+
/**
|
|
6696
6758
|
* Convert the definitions of a single matcher group into tool hook entries,
|
|
6697
6759
|
* honoring supported hook types and passthrough fields.
|
|
6698
6760
|
*/
|
|
@@ -6706,6 +6768,10 @@ function buildToolHooks({ defs, converterConfig }) {
|
|
|
6706
6768
|
converterConfig
|
|
6707
6769
|
});
|
|
6708
6770
|
hooks.push({
|
|
6771
|
+
...emitBooleanPassthroughFields({
|
|
6772
|
+
def,
|
|
6773
|
+
converterConfig
|
|
6774
|
+
}),
|
|
6709
6775
|
type: hookType,
|
|
6710
6776
|
...command !== void 0 && command !== null && { command },
|
|
6711
6777
|
...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
|
|
@@ -6793,6 +6859,10 @@ function toolHookToCanonical({ h, rawEntry, converterConfig }) {
|
|
|
6793
6859
|
...prompt !== void 0 && prompt !== null && { prompt },
|
|
6794
6860
|
...converterConfig.passthroughFields?.includes("name") && typeof h.name === "string" && { name: h.name },
|
|
6795
6861
|
...converterConfig.passthroughFields?.includes("description") && typeof h.description === "string" && { description: h.description },
|
|
6862
|
+
...importBooleanPassthroughFields({
|
|
6863
|
+
h,
|
|
6864
|
+
converterConfig
|
|
6865
|
+
}),
|
|
6796
6866
|
...rawEntry.matcher !== void 0 && rawEntry.matcher !== null && rawEntry.matcher !== "" && { matcher: rawEntry.matcher }
|
|
6797
6867
|
};
|
|
6798
6868
|
}
|
|
@@ -7087,7 +7157,7 @@ function combineAugmentSettings(base, local) {
|
|
|
7087
7157
|
const baseValue = result[key];
|
|
7088
7158
|
if (AUGMENTCODE_REPLACE_KEYS.has(key)) result[key] = localValue;
|
|
7089
7159
|
else if (Array.isArray(localValue) && Array.isArray(baseValue)) result[key] = [...localValue, ...baseValue];
|
|
7090
|
-
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);
|
|
7091
7161
|
else result[key] = localValue;
|
|
7092
7162
|
}
|
|
7093
7163
|
return result;
|
|
@@ -7131,14 +7201,14 @@ async function readAugmentcodeSettingsWithLocalOverlay({ outputRoot, relativeDir
|
|
|
7131
7201
|
} catch (error) {
|
|
7132
7202
|
throw new Error(`Failed to parse AugmentCode settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
7133
7203
|
}
|
|
7134
|
-
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`);
|
|
7135
7205
|
let baseParsed;
|
|
7136
7206
|
try {
|
|
7137
7207
|
baseParsed = JSON.parse(baseContent);
|
|
7138
7208
|
} catch {
|
|
7139
7209
|
return baseContent;
|
|
7140
7210
|
}
|
|
7141
|
-
const merged = combineAugmentSettings(isPlainObject(baseParsed) ? baseParsed : {}, localParsed);
|
|
7211
|
+
const merged = combineAugmentSettings(isPlainObject$1(baseParsed) ? baseParsed : {}, localParsed);
|
|
7142
7212
|
return JSON.stringify(merged, null, 2);
|
|
7143
7213
|
}
|
|
7144
7214
|
//#endregion
|
|
@@ -8723,7 +8793,14 @@ const JUNIE_CONVERTER_CONFIG = {
|
|
|
8723
8793
|
toolToCanonicalEventNames: JUNIE_TO_CANONICAL_EVENT_NAMES,
|
|
8724
8794
|
projectDirVar: "",
|
|
8725
8795
|
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
8726
|
-
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
|
+
}]
|
|
8727
8804
|
};
|
|
8728
8805
|
var JunieHooks = class JunieHooks extends ToolHooks {
|
|
8729
8806
|
constructor(params) {
|
|
@@ -9759,8 +9836,9 @@ function reasonixHooksToCanonical(hooks) {
|
|
|
9759
9836
|
* Reasonix hooks live in a Claude-Code-style but standalone JSON file —
|
|
9760
9837
|
* `.reasonix/settings.json` (project) or `~/.reasonix/settings.json`
|
|
9761
9838
|
* (global) — separate from the `[permissions]`/`[[plugins]]` TOML config.
|
|
9762
|
-
*
|
|
9763
|
-
* 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).
|
|
9764
9842
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
9765
9843
|
*/
|
|
9766
9844
|
var ReasonixHooks = class ReasonixHooks extends ToolHooks {
|
|
@@ -11839,7 +11917,7 @@ function parseAmpSettingsJsonc(fileContent) {
|
|
|
11839
11917
|
const details = errors.map((error) => `${(0, jsonc_parser.printParseErrorCode)(error.error)} at offset ${error.offset}`).join(", ");
|
|
11840
11918
|
throw new Error(`Failed to parse Amp settings: ${details}`);
|
|
11841
11919
|
}
|
|
11842
|
-
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");
|
|
11843
11921
|
return parsed;
|
|
11844
11922
|
}
|
|
11845
11923
|
function filterMcpServers(mcpServers) {
|
|
@@ -12161,7 +12239,7 @@ function parseAugmentcodeSettings(fileContent, relativeDirPath, relativeFilePath
|
|
|
12161
12239
|
} catch (error) {
|
|
12162
12240
|
throw new Error(`Failed to parse AugmentCode settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
12163
12241
|
}
|
|
12164
|
-
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`);
|
|
12165
12243
|
return parsed;
|
|
12166
12244
|
}
|
|
12167
12245
|
/**
|
|
@@ -12387,7 +12465,7 @@ function parseClineSettings(fileContent, relativeDirPath, relativeFilePath) {
|
|
|
12387
12465
|
} catch (error) {
|
|
12388
12466
|
throw new Error(`Failed to parse Cline MCP settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
12389
12467
|
}
|
|
12390
|
-
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`);
|
|
12391
12469
|
return parsed;
|
|
12392
12470
|
}
|
|
12393
12471
|
/**
|
|
@@ -12658,7 +12736,7 @@ var CodexcliMcp = class CodexcliMcp extends ToolMcp {
|
|
|
12658
12736
|
for (const [key, value] of Object.entries(obj)) {
|
|
12659
12737
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
12660
12738
|
if (value === null) continue;
|
|
12661
|
-
if (isPlainObject(value)) {
|
|
12739
|
+
if (isPlainObject$1(value)) {
|
|
12662
12740
|
const cleaned = this.removeEmptyEntries(value, depth + 1);
|
|
12663
12741
|
if (Object.keys(cleaned).length === 0) continue;
|
|
12664
12742
|
filtered[key] = cleaned;
|
|
@@ -13299,12 +13377,12 @@ function parseGooseConfig(fileContent, relativeDirPath, relativeFilePath) {
|
|
|
13299
13377
|
const configPath = (0, node_path.join)(relativeDirPath, relativeFilePath);
|
|
13300
13378
|
let parsed;
|
|
13301
13379
|
try {
|
|
13302
|
-
parsed = (
|
|
13380
|
+
parsed = loadYaml(fileContent);
|
|
13303
13381
|
} catch (error) {
|
|
13304
13382
|
throw new Error(`Failed to parse Goose config at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
13305
13383
|
}
|
|
13306
13384
|
if (parsed === void 0 || parsed === null) return {};
|
|
13307
|
-
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`);
|
|
13308
13386
|
return parsed;
|
|
13309
13387
|
}
|
|
13310
13388
|
/**
|
|
@@ -13349,7 +13427,7 @@ function applyGooseStdioFields(ext, config) {
|
|
|
13349
13427
|
ext.cmd = command;
|
|
13350
13428
|
if (isStringArray(config.args)) ext.args = config.args;
|
|
13351
13429
|
}
|
|
13352
|
-
if (isPlainObject(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
13430
|
+
if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
13353
13431
|
}
|
|
13354
13432
|
/**
|
|
13355
13433
|
* Converts a single rulesync canonical MCP server into a Goose `extensions:` entry.
|
|
@@ -13364,7 +13442,7 @@ function convertServerToGooseExtension(name, config) {
|
|
|
13364
13442
|
if (gooseType === "stdio") applyGooseStdioFields(ext, config);
|
|
13365
13443
|
else if (gooseType === "sse" || gooseType === "streamable_http") {
|
|
13366
13444
|
if (url !== void 0) ext.uri = url;
|
|
13367
|
-
if (isPlainObject(config.headers)) ext.headers = omitPrototypePollutionKeys(config.headers);
|
|
13445
|
+
if (isPlainObject$1(config.headers)) ext.headers = omitPrototypePollutionKeys(config.headers);
|
|
13368
13446
|
}
|
|
13369
13447
|
ext.enabled = config.disabled !== true;
|
|
13370
13448
|
const timeout = resolveGooseTimeout(config);
|
|
@@ -13406,9 +13484,9 @@ function convertFromGooseFormat(extensions) {
|
|
|
13406
13484
|
else if (type === "stdio") server.type = "stdio";
|
|
13407
13485
|
if (typeof ext.cmd === "string") server.command = ext.cmd;
|
|
13408
13486
|
if (isStringArray(ext.args)) server.args = ext.args;
|
|
13409
|
-
if (isPlainObject(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
13487
|
+
if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
13410
13488
|
if (typeof ext.uri === "string") server.url = ext.uri;
|
|
13411
|
-
if (isPlainObject(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
|
|
13489
|
+
if (isPlainObject$1(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
|
|
13412
13490
|
if (ext.enabled === false) server.disabled = true;
|
|
13413
13491
|
if (typeof ext.timeout === "number") server.timeout = ext.timeout;
|
|
13414
13492
|
result[name] = server;
|
|
@@ -13431,7 +13509,7 @@ function buildGoosePluginStdioServer(config) {
|
|
|
13431
13509
|
server.command = command;
|
|
13432
13510
|
if (isStringArray(config.args)) server.args = config.args;
|
|
13433
13511
|
}
|
|
13434
|
-
if (isPlainObject(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13512
|
+
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13435
13513
|
if (typeof config.cwd === "string") server.cwd = config.cwd;
|
|
13436
13514
|
return server;
|
|
13437
13515
|
}
|
|
@@ -13731,7 +13809,7 @@ var GrokcliMcp = class GrokcliMcp extends ToolMcp {
|
|
|
13731
13809
|
for (const [key, value] of Object.entries(obj)) {
|
|
13732
13810
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
13733
13811
|
if (value === null) continue;
|
|
13734
|
-
if (isPlainObject(value)) {
|
|
13812
|
+
if (isPlainObject$1(value)) {
|
|
13735
13813
|
const cleaned = this.removeEmptyEntries(value, depth + 1);
|
|
13736
13814
|
if (Object.keys(cleaned).length === 0) continue;
|
|
13737
13815
|
filtered[key] = cleaned;
|
|
@@ -13793,10 +13871,10 @@ function convertServerToHermes(config) {
|
|
|
13793
13871
|
out.command = command;
|
|
13794
13872
|
if (isStringArray(config.args)) out.args = config.args;
|
|
13795
13873
|
}
|
|
13796
|
-
if (isPlainObject(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
13874
|
+
if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
13797
13875
|
} else if (url !== void 0) {
|
|
13798
13876
|
out.url = url;
|
|
13799
|
-
if (isPlainObject(config.headers)) out.headers = omitPrototypePollutionKeys(config.headers);
|
|
13877
|
+
if (isPlainObject$1(config.headers)) out.headers = omitPrototypePollutionKeys(config.headers);
|
|
13800
13878
|
}
|
|
13801
13879
|
if (config.disabled === true) out.enabled = false;
|
|
13802
13880
|
const timeout = resolveHermesTimeout(config);
|
|
@@ -13841,9 +13919,9 @@ function convertFromHermesFormat(mcpServers) {
|
|
|
13841
13919
|
const server = {};
|
|
13842
13920
|
if (typeof config.command === "string") server.command = config.command;
|
|
13843
13921
|
if (isStringArray(config.args)) server.args = config.args;
|
|
13844
|
-
if (isPlainObject(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13922
|
+
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13845
13923
|
if (typeof config.url === "string") server.url = config.url;
|
|
13846
|
-
if (isPlainObject(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
13924
|
+
if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
13847
13925
|
if (config.enabled === false) server.disabled = true;
|
|
13848
13926
|
if (typeof config.timeout === "number") server.networkTimeout = config.timeout;
|
|
13849
13927
|
if (isRecord(config.tools)) {
|
|
@@ -14780,7 +14858,9 @@ const REASONIX_PLUGIN_FIELDS = [
|
|
|
14780
14858
|
"env",
|
|
14781
14859
|
"url",
|
|
14782
14860
|
"headers",
|
|
14783
|
-
"trusted_read_only_tools"
|
|
14861
|
+
"trusted_read_only_tools",
|
|
14862
|
+
"call_timeout_seconds",
|
|
14863
|
+
"tool_timeout_seconds"
|
|
14784
14864
|
];
|
|
14785
14865
|
var ReasonixMcp = class ReasonixMcp extends ToolMcp {
|
|
14786
14866
|
toml;
|
|
@@ -15012,7 +15092,7 @@ function parseRovodevMcpJson(fileContent, relativeDirPath, relativeFilePath) {
|
|
|
15012
15092
|
} catch (error) {
|
|
15013
15093
|
throw new Error(`Failed to parse Rovodev MCP config at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
15014
15094
|
}
|
|
15015
|
-
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`);
|
|
15016
15096
|
return parsed;
|
|
15017
15097
|
}
|
|
15018
15098
|
/**
|
|
@@ -16283,15 +16363,123 @@ const AntigravityCliPermissionsOverrideSchema = zod_mini.z.looseObject({
|
|
|
16283
16363
|
enableTerminalSandbox: zod_mini.z.optional(zod_mini.z.boolean())
|
|
16284
16364
|
});
|
|
16285
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
|
+
/**
|
|
16286
16473
|
* Permissions configuration.
|
|
16287
16474
|
* Keys are tool category names (e.g., "bash", "edit", "read", "webfetch").
|
|
16288
16475
|
* Values are pattern-to-action mappings for that tool category.
|
|
16289
16476
|
*
|
|
16290
16477
|
* The optional `opencode`/`hermes`/`cline`/`kilo`/`claudecode`/`vibe`/`cursor`/
|
|
16291
16478
|
* `qwencode`/`reasonix`/`factorydroid`/`warp`/`junie`/`takt`/`amp`/
|
|
16292
|
-
* `antigravity-cli` keys are tool-scoped
|
|
16293
|
-
* respective translator (see the matching
|
|
16294
|
-
* 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.
|
|
16295
16483
|
*
|
|
16296
16484
|
* @example
|
|
16297
16485
|
* {
|
|
@@ -16315,7 +16503,10 @@ const PermissionsConfigSchema = zod_mini.z.looseObject({
|
|
|
16315
16503
|
junie: zod_mini.z.optional(JuniePermissionsOverrideSchema),
|
|
16316
16504
|
takt: zod_mini.z.optional(TaktPermissionsOverrideSchema),
|
|
16317
16505
|
amp: zod_mini.z.optional(AmpPermissionsOverrideSchema),
|
|
16318
|
-
"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)
|
|
16319
16510
|
});
|
|
16320
16511
|
/**
|
|
16321
16512
|
* Full permissions file schema including optional $schema field.
|
|
@@ -16467,7 +16658,7 @@ function parseAmpSettings(fileContent) {
|
|
|
16467
16658
|
const details = errors.map((error) => `${(0, jsonc_parser.printParseErrorCode)(error.error)} at offset ${error.offset}`).join(", ");
|
|
16468
16659
|
throw new Error(`Failed to parse Amp settings: ${details}`);
|
|
16469
16660
|
}
|
|
16470
|
-
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");
|
|
16471
16662
|
return parsed;
|
|
16472
16663
|
}
|
|
16473
16664
|
function toDisableList(value) {
|
|
@@ -16494,7 +16685,7 @@ function toPermissionsList(value) {
|
|
|
16494
16685
|
if (!Array.isArray(value)) return [];
|
|
16495
16686
|
const entries = [];
|
|
16496
16687
|
for (const raw of value) {
|
|
16497
|
-
if (!isPlainObject(raw)) continue;
|
|
16688
|
+
if (!isPlainObject$1(raw)) continue;
|
|
16498
16689
|
const { tool, action } = raw;
|
|
16499
16690
|
if (typeof tool !== "string" || typeof action !== "string") continue;
|
|
16500
16691
|
if (action !== "allow" && action !== "reject" && action !== "ask" && action !== "delegate") continue;
|
|
@@ -16502,7 +16693,7 @@ function toPermissionsList(value) {
|
|
|
16502
16693
|
...raw,
|
|
16503
16694
|
tool,
|
|
16504
16695
|
action,
|
|
16505
|
-
...isPlainObject(raw.matches) ? { matches: raw.matches } : {}
|
|
16696
|
+
...isPlainObject$1(raw.matches) ? { matches: raw.matches } : {}
|
|
16506
16697
|
});
|
|
16507
16698
|
}
|
|
16508
16699
|
return entries;
|
|
@@ -17362,6 +17553,55 @@ function isSpecialEntry(entry) {
|
|
|
17362
17553
|
return false;
|
|
17363
17554
|
}
|
|
17364
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
|
+
/**
|
|
17365
17605
|
* Convert a glob-like pattern into a regex string for AugmentCode's `shellInputRegex`.
|
|
17366
17606
|
* Maps glob `*` to `.*`, `?` to `.`, escapes other regex metacharacters, and anchors at both ends.
|
|
17367
17607
|
*/
|
|
@@ -17492,12 +17732,18 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
17492
17732
|
} catch (error) {
|
|
17493
17733
|
throw new Error(`Failed to parse existing AugmentCode settings at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
17494
17734
|
}
|
|
17735
|
+
const config = rulesyncPermissions.getJson();
|
|
17495
17736
|
const generated = convertRulesyncToAugmentEntries({
|
|
17496
|
-
config
|
|
17737
|
+
config,
|
|
17497
17738
|
logger
|
|
17498
17739
|
});
|
|
17499
17740
|
const existingEntries = settings.toolPermissions ?? [];
|
|
17500
|
-
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]);
|
|
17501
17747
|
const basicExistingEntries = existingEntries.filter((entry) => !isSpecialEntry(entry));
|
|
17502
17748
|
const generatedKeys = new Set(generated.map((e) => `${e.toolName}|${e.shellInputRegex ?? ""}|${e.permission.type}`));
|
|
17503
17749
|
const preservedBasicEntries = basicExistingEntries.filter((entry) => {
|
|
@@ -17508,7 +17754,11 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
17508
17754
|
}
|
|
17509
17755
|
return false;
|
|
17510
17756
|
});
|
|
17511
|
-
const sortedBasic = sortAugmentEntries([
|
|
17757
|
+
const sortedBasic = sortAugmentEntries([
|
|
17758
|
+
...generated,
|
|
17759
|
+
...preservedBasicEntries,
|
|
17760
|
+
...authoredBasics
|
|
17761
|
+
]);
|
|
17512
17762
|
const merged = {
|
|
17513
17763
|
...settings,
|
|
17514
17764
|
toolPermissions: [...specialEntries, ...sortedBasic]
|
|
@@ -17532,11 +17782,14 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
17532
17782
|
} catch (error) {
|
|
17533
17783
|
throw new Error(`Failed to parse AugmentCode permissions content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
17534
17784
|
}
|
|
17535
|
-
const
|
|
17536
|
-
|
|
17785
|
+
const allEntries = settings.toolPermissions ?? [];
|
|
17786
|
+
const specialEntries = allEntries.filter((entry) => isSpecialEntry(entry));
|
|
17787
|
+
const result = { ...convertAugmentToRulesyncPermissions({
|
|
17788
|
+
entries: allEntries.filter((entry) => !isSpecialEntry(entry)),
|
|
17537
17789
|
logger: moduleLogger$1
|
|
17538
|
-
});
|
|
17539
|
-
|
|
17790
|
+
}) };
|
|
17791
|
+
if (specialEntries.length > 0) result.augmentcode = { toolPermissions: specialEntries };
|
|
17792
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
17540
17793
|
}
|
|
17541
17794
|
validate() {
|
|
17542
17795
|
try {
|
|
@@ -17670,10 +17923,7 @@ function convertAugmentToRulesyncPermissions({ entries, logger }) {
|
|
|
17670
17923
|
]);
|
|
17671
17924
|
const permission = {};
|
|
17672
17925
|
for (const entry of entries) {
|
|
17673
|
-
if (isSpecialEntry(entry))
|
|
17674
|
-
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.`);
|
|
17675
|
-
continue;
|
|
17676
|
-
}
|
|
17926
|
+
if (isSpecialEntry(entry)) continue;
|
|
17677
17927
|
const type = entry.permission.type;
|
|
17678
17928
|
if (!isBasicAugmentType(type)) continue;
|
|
17679
17929
|
const canonical = toCanonicalToolName$5(entry.toolName);
|
|
@@ -18105,6 +18355,13 @@ const WORKSPACE_WIDE_WRITE_PATTERNS = /* @__PURE__ */ new Set([
|
|
|
18105
18355
|
]);
|
|
18106
18356
|
const CODEX_MINIMAL_KEY = ":minimal";
|
|
18107
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
|
+
];
|
|
18108
18365
|
var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
18109
18366
|
static getSettablePaths(_options = {}) {
|
|
18110
18367
|
return {
|
|
@@ -18146,6 +18403,11 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
|
18146
18403
|
});
|
|
18147
18404
|
parsed.permissions = permissionsTable;
|
|
18148
18405
|
parsed.default_permissions = RULESYNC_PROFILE_NAME;
|
|
18406
|
+
applyCodexcliOverride({
|
|
18407
|
+
parsed,
|
|
18408
|
+
override: rulesyncPermissions.getJson().codexcli,
|
|
18409
|
+
logger
|
|
18410
|
+
});
|
|
18149
18411
|
return new CodexcliPermissions({
|
|
18150
18412
|
outputRoot,
|
|
18151
18413
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -18170,7 +18432,12 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
|
18170
18432
|
profile,
|
|
18171
18433
|
domainsHadUnknown
|
|
18172
18434
|
});
|
|
18173
|
-
|
|
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) });
|
|
18174
18441
|
}
|
|
18175
18442
|
validate() {
|
|
18176
18443
|
return {
|
|
@@ -18362,6 +18629,30 @@ function toMutableTable(value) {
|
|
|
18362
18629
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
18363
18630
|
return { ...value };
|
|
18364
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
|
+
}
|
|
18365
18656
|
function toFilesystemRecord(value) {
|
|
18366
18657
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
18367
18658
|
const result = {};
|
|
@@ -19276,7 +19567,7 @@ var GoosePermissions = class GoosePermissions extends ToolPermissions {
|
|
|
19276
19567
|
const existingContent = await readFileContentOrNull(filePath) ?? "";
|
|
19277
19568
|
let parsed;
|
|
19278
19569
|
try {
|
|
19279
|
-
parsed = existingContent.trim() === "" ? {} : (
|
|
19570
|
+
parsed = existingContent.trim() === "" ? {} : loadYaml(existingContent);
|
|
19280
19571
|
} catch (error) {
|
|
19281
19572
|
throw new Error(`Failed to parse existing Goose permission.yaml at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
19282
19573
|
}
|
|
@@ -19298,7 +19589,7 @@ var GoosePermissions = class GoosePermissions extends ToolPermissions {
|
|
|
19298
19589
|
let parsed;
|
|
19299
19590
|
try {
|
|
19300
19591
|
const content = this.getFileContent();
|
|
19301
|
-
parsed = content.trim() === "" ? {} : (
|
|
19592
|
+
parsed = content.trim() === "" ? {} : loadYaml(content);
|
|
19302
19593
|
} catch (error) {
|
|
19303
19594
|
throw new Error(`Failed to parse Goose permissions content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
19304
19595
|
}
|
|
@@ -20205,6 +20496,13 @@ const KiroAgentSchema = zod_mini.z.looseObject({
|
|
|
20205
20496
|
toolsSettings: zod_mini.z.optional(zod_mini.z.record(zod_mini.z.string(), zod_mini.z.unknown()))
|
|
20206
20497
|
});
|
|
20207
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
|
+
]);
|
|
20208
20506
|
var KiroPermissions = class KiroPermissions extends ToolPermissions {
|
|
20209
20507
|
static getSettablePaths(_options = {}) {
|
|
20210
20508
|
return {
|
|
@@ -20268,7 +20566,10 @@ var KiroPermissions = class KiroPermissions extends ToolPermissions {
|
|
|
20268
20566
|
const allowedTools = new Set(parsed.allowedTools ?? []);
|
|
20269
20567
|
if (allowedTools.has("web_fetch")) permission.webfetch = { "*": "allow" };
|
|
20270
20568
|
if (allowedTools.has("web_search")) permission.websearch = { "*": "allow" };
|
|
20271
|
-
|
|
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) });
|
|
20272
20573
|
}
|
|
20273
20574
|
validate() {
|
|
20274
20575
|
return {
|
|
@@ -20318,19 +20619,71 @@ function buildKiroPermissionsFromRulesync({ config, logger, existing }) {
|
|
|
20318
20619
|
});
|
|
20319
20620
|
else logger?.warn(`Kiro permissions do not support category: ${category}. Skipping.`);
|
|
20320
20621
|
}
|
|
20321
|
-
nextToolsSettings.shell =
|
|
20622
|
+
nextToolsSettings.shell = {
|
|
20623
|
+
...preservedShellFlags(existing),
|
|
20624
|
+
...shell
|
|
20625
|
+
};
|
|
20322
20626
|
nextToolsSettings.read = pathTable(pathBuckets.read);
|
|
20323
20627
|
nextToolsSettings.write = pathTable(pathBuckets.write);
|
|
20324
20628
|
for (const key of ["grep", "glob"]) {
|
|
20325
20629
|
const bucket = pathBuckets[key];
|
|
20326
20630
|
if (bucket && (bucket.allow.length > 0 || bucket.deny.length > 0)) nextToolsSettings[key] = pathTable(bucket);
|
|
20327
20631
|
}
|
|
20632
|
+
applyKiroOverride({
|
|
20633
|
+
override: config.kiro,
|
|
20634
|
+
nextToolsSettings,
|
|
20635
|
+
logger
|
|
20636
|
+
});
|
|
20328
20637
|
return {
|
|
20329
20638
|
...existing,
|
|
20330
20639
|
allowedTools: [...nextAllowedTools].toSorted(),
|
|
20331
20640
|
toolsSettings: nextToolsSettings
|
|
20332
20641
|
};
|
|
20333
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
|
+
}
|
|
20334
20687
|
function pathTable(bucket) {
|
|
20335
20688
|
return {
|
|
20336
20689
|
allowedPaths: bucket?.allow ?? [],
|
|
@@ -20350,6 +20703,31 @@ function asRecord$1(value) {
|
|
|
20350
20703
|
const result = UnknownRecordSchema.safeParse(value);
|
|
20351
20704
|
return result.success ? result.data : {};
|
|
20352
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
|
+
}
|
|
20353
20731
|
function asStringArray(value) {
|
|
20354
20732
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
20355
20733
|
}
|
|
@@ -21162,7 +21540,7 @@ var RovodevPermissions = class RovodevPermissions extends ToolPermissions {
|
|
|
21162
21540
|
const existingContent = await readFileContentOrNull(filePath) ?? "";
|
|
21163
21541
|
let parsed;
|
|
21164
21542
|
try {
|
|
21165
|
-
parsed = existingContent.trim() === "" ? {} : (
|
|
21543
|
+
parsed = existingContent.trim() === "" ? {} : loadYaml(existingContent);
|
|
21166
21544
|
} catch (error) {
|
|
21167
21545
|
throw new Error(`Failed to parse existing Rovodev config at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
21168
21546
|
}
|
|
@@ -21188,7 +21566,7 @@ var RovodevPermissions = class RovodevPermissions extends ToolPermissions {
|
|
|
21188
21566
|
let parsed;
|
|
21189
21567
|
try {
|
|
21190
21568
|
const content = this.getFileContent();
|
|
21191
|
-
parsed = content.trim() === "" ? {} : (
|
|
21569
|
+
parsed = content.trim() === "" ? {} : loadYaml(content);
|
|
21192
21570
|
} catch (error) {
|
|
21193
21571
|
throw new Error(`Failed to parse Rovodev permissions content in ${(0, node_path.join)(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
21194
21572
|
}
|
|
@@ -21383,9 +21761,9 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
|
|
|
21383
21761
|
const rulesyncJson = rulesyncPermissions.getJson();
|
|
21384
21762
|
const provider = resolveActiveProvider(config);
|
|
21385
21763
|
const mode = deriveTaktPermissionMode(rulesyncJson);
|
|
21386
|
-
const override = isPlainObject(rulesyncJson.takt) ? rulesyncJson.takt : void 0;
|
|
21387
|
-
const stepOverrides = isPlainObject(override?.[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? override[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21388
|
-
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;
|
|
21389
21767
|
const patch = {
|
|
21390
21768
|
[TAKT_PROVIDER_PROFILES_KEY]: { [provider]: {
|
|
21391
21769
|
[TAKT_DEFAULT_PERMISSION_MODE_KEY]: mode,
|
|
@@ -21416,12 +21794,12 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
|
|
|
21416
21794
|
invalidRootPolicy: "error"
|
|
21417
21795
|
});
|
|
21418
21796
|
const provider = resolveActiveProvider(config);
|
|
21419
|
-
const profiles = isPlainObject(config[TAKT_PROVIDER_PROFILES_KEY]) ? config[TAKT_PROVIDER_PROFILES_KEY] : {};
|
|
21420
|
-
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] : {};
|
|
21421
21799
|
const mode = profile[TAKT_DEFAULT_PERMISSION_MODE_KEY];
|
|
21422
21800
|
const rulesyncConfig = taktModeToRulesyncConfig(mode);
|
|
21423
|
-
const stepOverrides = isPlainObject(profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21424
|
-
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;
|
|
21425
21803
|
const taktOverride = {};
|
|
21426
21804
|
if (stepOverrides && Object.keys(stepOverrides).length > 0) taktOverride[TAKT_STEP_PERMISSION_OVERRIDES_KEY] = stepOverrides;
|
|
21427
21805
|
if (providerOptions && Object.keys(providerOptions).length > 0) taktOverride[TAKT_PROVIDER_OPTIONS_KEY] = providerOptions;
|
|
@@ -21453,7 +21831,7 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
|
|
|
21453
21831
|
function resolveActiveProvider(config) {
|
|
21454
21832
|
if (typeof config[TAKT_PROVIDER_KEY] === "string" && config[TAKT_PROVIDER_KEY].trim() !== "") return config[TAKT_PROVIDER_KEY];
|
|
21455
21833
|
const profiles = config[TAKT_PROVIDER_PROFILES_KEY];
|
|
21456
|
-
if (isPlainObject(profiles)) {
|
|
21834
|
+
if (isPlainObject$1(profiles)) {
|
|
21457
21835
|
const keys = Object.keys(profiles);
|
|
21458
21836
|
if (keys.length === 1) return keys[0];
|
|
21459
21837
|
}
|
|
@@ -24442,7 +24820,7 @@ function extractOpenaiYamlFile(otherFiles) {
|
|
|
24442
24820
|
const rest = [];
|
|
24443
24821
|
for (const file of otherFiles) {
|
|
24444
24822
|
if (toPosixPath(file.relativeFilePathToDirPath) === target) try {
|
|
24445
|
-
const loaded = (
|
|
24823
|
+
const loaded = loadYaml(file.fileBuffer.toString("utf-8"));
|
|
24446
24824
|
if (loaded !== null && typeof loaded === "object" && !Array.isArray(loaded)) {
|
|
24447
24825
|
parsed = loaded;
|
|
24448
24826
|
continue;
|
|
@@ -26693,6 +27071,139 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
|
|
|
26693
27071
|
}
|
|
26694
27072
|
};
|
|
26695
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
|
|
26696
27207
|
//#region src/constants/replit-paths.ts
|
|
26697
27208
|
const REPLIT_RULE_FILE_NAME = "replit.md";
|
|
26698
27209
|
const REPLIT_SKILLS_DIR_PATH = (0, node_path.join)(".agents", "skills");
|
|
@@ -27787,6 +28298,14 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
27787
28298
|
supportsGlobal: true
|
|
27788
28299
|
}
|
|
27789
28300
|
}],
|
|
28301
|
+
["reasonix", {
|
|
28302
|
+
class: ReasonixSkill,
|
|
28303
|
+
meta: {
|
|
28304
|
+
supportsProject: true,
|
|
28305
|
+
supportsSimulated: false,
|
|
28306
|
+
supportsGlobal: true
|
|
28307
|
+
}
|
|
28308
|
+
}],
|
|
27790
28309
|
["replit", {
|
|
27791
28310
|
class: ReplitSkill,
|
|
27792
28311
|
meta: {
|
|
@@ -30070,7 +30589,7 @@ var GooseSubagent = class GooseSubagent extends ToolSubagent {
|
|
|
30070
30589
|
const fileContent = await readFileContent(filePath);
|
|
30071
30590
|
let parsed;
|
|
30072
30591
|
try {
|
|
30073
|
-
parsed = (
|
|
30592
|
+
parsed = loadYaml(fileContent);
|
|
30074
30593
|
} catch (error) {
|
|
30075
30594
|
throw new Error(`Failed to parse Goose recipe (${filePath}): ${formatError(error)}`, { cause: error });
|
|
30076
30595
|
}
|
|
@@ -31458,7 +31977,7 @@ var RooSubagent = class RooSubagent extends ToolSubagent {
|
|
|
31458
31977
|
const fileContent = await readFileContent(filePath);
|
|
31459
31978
|
let parsed;
|
|
31460
31979
|
try {
|
|
31461
|
-
parsed = (
|
|
31980
|
+
parsed = loadYaml(fileContent);
|
|
31462
31981
|
} catch (error) {
|
|
31463
31982
|
throw new Error(`Failed to parse .roomodes (${filePath}): ${formatError(error)}`, { cause: error });
|
|
31464
31983
|
}
|
|
@@ -35651,6 +36170,72 @@ var QwencodeRule = class QwencodeRule extends ToolRule {
|
|
|
35651
36170
|
}
|
|
35652
36171
|
};
|
|
35653
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
|
|
35654
36239
|
//#region src/features/rules/replit-rule.ts
|
|
35655
36240
|
/**
|
|
35656
36241
|
* Rule generator for Replit Agent
|
|
@@ -36563,6 +37148,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
36563
37148
|
additionalConventions: { subagents: { subagentClass: QwencodeSubagent } }
|
|
36564
37149
|
}
|
|
36565
37150
|
}],
|
|
37151
|
+
["reasonix", {
|
|
37152
|
+
class: ReasonixRule,
|
|
37153
|
+
meta: {
|
|
37154
|
+
extension: "md",
|
|
37155
|
+
supportsGlobal: true,
|
|
37156
|
+
ruleDiscoveryMode: "auto",
|
|
37157
|
+
foldsNonRootIntoRoot: true
|
|
37158
|
+
}
|
|
37159
|
+
}],
|
|
36566
37160
|
["replit", {
|
|
36567
37161
|
class: ReplitRule,
|
|
36568
37162
|
meta: {
|
|
@@ -37448,7 +38042,6 @@ const SHARED_WRITE_FEATURE_ORDER = [
|
|
|
37448
38042
|
"permissions",
|
|
37449
38043
|
"rules"
|
|
37450
38044
|
];
|
|
37451
|
-
const SHARED_WRITE_FEATURES = new Set(SHARED_WRITE_FEATURE_ORDER);
|
|
37452
38045
|
const TARGETS_NOT_DERIVED = /* @__PURE__ */ new Set(["augmentcode-legacy", "claudecode-legacy"]);
|
|
37453
38046
|
const sharedFileKey = (path) => {
|
|
37454
38047
|
const dir = path.relativeDirPath.replace(/\\/g, "/").replace(/\/$/, "");
|
|
@@ -37471,7 +38064,13 @@ const settablePathsForScope = (cls, global) => {
|
|
|
37471
38064
|
paths.push(settable.root);
|
|
37472
38065
|
for (const alt of settable.alternativeRoots ?? []) paths.push(alt);
|
|
37473
38066
|
}
|
|
37474
|
-
|
|
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);
|
|
37475
38074
|
return paths;
|
|
37476
38075
|
};
|
|
37477
38076
|
const collectFactoryPaths = (factory) => [...settablePathsForScope(factory.class, false), ...settablePathsForScope(factory.class, true)];
|
|
@@ -37484,7 +38083,6 @@ const deriveSharedFileWriters = () => {
|
|
|
37484
38083
|
const byKey = /* @__PURE__ */ new Map();
|
|
37485
38084
|
const pathByKey = /* @__PURE__ */ new Map();
|
|
37486
38085
|
for (const entry of PROCESSOR_REGISTRY) {
|
|
37487
|
-
if (!SHARED_WRITE_FEATURES.has(entry.feature)) continue;
|
|
37488
38086
|
const factories = entry.factory;
|
|
37489
38087
|
for (const [tool, factory] of factories) {
|
|
37490
38088
|
if (TARGETS_NOT_DERIVED.has(tool)) continue;
|
|
@@ -38906,6 +39504,12 @@ Object.defineProperty(exports, "listDirectoryFiles", {
|
|
|
38906
39504
|
return listDirectoryFiles;
|
|
38907
39505
|
}
|
|
38908
39506
|
});
|
|
39507
|
+
Object.defineProperty(exports, "loadYaml", {
|
|
39508
|
+
enumerable: true,
|
|
39509
|
+
get: function() {
|
|
39510
|
+
return loadYaml;
|
|
39511
|
+
}
|
|
39512
|
+
});
|
|
38909
39513
|
Object.defineProperty(exports, "readFileContent", {
|
|
38910
39514
|
enumerable: true,
|
|
38911
39515
|
get: function() {
|