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
|
@@ -7,9 +7,9 @@ import os from "node:os";
|
|
|
7
7
|
import { intersection, kebabCase, uniq } from "es-toolkit";
|
|
8
8
|
import { globbySync } from "globby";
|
|
9
9
|
import { isDeepStrictEqual } from "node:util";
|
|
10
|
-
import { dump, load } from "js-yaml";
|
|
11
10
|
import * as smolToml from "smol-toml";
|
|
12
11
|
import matter from "gray-matter";
|
|
12
|
+
import { YAMLException, dump, load } from "js-yaml";
|
|
13
13
|
import { omit } from "es-toolkit/object";
|
|
14
14
|
import { encode } from "@toon-format/toon";
|
|
15
15
|
//#region src/types/features.ts
|
|
@@ -141,6 +141,7 @@ const rulesProcessorToolTargetTuple = [
|
|
|
141
141
|
"opencode",
|
|
142
142
|
"pi",
|
|
143
143
|
"qwencode",
|
|
144
|
+
"reasonix",
|
|
144
145
|
"replit",
|
|
145
146
|
"roo",
|
|
146
147
|
"rovodev",
|
|
@@ -288,6 +289,7 @@ const skillsProcessorToolTargetTuple = [
|
|
|
288
289
|
"opencode",
|
|
289
290
|
"pi",
|
|
290
291
|
"qwencode",
|
|
292
|
+
"reasonix",
|
|
291
293
|
"replit",
|
|
292
294
|
"roo",
|
|
293
295
|
"rovodev",
|
|
@@ -852,7 +854,7 @@ const SourceEntrySchema = z.object({
|
|
|
852
854
|
scope: optional(z.enum(["project", "user"]))
|
|
853
855
|
});
|
|
854
856
|
const ConfigParamsSchema = z.object({
|
|
855
|
-
outputRoots: z.array(z.string()),
|
|
857
|
+
outputRoots: z.union([z.array(z.string()), z.record(z.string(), z.union([z.string(), z.array(z.string())]))]),
|
|
856
858
|
targets: RulesyncConfigTargetsSchema,
|
|
857
859
|
features: RulesyncFeaturesSchema,
|
|
858
860
|
verbose: z.boolean(),
|
|
@@ -957,6 +959,7 @@ var Config = class Config {
|
|
|
957
959
|
const resolvedTargets = targets ?? [];
|
|
958
960
|
const resolvedFeatures = features ?? [];
|
|
959
961
|
this.validateObjectFormTargetKeys(resolvedTargets);
|
|
962
|
+
this.validateObjectFormOutputRootKeys(outputRoots);
|
|
960
963
|
this.validateConflictingTargets(resolvedTargets);
|
|
961
964
|
if (dryRun && check) throw new Error("--dry-run and --check cannot be used together");
|
|
962
965
|
this.outputRoots = outputRoots;
|
|
@@ -994,6 +997,11 @@ var Config = class Config {
|
|
|
994
997
|
if (!validTargets.has(key)) throw new Error(`Unknown target '${key}'. Valid targets: ${ALL_TOOL_TARGETS.join(", ")}.`);
|
|
995
998
|
}
|
|
996
999
|
}
|
|
1000
|
+
validateObjectFormOutputRootKeys(outputRoots) {
|
|
1001
|
+
if (Array.isArray(outputRoots)) return;
|
|
1002
|
+
const validTargets = new Set(ALL_TOOL_TARGETS);
|
|
1003
|
+
for (const key of Object.keys(outputRoots)) if (!validTargets.has(key)) throw new Error(`Unknown outputRoots target '${key}'. Valid targets: ${ALL_TOOL_TARGETS.join(", ")}.`);
|
|
1004
|
+
}
|
|
997
1005
|
validateConflictingTargets(targets) {
|
|
998
1006
|
const has = (target) => {
|
|
999
1007
|
if (Array.isArray(targets)) return targets.includes(target);
|
|
@@ -1001,8 +1009,19 @@ var Config = class Config {
|
|
|
1001
1009
|
};
|
|
1002
1010
|
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.`);
|
|
1003
1011
|
}
|
|
1004
|
-
getOutputRoots() {
|
|
1005
|
-
return this.outputRoots;
|
|
1012
|
+
getOutputRoots(target) {
|
|
1013
|
+
if (Array.isArray(this.outputRoots)) return this.outputRoots;
|
|
1014
|
+
if (target) {
|
|
1015
|
+
const targetOutputRoots = this.outputRoots[target];
|
|
1016
|
+
if (targetOutputRoots === void 0) return [];
|
|
1017
|
+
return Array.isArray(targetOutputRoots) ? targetOutputRoots : [targetOutputRoots];
|
|
1018
|
+
}
|
|
1019
|
+
const allRoots = [];
|
|
1020
|
+
for (const value of Object.values(this.outputRoots)) {
|
|
1021
|
+
if (value === void 0) continue;
|
|
1022
|
+
allRoots.push(...Array.isArray(value) ? value : [value]);
|
|
1023
|
+
}
|
|
1024
|
+
return [...new Set(allRoots)];
|
|
1006
1025
|
}
|
|
1007
1026
|
/**
|
|
1008
1027
|
* Filter an arbitrary string-key list down to the known `ToolTarget` set,
|
|
@@ -1364,10 +1383,21 @@ var ConfigResolver = class {
|
|
|
1364
1383
|
};
|
|
1365
1384
|
function getOutputRootsInLightOfGlobal({ outputRoots, global }) {
|
|
1366
1385
|
if (global) return [getHomeDirectory()];
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1386
|
+
if (Array.isArray(outputRoots)) {
|
|
1387
|
+
outputRoots.forEach((outputRoot) => {
|
|
1388
|
+
validateOutputRoot(outputRoot);
|
|
1389
|
+
});
|
|
1390
|
+
return outputRoots.map((outputRoot) => resolve(outputRoot));
|
|
1391
|
+
}
|
|
1392
|
+
const resolvedOutputRoots = {};
|
|
1393
|
+
for (const [target, targetOutputRoots] of Object.entries(outputRoots)) {
|
|
1394
|
+
const roots = Array.isArray(targetOutputRoots) ? targetOutputRoots : [targetOutputRoots];
|
|
1395
|
+
roots.forEach((outputRoot) => {
|
|
1396
|
+
validateOutputRoot(outputRoot);
|
|
1397
|
+
});
|
|
1398
|
+
resolvedOutputRoots[target] = Array.isArray(targetOutputRoots) ? roots.map((outputRoot) => resolve(outputRoot)) : resolve(targetOutputRoots);
|
|
1399
|
+
}
|
|
1400
|
+
return resolvedOutputRoots;
|
|
1371
1401
|
}
|
|
1372
1402
|
function extractConfigFileTargets(targets) {
|
|
1373
1403
|
if (targets === void 0) return void 0;
|
|
@@ -1396,7 +1426,7 @@ function isRecord(value) {
|
|
|
1396
1426
|
* conversion, etc.) should reject inputs whose prototype could carry
|
|
1397
1427
|
* malicious accessor descriptors.
|
|
1398
1428
|
*/
|
|
1399
|
-
function isPlainObject(value) {
|
|
1429
|
+
function isPlainObject$1(value) {
|
|
1400
1430
|
if (!isRecord(value)) return false;
|
|
1401
1431
|
const proto = Object.getPrototypeOf(value);
|
|
1402
1432
|
return proto === null || proto === Object.prototype;
|
|
@@ -1408,11 +1438,43 @@ function isStringArray(value) {
|
|
|
1408
1438
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
1409
1439
|
}
|
|
1410
1440
|
//#endregion
|
|
1441
|
+
//#region src/utils/yaml.ts
|
|
1442
|
+
/**
|
|
1443
|
+
* js-yaml v5's `reason` for an empty document. `load("")` — and any
|
|
1444
|
+
* whitespace/comment-only input — throws a `YAMLException` with this reason
|
|
1445
|
+
* instead of returning `undefined` the way v4 did.
|
|
1446
|
+
*/
|
|
1447
|
+
const EMPTY_INPUT_REASON = "expected a document, but the input is empty";
|
|
1448
|
+
/**
|
|
1449
|
+
* Load YAML content while preserving js-yaml v4's behavior of returning
|
|
1450
|
+
* `undefined` for empty documents (empty, whitespace-only, or comment-only
|
|
1451
|
+
* input).
|
|
1452
|
+
*
|
|
1453
|
+
* js-yaml v5 changed `load("")` to **throw** ("expected a document, but the
|
|
1454
|
+
* input is empty") instead of returning `undefined`. Many call sites here parse
|
|
1455
|
+
* config/lock/frontmatter files that can legitimately be empty and rely on the
|
|
1456
|
+
* old `undefined` sentinel (`load(input) ?? {}`, `if (loaded === undefined)`,
|
|
1457
|
+
* "an empty file parses to undefined/null"). Routing every load through this
|
|
1458
|
+
* wrapper keeps that contract in one place so a future js-yaml bump cannot
|
|
1459
|
+
* reintroduce the empty-input regression. Genuine parse errors still propagate.
|
|
1460
|
+
*
|
|
1461
|
+
* @see https://github.com/nodeca/js-yaml/blob/master/docs/migrate_v4_to_v5.md#empty-input-throws
|
|
1462
|
+
*/
|
|
1463
|
+
function loadYaml(content) {
|
|
1464
|
+
if (content.trim() === "") return;
|
|
1465
|
+
try {
|
|
1466
|
+
return load(content);
|
|
1467
|
+
} catch (error) {
|
|
1468
|
+
if (error instanceof YAMLException && error.reason === EMPTY_INPUT_REASON) return;
|
|
1469
|
+
throw error;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
//#endregion
|
|
1411
1473
|
//#region src/utils/frontmatter.ts
|
|
1412
1474
|
function deepRemoveNullishValue(value) {
|
|
1413
1475
|
if (value === null || value === void 0) return;
|
|
1414
1476
|
if (Array.isArray(value)) return value.map((item) => deepRemoveNullishValue(item)).filter((item) => item !== void 0);
|
|
1415
|
-
if (isPlainObject(value)) {
|
|
1477
|
+
if (isPlainObject$1(value)) {
|
|
1416
1478
|
const result = {};
|
|
1417
1479
|
for (const [key, val] of Object.entries(value)) {
|
|
1418
1480
|
const cleaned = deepRemoveNullishValue(val);
|
|
@@ -1435,7 +1497,7 @@ function deepFlattenStringsValue(value) {
|
|
|
1435
1497
|
if (value === null || value === void 0) return;
|
|
1436
1498
|
if (typeof value === "string") return value.replace(/\n+/g, " ").trim();
|
|
1437
1499
|
if (Array.isArray(value)) return value.map((item) => deepFlattenStringsValue(item)).filter((item) => item !== void 0);
|
|
1438
|
-
if (isPlainObject(value)) {
|
|
1500
|
+
if (isPlainObject$1(value)) {
|
|
1439
1501
|
const result = {};
|
|
1440
1502
|
for (const [key, val] of Object.entries(value)) {
|
|
1441
1503
|
const cleaned = deepFlattenStringsValue(val);
|
|
@@ -1458,7 +1520,7 @@ function stringifyFrontmatter(body, frontmatter, options) {
|
|
|
1458
1520
|
const { avoidBlockScalars = false } = options ?? {};
|
|
1459
1521
|
const cleanFrontmatter = avoidBlockScalars ? deepFlattenStringsObject(frontmatter) : deepRemoveNullishObject(frontmatter);
|
|
1460
1522
|
if (avoidBlockScalars) return matter.stringify(body, cleanFrontmatter, { engines: { yaml: {
|
|
1461
|
-
parse: (input) =>
|
|
1523
|
+
parse: (input) => loadYaml(input) ?? {},
|
|
1462
1524
|
stringify: (data) => dump(data, { lineWidth: -1 })
|
|
1463
1525
|
} } });
|
|
1464
1526
|
return matter.stringify(body, cleanFrontmatter);
|
|
@@ -1497,7 +1559,7 @@ function tryJsonEquivalent(a, b) {
|
|
|
1497
1559
|
}
|
|
1498
1560
|
function tryYamlEquivalent(a, b) {
|
|
1499
1561
|
try {
|
|
1500
|
-
return isDeepStrictEqual(
|
|
1562
|
+
return isDeepStrictEqual(loadYaml(a), loadYaml(b));
|
|
1501
1563
|
} catch {
|
|
1502
1564
|
return;
|
|
1503
1565
|
}
|
|
@@ -3304,7 +3366,7 @@ var GooseCommand = class GooseCommand extends ToolCommand {
|
|
|
3304
3366
|
const where = join(this.relativeDirPath, this.relativeFilePath);
|
|
3305
3367
|
let parsed;
|
|
3306
3368
|
try {
|
|
3307
|
-
parsed =
|
|
3369
|
+
parsed = loadYaml(content);
|
|
3308
3370
|
} catch (error) {
|
|
3309
3371
|
throw new Error(`Failed to parse Goose recipe (${where}): ${formatError(error)}`, { cause: error });
|
|
3310
3372
|
}
|
|
@@ -3957,7 +4019,7 @@ function omitPrototypePollutionKeys(record) {
|
|
|
3957
4019
|
//#region src/features/shared/shared-config-gateway.ts
|
|
3958
4020
|
function sanitizeSharedConfigValue(value) {
|
|
3959
4021
|
if (Array.isArray(value)) return value.map(sanitizeSharedConfigValue);
|
|
3960
|
-
if (!isPlainObject(value)) return value;
|
|
4022
|
+
if (!isPlainObject$1(value)) return value;
|
|
3961
4023
|
const result = {};
|
|
3962
4024
|
for (const [key, nested] of Object.entries(omitPrototypePollutionKeys(value))) result[key] = sanitizeSharedConfigValue(nested);
|
|
3963
4025
|
return result;
|
|
@@ -3973,14 +4035,14 @@ function parseSharedConfig({ format, fileContent, filePath, invalidRootPolicy =
|
|
|
3973
4035
|
const at = filePath === void 0 ? "" : ` at ${filePath}`;
|
|
3974
4036
|
let parsed;
|
|
3975
4037
|
try {
|
|
3976
|
-
if (format === "yaml") parsed =
|
|
4038
|
+
if (format === "yaml") parsed = loadYaml(fileContent);
|
|
3977
4039
|
else if (format === "json") parsed = JSON.parse(fileContent);
|
|
3978
4040
|
else parsed = parse(fileContent);
|
|
3979
4041
|
} catch (error) {
|
|
3980
4042
|
throw new Error(`Failed to parse shared config${at}: ${formatError(error)}`, { cause: error });
|
|
3981
4043
|
}
|
|
3982
4044
|
if (parsed === void 0 || parsed === null) return {};
|
|
3983
|
-
if (!isPlainObject(parsed)) {
|
|
4045
|
+
if (!isPlainObject$1(parsed)) {
|
|
3984
4046
|
if (invalidRootPolicy === "error") throw new Error(`Failed to parse shared config${at}: expected a mapping at the root`);
|
|
3985
4047
|
return {};
|
|
3986
4048
|
}
|
|
@@ -4023,7 +4085,7 @@ function mergeSharedConfigDeep({ base, patch }) {
|
|
|
4023
4085
|
for (const [key, patchValue] of Object.entries(patch)) {
|
|
4024
4086
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
4025
4087
|
const baseValue = result[key];
|
|
4026
|
-
if (isPlainObject(baseValue) && isPlainObject(patchValue)) result[key] = mergeSharedConfigDeep({
|
|
4088
|
+
if (isPlainObject$1(baseValue) && isPlainObject$1(patchValue)) result[key] = mergeSharedConfigDeep({
|
|
4027
4089
|
base: baseValue,
|
|
4028
4090
|
patch: patchValue
|
|
4029
4091
|
});
|
|
@@ -4657,6 +4719,8 @@ const REASONIX_GLOBAL_PERMISSIONS_FILE_NAME = REASONIX_GLOBAL_MCP_FILE_NAME;
|
|
|
4657
4719
|
const REASONIX_DIR = REASONIX_GLOBAL_DIR;
|
|
4658
4720
|
const REASONIX_SETTINGS_FILE_NAME = "settings.json";
|
|
4659
4721
|
const REASONIX_COMMANDS_DIR_PATH = join(REASONIX_DIR, "commands");
|
|
4722
|
+
const REASONIX_RULE_FILE_NAME = "REASONIX.md";
|
|
4723
|
+
const REASONIX_SKILLS_DIR_PATH = join(REASONIX_DIR, "skills");
|
|
4660
4724
|
//#endregion
|
|
4661
4725
|
//#region src/features/commands/reasonix-command.ts
|
|
4662
4726
|
/**
|
|
@@ -5062,8 +5126,8 @@ var RovodevCommand = class RovodevCommand extends ToolCommand {
|
|
|
5062
5126
|
const existingContent = await readFileContentOrNull(join(outputRoot, ROVODEV_DIR, ROVODEV_PROMPTS_FILE_NAME));
|
|
5063
5127
|
let existing = {};
|
|
5064
5128
|
if (existingContent) try {
|
|
5065
|
-
const parsed =
|
|
5066
|
-
if (isPlainObject(parsed)) existing = parsed;
|
|
5129
|
+
const parsed = loadYaml(existingContent);
|
|
5130
|
+
if (isPlainObject$1(parsed)) existing = parsed;
|
|
5067
5131
|
} catch {}
|
|
5068
5132
|
const prompts = rovodevCommands.map((command) => ({
|
|
5069
5133
|
name: command.getName(),
|
|
@@ -5093,11 +5157,11 @@ async function lookupPromptDescription({ outputRoot, relativeFilePath, name }) {
|
|
|
5093
5157
|
if (!manifestContent) return "";
|
|
5094
5158
|
let parsed;
|
|
5095
5159
|
try {
|
|
5096
|
-
parsed =
|
|
5160
|
+
parsed = loadYaml(manifestContent);
|
|
5097
5161
|
} catch {
|
|
5098
5162
|
return "";
|
|
5099
5163
|
}
|
|
5100
|
-
if (!isPlainObject(parsed) || !Array.isArray(parsed.prompts)) return "";
|
|
5164
|
+
if (!isPlainObject$1(parsed) || !Array.isArray(parsed.prompts)) return "";
|
|
5101
5165
|
const expectedContentFile = toPosixPath(join("prompts", relativeFilePath));
|
|
5102
5166
|
const entry = parsed.prompts.find((candidate) => isRecord(candidate) && (candidate.content_file === expectedContentFile || candidate.name === name));
|
|
5103
5167
|
return isRecord(entry) && typeof entry.description === "string" ? entry.description : "";
|
|
@@ -6135,18 +6199,25 @@ const QWENCODE_HOOK_EVENTS = [
|
|
|
6135
6199
|
* Reasonix's `.reasonix/settings.json` (project) / `~/.reasonix/settings.json`
|
|
6136
6200
|
* (global) documents a ten-event surface (`PreToolUse`, `PostToolUse`,
|
|
6137
6201
|
* `UserPromptSubmit`, `Stop`, `PostLLMCall`, `SessionStart`, `SessionEnd`,
|
|
6138
|
-
* `SubagentStop`, `Notification`, `PreCompact`)
|
|
6139
|
-
*
|
|
6140
|
-
* `UserPromptSubmit` ← `beforeSubmitPrompt`,
|
|
6141
|
-
*
|
|
6142
|
-
*
|
|
6202
|
+
* `SubagentStop`, `Notification`, `PreCompact`). The eight events with a clean
|
|
6203
|
+
* canonical equivalent are mapped: `PreToolUse`, `PostToolUse`,
|
|
6204
|
+
* `UserPromptSubmit` ← `beforeSubmitPrompt`, `Stop`, `SessionStart`,
|
|
6205
|
+
* `SessionEnd`, `SubagentStop`, and `PostLLMCall` ← `postModelInvocation`.
|
|
6206
|
+
* (`Notification` and `PreCompact` have no canonical event and are left out.)
|
|
6207
|
+
* `match` (Reasonix's matcher field name) is honored only on
|
|
6208
|
+
* `PreToolUse`/`PostToolUse`, matching the canonical `matcher` field's
|
|
6209
|
+
* tool-event scoping used by other adapters.
|
|
6143
6210
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
6144
6211
|
*/
|
|
6145
6212
|
const REASONIX_HOOK_EVENTS = [
|
|
6146
6213
|
"preToolUse",
|
|
6147
6214
|
"postToolUse",
|
|
6148
6215
|
"beforeSubmitPrompt",
|
|
6149
|
-
"stop"
|
|
6216
|
+
"stop",
|
|
6217
|
+
"sessionStart",
|
|
6218
|
+
"sessionEnd",
|
|
6219
|
+
"subagentStop",
|
|
6220
|
+
"postModelInvocation"
|
|
6150
6221
|
];
|
|
6151
6222
|
/**
|
|
6152
6223
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
@@ -6582,14 +6653,18 @@ const QWENCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
|
|
|
6582
6653
|
/**
|
|
6583
6654
|
* Map canonical camelCase event names to Reasonix PascalCase.
|
|
6584
6655
|
* Reasonix explicitly mirrors Claude Code's hooks model, so it reuses the same
|
|
6585
|
-
* PascalCase names for the
|
|
6656
|
+
* PascalCase names for the events rulesync maps.
|
|
6586
6657
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
6587
6658
|
*/
|
|
6588
6659
|
const CANONICAL_TO_REASONIX_EVENT_NAMES = {
|
|
6589
6660
|
preToolUse: "PreToolUse",
|
|
6590
6661
|
postToolUse: "PostToolUse",
|
|
6591
6662
|
beforeSubmitPrompt: "UserPromptSubmit",
|
|
6592
|
-
stop: "Stop"
|
|
6663
|
+
stop: "Stop",
|
|
6664
|
+
sessionStart: "SessionStart",
|
|
6665
|
+
sessionEnd: "SessionEnd",
|
|
6666
|
+
subagentStop: "SubagentStop",
|
|
6667
|
+
postModelInvocation: "PostLLMCall"
|
|
6593
6668
|
};
|
|
6594
6669
|
/**
|
|
6595
6670
|
* Map Reasonix PascalCase event names to canonical camelCase.
|
|
@@ -6638,6 +6713,21 @@ function applyCommandPrefix({ def, converterConfig }) {
|
|
|
6638
6713
|
return converterConfig.projectDirVar !== "" && typeof trimmedCommand === "string" && !trimmedCommand.startsWith("$") && (!converterConfig.prefixDotRelativeCommandsOnly || trimmedCommand.startsWith(".")) && typeof trimmedCommand === "string" ? `"${converterConfig.projectDirVar}"/${trimmedCommand.replace(/^\.\//, "")}` : def.command;
|
|
6639
6714
|
}
|
|
6640
6715
|
/**
|
|
6716
|
+
* Emit the configured boolean passthrough fields on the tool side, mapping each
|
|
6717
|
+
* canonical field name to its (possibly renamed) tool field name. Only boolean
|
|
6718
|
+
* values are carried through.
|
|
6719
|
+
*/
|
|
6720
|
+
function emitBooleanPassthroughFields({ def, converterConfig }) {
|
|
6721
|
+
return Object.fromEntries((converterConfig.booleanPassthroughFields ?? []).filter(({ canonical }) => typeof def[canonical] === "boolean").map(({ canonical, tool }) => [tool, def[canonical]]));
|
|
6722
|
+
}
|
|
6723
|
+
/**
|
|
6724
|
+
* Import the configured boolean passthrough fields back into canonical fields,
|
|
6725
|
+
* reversing {@link emitBooleanPassthroughFields}. Only boolean values are read.
|
|
6726
|
+
*/
|
|
6727
|
+
function importBooleanPassthroughFields({ h, converterConfig }) {
|
|
6728
|
+
return Object.fromEntries((converterConfig.booleanPassthroughFields ?? []).filter(({ tool }) => typeof h[tool] === "boolean").map(({ canonical, tool }) => [canonical, h[tool]]));
|
|
6729
|
+
}
|
|
6730
|
+
/**
|
|
6641
6731
|
* Convert the definitions of a single matcher group into tool hook entries,
|
|
6642
6732
|
* honoring supported hook types and passthrough fields.
|
|
6643
6733
|
*/
|
|
@@ -6651,6 +6741,10 @@ function buildToolHooks({ defs, converterConfig }) {
|
|
|
6651
6741
|
converterConfig
|
|
6652
6742
|
});
|
|
6653
6743
|
hooks.push({
|
|
6744
|
+
...emitBooleanPassthroughFields({
|
|
6745
|
+
def,
|
|
6746
|
+
converterConfig
|
|
6747
|
+
}),
|
|
6654
6748
|
type: hookType,
|
|
6655
6749
|
...command !== void 0 && command !== null && { command },
|
|
6656
6750
|
...def.timeout !== void 0 && def.timeout !== null && { timeout: def.timeout },
|
|
@@ -6738,6 +6832,10 @@ function toolHookToCanonical({ h, rawEntry, converterConfig }) {
|
|
|
6738
6832
|
...prompt !== void 0 && prompt !== null && { prompt },
|
|
6739
6833
|
...converterConfig.passthroughFields?.includes("name") && typeof h.name === "string" && { name: h.name },
|
|
6740
6834
|
...converterConfig.passthroughFields?.includes("description") && typeof h.description === "string" && { description: h.description },
|
|
6835
|
+
...importBooleanPassthroughFields({
|
|
6836
|
+
h,
|
|
6837
|
+
converterConfig
|
|
6838
|
+
}),
|
|
6741
6839
|
...rawEntry.matcher !== void 0 && rawEntry.matcher !== null && rawEntry.matcher !== "" && { matcher: rawEntry.matcher }
|
|
6742
6840
|
};
|
|
6743
6841
|
}
|
|
@@ -7032,7 +7130,7 @@ function combineAugmentSettings(base, local) {
|
|
|
7032
7130
|
const baseValue = result[key];
|
|
7033
7131
|
if (AUGMENTCODE_REPLACE_KEYS.has(key)) result[key] = localValue;
|
|
7034
7132
|
else if (Array.isArray(localValue) && Array.isArray(baseValue)) result[key] = [...localValue, ...baseValue];
|
|
7035
|
-
else if (isPlainObject(localValue) && isPlainObject(baseValue)) result[key] = combineAugmentSettings(baseValue, localValue);
|
|
7133
|
+
else if (isPlainObject$1(localValue) && isPlainObject$1(baseValue)) result[key] = combineAugmentSettings(baseValue, localValue);
|
|
7036
7134
|
else result[key] = localValue;
|
|
7037
7135
|
}
|
|
7038
7136
|
return result;
|
|
@@ -7076,14 +7174,14 @@ async function readAugmentcodeSettingsWithLocalOverlay({ outputRoot, relativeDir
|
|
|
7076
7174
|
} catch (error) {
|
|
7077
7175
|
throw new Error(`Failed to parse AugmentCode settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
7078
7176
|
}
|
|
7079
|
-
if (!isPlainObject(localParsed)) throw new Error(`Failed to parse AugmentCode settings at ${configPath}: expected a JSON object`);
|
|
7177
|
+
if (!isPlainObject$1(localParsed)) throw new Error(`Failed to parse AugmentCode settings at ${configPath}: expected a JSON object`);
|
|
7080
7178
|
let baseParsed;
|
|
7081
7179
|
try {
|
|
7082
7180
|
baseParsed = JSON.parse(baseContent);
|
|
7083
7181
|
} catch {
|
|
7084
7182
|
return baseContent;
|
|
7085
7183
|
}
|
|
7086
|
-
const merged = combineAugmentSettings(isPlainObject(baseParsed) ? baseParsed : {}, localParsed);
|
|
7184
|
+
const merged = combineAugmentSettings(isPlainObject$1(baseParsed) ? baseParsed : {}, localParsed);
|
|
7087
7185
|
return JSON.stringify(merged, null, 2);
|
|
7088
7186
|
}
|
|
7089
7187
|
//#endregion
|
|
@@ -8668,7 +8766,14 @@ const JUNIE_CONVERTER_CONFIG = {
|
|
|
8668
8766
|
toolToCanonicalEventNames: JUNIE_TO_CANONICAL_EVENT_NAMES,
|
|
8669
8767
|
projectDirVar: "",
|
|
8670
8768
|
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
8671
|
-
noMatcherEvents: /* @__PURE__ */ new Set(["beforeSubmitPrompt", "stop"])
|
|
8769
|
+
noMatcherEvents: /* @__PURE__ */ new Set(["beforeSubmitPrompt", "stop"]),
|
|
8770
|
+
booleanPassthroughFields: [{
|
|
8771
|
+
canonical: "failClosed",
|
|
8772
|
+
tool: "blockOnError"
|
|
8773
|
+
}, {
|
|
8774
|
+
canonical: "async",
|
|
8775
|
+
tool: "async"
|
|
8776
|
+
}]
|
|
8672
8777
|
};
|
|
8673
8778
|
var JunieHooks = class JunieHooks extends ToolHooks {
|
|
8674
8779
|
constructor(params) {
|
|
@@ -9704,8 +9809,9 @@ function reasonixHooksToCanonical(hooks) {
|
|
|
9704
9809
|
* Reasonix hooks live in a Claude-Code-style but standalone JSON file —
|
|
9705
9810
|
* `.reasonix/settings.json` (project) or `~/.reasonix/settings.json`
|
|
9706
9811
|
* (global) — separate from the `[permissions]`/`[[plugins]]` TOML config.
|
|
9707
|
-
*
|
|
9708
|
-
* PreToolUse/PostToolUse/UserPromptSubmit/Stop
|
|
9812
|
+
* The eight upstream events with a clean canonical equivalent are mapped:
|
|
9813
|
+
* PreToolUse/PostToolUse/UserPromptSubmit/Stop plus SessionStart/SessionEnd/
|
|
9814
|
+
* SubagentStop/PostLLMCall (see REASONIX_HOOK_EVENTS).
|
|
9709
9815
|
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/DESKTOP_HOOKS.zh-CN.md
|
|
9710
9816
|
*/
|
|
9711
9817
|
var ReasonixHooks = class ReasonixHooks extends ToolHooks {
|
|
@@ -11784,7 +11890,7 @@ function parseAmpSettingsJsonc(fileContent) {
|
|
|
11784
11890
|
const details = errors.map((error) => `${printParseErrorCode(error.error)} at offset ${error.offset}`).join(", ");
|
|
11785
11891
|
throw new Error(`Failed to parse Amp settings: ${details}`);
|
|
11786
11892
|
}
|
|
11787
|
-
if (!isPlainObject(parsed)) throw new Error("Amp settings must be a JSON object");
|
|
11893
|
+
if (!isPlainObject$1(parsed)) throw new Error("Amp settings must be a JSON object");
|
|
11788
11894
|
return parsed;
|
|
11789
11895
|
}
|
|
11790
11896
|
function filterMcpServers(mcpServers) {
|
|
@@ -12106,7 +12212,7 @@ function parseAugmentcodeSettings(fileContent, relativeDirPath, relativeFilePath
|
|
|
12106
12212
|
} catch (error) {
|
|
12107
12213
|
throw new Error(`Failed to parse AugmentCode settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
12108
12214
|
}
|
|
12109
|
-
if (!isPlainObject(parsed)) throw new Error(`Failed to parse AugmentCode settings at ${configPath}: expected a JSON object`);
|
|
12215
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse AugmentCode settings at ${configPath}: expected a JSON object`);
|
|
12110
12216
|
return parsed;
|
|
12111
12217
|
}
|
|
12112
12218
|
/**
|
|
@@ -12332,7 +12438,7 @@ function parseClineSettings(fileContent, relativeDirPath, relativeFilePath) {
|
|
|
12332
12438
|
} catch (error) {
|
|
12333
12439
|
throw new Error(`Failed to parse Cline MCP settings at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
12334
12440
|
}
|
|
12335
|
-
if (!isPlainObject(parsed)) throw new Error(`Failed to parse Cline MCP settings at ${configPath}: expected a JSON object`);
|
|
12441
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Cline MCP settings at ${configPath}: expected a JSON object`);
|
|
12336
12442
|
return parsed;
|
|
12337
12443
|
}
|
|
12338
12444
|
/**
|
|
@@ -12603,7 +12709,7 @@ var CodexcliMcp = class CodexcliMcp extends ToolMcp {
|
|
|
12603
12709
|
for (const [key, value] of Object.entries(obj)) {
|
|
12604
12710
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
12605
12711
|
if (value === null) continue;
|
|
12606
|
-
if (isPlainObject(value)) {
|
|
12712
|
+
if (isPlainObject$1(value)) {
|
|
12607
12713
|
const cleaned = this.removeEmptyEntries(value, depth + 1);
|
|
12608
12714
|
if (Object.keys(cleaned).length === 0) continue;
|
|
12609
12715
|
filtered[key] = cleaned;
|
|
@@ -13244,12 +13350,12 @@ function parseGooseConfig(fileContent, relativeDirPath, relativeFilePath) {
|
|
|
13244
13350
|
const configPath = join(relativeDirPath, relativeFilePath);
|
|
13245
13351
|
let parsed;
|
|
13246
13352
|
try {
|
|
13247
|
-
parsed =
|
|
13353
|
+
parsed = loadYaml(fileContent);
|
|
13248
13354
|
} catch (error) {
|
|
13249
13355
|
throw new Error(`Failed to parse Goose config at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
13250
13356
|
}
|
|
13251
13357
|
if (parsed === void 0 || parsed === null) return {};
|
|
13252
|
-
if (!isPlainObject(parsed)) throw new Error(`Failed to parse Goose config at ${configPath}: expected a YAML mapping`);
|
|
13358
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Goose config at ${configPath}: expected a YAML mapping`);
|
|
13253
13359
|
return parsed;
|
|
13254
13360
|
}
|
|
13255
13361
|
/**
|
|
@@ -13294,7 +13400,7 @@ function applyGooseStdioFields(ext, config) {
|
|
|
13294
13400
|
ext.cmd = command;
|
|
13295
13401
|
if (isStringArray(config.args)) ext.args = config.args;
|
|
13296
13402
|
}
|
|
13297
|
-
if (isPlainObject(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
13403
|
+
if (isPlainObject$1(config.env)) ext.envs = omitPrototypePollutionKeys(config.env);
|
|
13298
13404
|
}
|
|
13299
13405
|
/**
|
|
13300
13406
|
* Converts a single rulesync canonical MCP server into a Goose `extensions:` entry.
|
|
@@ -13309,7 +13415,7 @@ function convertServerToGooseExtension(name, config) {
|
|
|
13309
13415
|
if (gooseType === "stdio") applyGooseStdioFields(ext, config);
|
|
13310
13416
|
else if (gooseType === "sse" || gooseType === "streamable_http") {
|
|
13311
13417
|
if (url !== void 0) ext.uri = url;
|
|
13312
|
-
if (isPlainObject(config.headers)) ext.headers = omitPrototypePollutionKeys(config.headers);
|
|
13418
|
+
if (isPlainObject$1(config.headers)) ext.headers = omitPrototypePollutionKeys(config.headers);
|
|
13313
13419
|
}
|
|
13314
13420
|
ext.enabled = config.disabled !== true;
|
|
13315
13421
|
const timeout = resolveGooseTimeout(config);
|
|
@@ -13351,9 +13457,9 @@ function convertFromGooseFormat(extensions) {
|
|
|
13351
13457
|
else if (type === "stdio") server.type = "stdio";
|
|
13352
13458
|
if (typeof ext.cmd === "string") server.command = ext.cmd;
|
|
13353
13459
|
if (isStringArray(ext.args)) server.args = ext.args;
|
|
13354
|
-
if (isPlainObject(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
13460
|
+
if (isPlainObject$1(ext.envs)) server.env = omitPrototypePollutionKeys(ext.envs);
|
|
13355
13461
|
if (typeof ext.uri === "string") server.url = ext.uri;
|
|
13356
|
-
if (isPlainObject(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
|
|
13462
|
+
if (isPlainObject$1(ext.headers)) server.headers = omitPrototypePollutionKeys(ext.headers);
|
|
13357
13463
|
if (ext.enabled === false) server.disabled = true;
|
|
13358
13464
|
if (typeof ext.timeout === "number") server.timeout = ext.timeout;
|
|
13359
13465
|
result[name] = server;
|
|
@@ -13376,7 +13482,7 @@ function buildGoosePluginStdioServer(config) {
|
|
|
13376
13482
|
server.command = command;
|
|
13377
13483
|
if (isStringArray(config.args)) server.args = config.args;
|
|
13378
13484
|
}
|
|
13379
|
-
if (isPlainObject(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13485
|
+
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13380
13486
|
if (typeof config.cwd === "string") server.cwd = config.cwd;
|
|
13381
13487
|
return server;
|
|
13382
13488
|
}
|
|
@@ -13676,7 +13782,7 @@ var GrokcliMcp = class GrokcliMcp extends ToolMcp {
|
|
|
13676
13782
|
for (const [key, value] of Object.entries(obj)) {
|
|
13677
13783
|
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
13678
13784
|
if (value === null) continue;
|
|
13679
|
-
if (isPlainObject(value)) {
|
|
13785
|
+
if (isPlainObject$1(value)) {
|
|
13680
13786
|
const cleaned = this.removeEmptyEntries(value, depth + 1);
|
|
13681
13787
|
if (Object.keys(cleaned).length === 0) continue;
|
|
13682
13788
|
filtered[key] = cleaned;
|
|
@@ -13738,10 +13844,10 @@ function convertServerToHermes(config) {
|
|
|
13738
13844
|
out.command = command;
|
|
13739
13845
|
if (isStringArray(config.args)) out.args = config.args;
|
|
13740
13846
|
}
|
|
13741
|
-
if (isPlainObject(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
13847
|
+
if (isPlainObject$1(config.env)) out.env = omitPrototypePollutionKeys(config.env);
|
|
13742
13848
|
} else if (url !== void 0) {
|
|
13743
13849
|
out.url = url;
|
|
13744
|
-
if (isPlainObject(config.headers)) out.headers = omitPrototypePollutionKeys(config.headers);
|
|
13850
|
+
if (isPlainObject$1(config.headers)) out.headers = omitPrototypePollutionKeys(config.headers);
|
|
13745
13851
|
}
|
|
13746
13852
|
if (config.disabled === true) out.enabled = false;
|
|
13747
13853
|
const timeout = resolveHermesTimeout(config);
|
|
@@ -13786,9 +13892,9 @@ function convertFromHermesFormat(mcpServers) {
|
|
|
13786
13892
|
const server = {};
|
|
13787
13893
|
if (typeof config.command === "string") server.command = config.command;
|
|
13788
13894
|
if (isStringArray(config.args)) server.args = config.args;
|
|
13789
|
-
if (isPlainObject(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13895
|
+
if (isPlainObject$1(config.env)) server.env = omitPrototypePollutionKeys(config.env);
|
|
13790
13896
|
if (typeof config.url === "string") server.url = config.url;
|
|
13791
|
-
if (isPlainObject(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
13897
|
+
if (isPlainObject$1(config.headers)) server.headers = omitPrototypePollutionKeys(config.headers);
|
|
13792
13898
|
if (config.enabled === false) server.disabled = true;
|
|
13793
13899
|
if (typeof config.timeout === "number") server.networkTimeout = config.timeout;
|
|
13794
13900
|
if (isRecord(config.tools)) {
|
|
@@ -14725,7 +14831,9 @@ const REASONIX_PLUGIN_FIELDS = [
|
|
|
14725
14831
|
"env",
|
|
14726
14832
|
"url",
|
|
14727
14833
|
"headers",
|
|
14728
|
-
"trusted_read_only_tools"
|
|
14834
|
+
"trusted_read_only_tools",
|
|
14835
|
+
"call_timeout_seconds",
|
|
14836
|
+
"tool_timeout_seconds"
|
|
14729
14837
|
];
|
|
14730
14838
|
var ReasonixMcp = class ReasonixMcp extends ToolMcp {
|
|
14731
14839
|
toml;
|
|
@@ -14957,7 +15065,7 @@ function parseRovodevMcpJson(fileContent, relativeDirPath, relativeFilePath) {
|
|
|
14957
15065
|
} catch (error) {
|
|
14958
15066
|
throw new Error(`Failed to parse Rovodev MCP config at ${configPath}: ${formatError(error)}`, { cause: error });
|
|
14959
15067
|
}
|
|
14960
|
-
if (!isPlainObject(parsed)) throw new Error(`Failed to parse Rovodev MCP config at ${configPath}: expected a JSON object`);
|
|
15068
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Rovodev MCP config at ${configPath}: expected a JSON object`);
|
|
14961
15069
|
return parsed;
|
|
14962
15070
|
}
|
|
14963
15071
|
/**
|
|
@@ -16228,15 +16336,123 @@ const AntigravityCliPermissionsOverrideSchema = z.looseObject({
|
|
|
16228
16336
|
enableTerminalSandbox: z.optional(z.boolean())
|
|
16229
16337
|
});
|
|
16230
16338
|
/**
|
|
16339
|
+
* Tool-scoped override block for AugmentCode. AugmentCode's `toolPermissions[]`
|
|
16340
|
+
* array supports "custom policy" entries the canonical allow/ask/deny model
|
|
16341
|
+
* cannot express: `permission.type` of `webhook-policy` / `script-policy`
|
|
16342
|
+
* (delegating the decision to a `webhookUrl` / `script`) and an `eventType` of
|
|
16343
|
+
* `tool-response` (a post-execution check rather than the default pre-execution
|
|
16344
|
+
* `tool-call`). These are authored here as verbatim `toolPermissions` entries
|
|
16345
|
+
* and prepended — ahead of the canonical-generated basic rules — into the shared
|
|
16346
|
+
* `.augment/settings.json`, so a webhook/script gate or tool-response check is
|
|
16347
|
+
* never shadowed by a regenerated allow/deny/ask entry under AugmentCode's
|
|
16348
|
+
* first-match-wins evaluation. The shared `permission` block continues to drive
|
|
16349
|
+
* the basic `allow` / `deny` / `ask-user` entries. Kept `looseObject` (verbatim
|
|
16350
|
+
* passthrough) so `shellInputRegex`, `eventType`, `webhookUrl`, `script`, and
|
|
16351
|
+
* any future policy field survive untouched. Both project and global scope are
|
|
16352
|
+
* supported.
|
|
16353
|
+
*
|
|
16354
|
+
* @example
|
|
16355
|
+
* { "toolPermissions": [
|
|
16356
|
+
* { "toolName": "github-api",
|
|
16357
|
+
* "permission": { "type": "webhook-policy", "webhookUrl": "https://api.example.com/validate" } },
|
|
16358
|
+
* { "toolName": "view", "eventType": "tool-response", "permission": { "type": "allow" } } ] }
|
|
16359
|
+
*/
|
|
16360
|
+
const AugmentcodePermissionsOverrideSchema = z.looseObject({ toolPermissions: z.optional(z.array(z.looseObject({
|
|
16361
|
+
toolName: z.string(),
|
|
16362
|
+
permission: z.looseObject({ type: z.string() })
|
|
16363
|
+
}))) });
|
|
16364
|
+
/**
|
|
16365
|
+
* Tool-scoped override block for Kiro. Kiro's agent config (`.kiro/agents/<name>.json`)
|
|
16366
|
+
* exposes per-tool `toolsSettings` knobs with no canonical allow/ask/deny
|
|
16367
|
+
* category: the shell auto-trust flags `shell.autoAllowReadonly` /
|
|
16368
|
+
* `shell.denyByDefault`, the `aws` built-in tool's `allowedServices` /
|
|
16369
|
+
* `deniedServices` (+ `autoAllowReadonly`), and the `web_fetch` domain trust
|
|
16370
|
+
* arrays `trusted` / `blocked` (regex host patterns; documented for `web_fetch`
|
|
16371
|
+
* only — `web_search` has no such surface). Fields placed here are deep-merged
|
|
16372
|
+
* (per `toolsSettings` key, override wins at the leaf) into the shared agent
|
|
16373
|
+
* config, while the canonical `permission` block continues to drive
|
|
16374
|
+
* `shell.{allowed,denied}Commands`, `read`/`write`/`grep`/`glob` paths, and the
|
|
16375
|
+
* `web_fetch`/`web_search` `allowedTools` toggles. Kept `looseObject` at every
|
|
16376
|
+
* level (verbatim passthrough) so future Kiro `toolsSettings` fields survive.
|
|
16377
|
+
*
|
|
16378
|
+
* Kiro's MCP `autoApprove` / `disabledTools` lists are intentionally NOT modeled
|
|
16379
|
+
* here: they live in a SEPARATE file (`.kiro/settings/mcp.json`, under
|
|
16380
|
+
* `mcpServers.<name>`), not the agent config this permissions translator writes,
|
|
16381
|
+
* and reconciling them with the canonical `mcp__*` model is a distinct design
|
|
16382
|
+
* question left out of scope.
|
|
16383
|
+
*
|
|
16384
|
+
* @example
|
|
16385
|
+
* { "toolsSettings": { "shell": { "autoAllowReadonly": true },
|
|
16386
|
+
* "aws": { "allowedServices": ["s3"], "deniedServices": ["eks"] },
|
|
16387
|
+
* "web_fetch": { "trusted": [".*github\\.com.*"] } } }
|
|
16388
|
+
*/
|
|
16389
|
+
const KiroPermissionsOverrideSchema = z.looseObject({ toolsSettings: z.optional(z.looseObject({
|
|
16390
|
+
shell: z.optional(z.looseObject({
|
|
16391
|
+
autoAllowReadonly: z.optional(z.boolean()),
|
|
16392
|
+
denyByDefault: z.optional(z.boolean())
|
|
16393
|
+
})),
|
|
16394
|
+
aws: z.optional(z.looseObject({
|
|
16395
|
+
allowedServices: z.optional(z.array(z.string())),
|
|
16396
|
+
deniedServices: z.optional(z.array(z.string())),
|
|
16397
|
+
autoAllowReadonly: z.optional(z.boolean())
|
|
16398
|
+
})),
|
|
16399
|
+
web_fetch: z.optional(z.looseObject({
|
|
16400
|
+
trusted: z.optional(z.array(z.string())),
|
|
16401
|
+
blocked: z.optional(z.array(z.string()))
|
|
16402
|
+
}))
|
|
16403
|
+
})) });
|
|
16404
|
+
/**
|
|
16405
|
+
* Codex CLI-scoped permission override.
|
|
16406
|
+
*
|
|
16407
|
+
* Codex CLI's permission surface is richer than the canonical allow/ask/deny
|
|
16408
|
+
* model: its approval workflow, classic sandbox system, and per-app tool gating
|
|
16409
|
+
* have no canonical category. Author them through a tool-scoped `codexcli`
|
|
16410
|
+
* override whose fields are written verbatim as top-level `.codex/config.toml`
|
|
16411
|
+
* keys (the override wins per key; existing sibling keys the user set directly
|
|
16412
|
+
* are preserved):
|
|
16413
|
+
* - `approval_policy` — `untrusted` | `on-request` | `never`, or a
|
|
16414
|
+
* `{ granular = { … } }` table (kept verbatim; the granular schema has
|
|
16415
|
+
* required fields that are brittle to model as typed keys).
|
|
16416
|
+
* - `sandbox_mode` — `read-only` | `workspace-write` | `danger-full-access`,
|
|
16417
|
+
* with the sibling `sandbox_workspace_write` table (`network_access`,
|
|
16418
|
+
* `writable_roots`, …).
|
|
16419
|
+
* - `apps` — per-app tool gating (`apps.<id>.tools.<tool>.approval_mode` /
|
|
16420
|
+
* `.enabled`, `apps.<id>.default_tools_approval_mode`).
|
|
16421
|
+
* - `approvals_reviewer` — the reviewer-approval surface.
|
|
16422
|
+
*
|
|
16423
|
+
* Two surfaces are deliberately NOT authorable here so the override can never
|
|
16424
|
+
* clobber a feature-owned key: `mcp_servers.*` per-MCP gating is owned by the
|
|
16425
|
+
* MCP feature (`codexcli-mcp.ts` already writes the `mcp_servers` tables in the
|
|
16426
|
+
* same `config.toml`), and `permissions` / `default_permissions` are owned by
|
|
16427
|
+
* the canonical model. Any such key placed in the override is skipped with a
|
|
16428
|
+
* warning. Kept `looseObject` (verbatim passthrough) so future top-level Codex
|
|
16429
|
+
* config keys can be authored without Rulesync modeling each one.
|
|
16430
|
+
*
|
|
16431
|
+
* @see https://developers.openai.com/codex/config-reference
|
|
16432
|
+
* @see https://developers.openai.com/codex/permissions
|
|
16433
|
+
*
|
|
16434
|
+
* @example
|
|
16435
|
+
* { "approval_policy": "on-request", "sandbox_mode": "workspace-write",
|
|
16436
|
+
* "sandbox_workspace_write": { "network_access": true } }
|
|
16437
|
+
*/
|
|
16438
|
+
const CodexcliPermissionsOverrideSchema = z.looseObject({
|
|
16439
|
+
approval_policy: z.optional(z.union([z.string(), z.looseObject({})])),
|
|
16440
|
+
sandbox_mode: z.optional(z.string()),
|
|
16441
|
+
sandbox_workspace_write: z.optional(z.looseObject({})),
|
|
16442
|
+
apps: z.optional(z.looseObject({})),
|
|
16443
|
+
approvals_reviewer: z.optional(z.union([z.string(), z.looseObject({})]))
|
|
16444
|
+
});
|
|
16445
|
+
/**
|
|
16231
16446
|
* Permissions configuration.
|
|
16232
16447
|
* Keys are tool category names (e.g., "bash", "edit", "read", "webfetch").
|
|
16233
16448
|
* Values are pattern-to-action mappings for that tool category.
|
|
16234
16449
|
*
|
|
16235
16450
|
* The optional `opencode`/`hermes`/`cline`/`kilo`/`claudecode`/`vibe`/`cursor`/
|
|
16236
16451
|
* `qwencode`/`reasonix`/`factorydroid`/`warp`/`junie`/`takt`/`amp`/
|
|
16237
|
-
* `antigravity-cli` keys are tool-scoped
|
|
16238
|
-
* respective translator (see the matching
|
|
16239
|
-
* other tool reads the shared `permission`
|
|
16452
|
+
* `antigravity-cli`/`augmentcode`/`kiro`/`codexcli` keys are tool-scoped
|
|
16453
|
+
* overrides consumed only by their respective translator (see the matching
|
|
16454
|
+
* `*PermissionsOverrideSchema`); every other tool reads the shared `permission`
|
|
16455
|
+
* block and ignores them.
|
|
16240
16456
|
*
|
|
16241
16457
|
* @example
|
|
16242
16458
|
* {
|
|
@@ -16260,7 +16476,10 @@ const PermissionsConfigSchema = z.looseObject({
|
|
|
16260
16476
|
junie: z.optional(JuniePermissionsOverrideSchema),
|
|
16261
16477
|
takt: z.optional(TaktPermissionsOverrideSchema),
|
|
16262
16478
|
amp: z.optional(AmpPermissionsOverrideSchema),
|
|
16263
|
-
"antigravity-cli": z.optional(AntigravityCliPermissionsOverrideSchema)
|
|
16479
|
+
"antigravity-cli": z.optional(AntigravityCliPermissionsOverrideSchema),
|
|
16480
|
+
augmentcode: z.optional(AugmentcodePermissionsOverrideSchema),
|
|
16481
|
+
kiro: z.optional(KiroPermissionsOverrideSchema),
|
|
16482
|
+
codexcli: z.optional(CodexcliPermissionsOverrideSchema)
|
|
16264
16483
|
});
|
|
16265
16484
|
/**
|
|
16266
16485
|
* Full permissions file schema including optional $schema field.
|
|
@@ -16412,7 +16631,7 @@ function parseAmpSettings(fileContent) {
|
|
|
16412
16631
|
const details = errors.map((error) => `${printParseErrorCode(error.error)} at offset ${error.offset}`).join(", ");
|
|
16413
16632
|
throw new Error(`Failed to parse Amp settings: ${details}`);
|
|
16414
16633
|
}
|
|
16415
|
-
if (!isPlainObject(parsed)) throw new Error("Amp settings must be a JSON object");
|
|
16634
|
+
if (!isPlainObject$1(parsed)) throw new Error("Amp settings must be a JSON object");
|
|
16416
16635
|
return parsed;
|
|
16417
16636
|
}
|
|
16418
16637
|
function toDisableList(value) {
|
|
@@ -16439,7 +16658,7 @@ function toPermissionsList(value) {
|
|
|
16439
16658
|
if (!Array.isArray(value)) return [];
|
|
16440
16659
|
const entries = [];
|
|
16441
16660
|
for (const raw of value) {
|
|
16442
|
-
if (!isPlainObject(raw)) continue;
|
|
16661
|
+
if (!isPlainObject$1(raw)) continue;
|
|
16443
16662
|
const { tool, action } = raw;
|
|
16444
16663
|
if (typeof tool !== "string" || typeof action !== "string") continue;
|
|
16445
16664
|
if (action !== "allow" && action !== "reject" && action !== "ask" && action !== "delegate") continue;
|
|
@@ -16447,7 +16666,7 @@ function toPermissionsList(value) {
|
|
|
16447
16666
|
...raw,
|
|
16448
16667
|
tool,
|
|
16449
16668
|
action,
|
|
16450
|
-
...isPlainObject(raw.matches) ? { matches: raw.matches } : {}
|
|
16669
|
+
...isPlainObject$1(raw.matches) ? { matches: raw.matches } : {}
|
|
16451
16670
|
});
|
|
16452
16671
|
}
|
|
16453
16672
|
return entries;
|
|
@@ -17307,6 +17526,55 @@ function isSpecialEntry(entry) {
|
|
|
17307
17526
|
return false;
|
|
17308
17527
|
}
|
|
17309
17528
|
/**
|
|
17529
|
+
* A composite identity for a special entry, used to de-duplicate authored
|
|
17530
|
+
* (`augmentcode` override) and preserved (existing-file) special entries so the
|
|
17531
|
+
* same policy is not emitted twice into `toolPermissions`. Every field that can
|
|
17532
|
+
* distinguish two special entries is included.
|
|
17533
|
+
*/
|
|
17534
|
+
function specialEntryKey(entry) {
|
|
17535
|
+
return JSON.stringify([
|
|
17536
|
+
entry.toolName,
|
|
17537
|
+
entry.shellInputRegex ?? "",
|
|
17538
|
+
entry.eventType ?? "",
|
|
17539
|
+
entry.permission.type,
|
|
17540
|
+
entry.permission.webhookUrl ?? "",
|
|
17541
|
+
entry.permission.script ?? ""
|
|
17542
|
+
]);
|
|
17543
|
+
}
|
|
17544
|
+
/**
|
|
17545
|
+
* Stable de-duplication of special entries by {@link specialEntryKey}, keeping
|
|
17546
|
+
* the first occurrence (authored entries lead, so an authored policy wins over
|
|
17547
|
+
* an identical preserved one).
|
|
17548
|
+
*/
|
|
17549
|
+
function dedupeSpecialEntries(entries) {
|
|
17550
|
+
const seen = /* @__PURE__ */ new Set();
|
|
17551
|
+
const out = [];
|
|
17552
|
+
for (const entry of entries) {
|
|
17553
|
+
const key = specialEntryKey(entry);
|
|
17554
|
+
if (seen.has(key)) continue;
|
|
17555
|
+
seen.add(key);
|
|
17556
|
+
out.push(entry);
|
|
17557
|
+
}
|
|
17558
|
+
return out;
|
|
17559
|
+
}
|
|
17560
|
+
/**
|
|
17561
|
+
* Validate/normalize the `augmentcode` override's `toolPermissions` array into
|
|
17562
|
+
* typed `AugmentToolPermission` entries, dropping any malformed item (with a
|
|
17563
|
+
* warning so a typo is not lost silently). The override schema is intentionally
|
|
17564
|
+
* loose (verbatim passthrough), so entries are re-parsed through the full entry
|
|
17565
|
+
* schema here to guarantee shape before they are written into the shared
|
|
17566
|
+
* settings file.
|
|
17567
|
+
*/
|
|
17568
|
+
function coerceAuthoredEntries(entries, logger) {
|
|
17569
|
+
const out = [];
|
|
17570
|
+
for (const item of entries) {
|
|
17571
|
+
const parsed = AugmentToolPermissionSchema.safeParse(item);
|
|
17572
|
+
if (parsed.success) out.push(parsed.data);
|
|
17573
|
+
else logger?.warn(`AugmentCode permissions: dropping malformed 'augmentcode.toolPermissions' override entry: ${formatError(parsed.error)}.`);
|
|
17574
|
+
}
|
|
17575
|
+
return out;
|
|
17576
|
+
}
|
|
17577
|
+
/**
|
|
17310
17578
|
* Convert a glob-like pattern into a regex string for AugmentCode's `shellInputRegex`.
|
|
17311
17579
|
* Maps glob `*` to `.*`, `?` to `.`, escapes other regex metacharacters, and anchors at both ends.
|
|
17312
17580
|
*/
|
|
@@ -17437,12 +17705,18 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
17437
17705
|
} catch (error) {
|
|
17438
17706
|
throw new Error(`Failed to parse existing AugmentCode settings at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
17439
17707
|
}
|
|
17708
|
+
const config = rulesyncPermissions.getJson();
|
|
17440
17709
|
const generated = convertRulesyncToAugmentEntries({
|
|
17441
|
-
config
|
|
17710
|
+
config,
|
|
17442
17711
|
logger
|
|
17443
17712
|
});
|
|
17444
17713
|
const existingEntries = settings.toolPermissions ?? [];
|
|
17445
|
-
const
|
|
17714
|
+
const override = config.augmentcode;
|
|
17715
|
+
const authoredEntries = override?.toolPermissions ? coerceAuthoredEntries(override.toolPermissions, logger) : [];
|
|
17716
|
+
const authoredSpecials = authoredEntries.filter((entry) => isSpecialEntry(entry));
|
|
17717
|
+
const authoredBasics = authoredEntries.filter((entry) => !isSpecialEntry(entry));
|
|
17718
|
+
const preservedSpecials = override?.toolPermissions ? [] : existingEntries.filter((entry) => isSpecialEntry(entry));
|
|
17719
|
+
const specialEntries = dedupeSpecialEntries([...authoredSpecials, ...preservedSpecials]);
|
|
17446
17720
|
const basicExistingEntries = existingEntries.filter((entry) => !isSpecialEntry(entry));
|
|
17447
17721
|
const generatedKeys = new Set(generated.map((e) => `${e.toolName}|${e.shellInputRegex ?? ""}|${e.permission.type}`));
|
|
17448
17722
|
const preservedBasicEntries = basicExistingEntries.filter((entry) => {
|
|
@@ -17453,7 +17727,11 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
17453
17727
|
}
|
|
17454
17728
|
return false;
|
|
17455
17729
|
});
|
|
17456
|
-
const sortedBasic = sortAugmentEntries([
|
|
17730
|
+
const sortedBasic = sortAugmentEntries([
|
|
17731
|
+
...generated,
|
|
17732
|
+
...preservedBasicEntries,
|
|
17733
|
+
...authoredBasics
|
|
17734
|
+
]);
|
|
17457
17735
|
const merged = {
|
|
17458
17736
|
...settings,
|
|
17459
17737
|
toolPermissions: [...specialEntries, ...sortedBasic]
|
|
@@ -17477,11 +17755,14 @@ var AugmentcodePermissions = class AugmentcodePermissions extends ToolPermission
|
|
|
17477
17755
|
} catch (error) {
|
|
17478
17756
|
throw new Error(`Failed to parse AugmentCode permissions content in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
17479
17757
|
}
|
|
17480
|
-
const
|
|
17481
|
-
|
|
17758
|
+
const allEntries = settings.toolPermissions ?? [];
|
|
17759
|
+
const specialEntries = allEntries.filter((entry) => isSpecialEntry(entry));
|
|
17760
|
+
const result = { ...convertAugmentToRulesyncPermissions({
|
|
17761
|
+
entries: allEntries.filter((entry) => !isSpecialEntry(entry)),
|
|
17482
17762
|
logger: moduleLogger$1
|
|
17483
|
-
});
|
|
17484
|
-
|
|
17763
|
+
}) };
|
|
17764
|
+
if (specialEntries.length > 0) result.augmentcode = { toolPermissions: specialEntries };
|
|
17765
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
17485
17766
|
}
|
|
17486
17767
|
validate() {
|
|
17487
17768
|
try {
|
|
@@ -17615,10 +17896,7 @@ function convertAugmentToRulesyncPermissions({ entries, logger }) {
|
|
|
17615
17896
|
]);
|
|
17616
17897
|
const permission = {};
|
|
17617
17898
|
for (const entry of entries) {
|
|
17618
|
-
if (isSpecialEntry(entry))
|
|
17619
|
-
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.`);
|
|
17620
|
-
continue;
|
|
17621
|
-
}
|
|
17899
|
+
if (isSpecialEntry(entry)) continue;
|
|
17622
17900
|
const type = entry.permission.type;
|
|
17623
17901
|
if (!isBasicAugmentType(type)) continue;
|
|
17624
17902
|
const canonical = toCanonicalToolName$5(entry.toolName);
|
|
@@ -18050,6 +18328,13 @@ const WORKSPACE_WIDE_WRITE_PATTERNS = /* @__PURE__ */ new Set([
|
|
|
18050
18328
|
]);
|
|
18051
18329
|
const CODEX_MINIMAL_KEY = ":minimal";
|
|
18052
18330
|
const GLOBAL_WILDCARD_DOMAIN = "*";
|
|
18331
|
+
const CODEXCLI_OVERRIDE_KEYS = [
|
|
18332
|
+
"approval_policy",
|
|
18333
|
+
"sandbox_mode",
|
|
18334
|
+
"sandbox_workspace_write",
|
|
18335
|
+
"apps",
|
|
18336
|
+
"approvals_reviewer"
|
|
18337
|
+
];
|
|
18053
18338
|
var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
18054
18339
|
static getSettablePaths(_options = {}) {
|
|
18055
18340
|
return {
|
|
@@ -18091,6 +18376,11 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
|
18091
18376
|
});
|
|
18092
18377
|
parsed.permissions = permissionsTable;
|
|
18093
18378
|
parsed.default_permissions = RULESYNC_PROFILE_NAME;
|
|
18379
|
+
applyCodexcliOverride({
|
|
18380
|
+
parsed,
|
|
18381
|
+
override: rulesyncPermissions.getJson().codexcli,
|
|
18382
|
+
logger
|
|
18383
|
+
});
|
|
18094
18384
|
return new CodexcliPermissions({
|
|
18095
18385
|
outputRoot,
|
|
18096
18386
|
relativeDirPath: paths.relativeDirPath,
|
|
@@ -18115,7 +18405,12 @@ var CodexcliPermissions = class CodexcliPermissions extends ToolPermissions {
|
|
|
18115
18405
|
profile,
|
|
18116
18406
|
domainsHadUnknown
|
|
18117
18407
|
});
|
|
18118
|
-
|
|
18408
|
+
const override = extractCodexcliOverride(table);
|
|
18409
|
+
const result = Object.keys(override).length > 0 ? {
|
|
18410
|
+
...config,
|
|
18411
|
+
codexcli: override
|
|
18412
|
+
} : config;
|
|
18413
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
18119
18414
|
}
|
|
18120
18415
|
validate() {
|
|
18121
18416
|
return {
|
|
@@ -18307,6 +18602,30 @@ function toMutableTable(value) {
|
|
|
18307
18602
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
18308
18603
|
return { ...value };
|
|
18309
18604
|
}
|
|
18605
|
+
function isPlainObject(value) {
|
|
18606
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
18607
|
+
}
|
|
18608
|
+
function applyCodexcliOverride({ parsed, override, logger }) {
|
|
18609
|
+
if (!override) return;
|
|
18610
|
+
const allowed = new Set(CODEXCLI_OVERRIDE_KEYS);
|
|
18611
|
+
for (const [key, value] of Object.entries(override)) {
|
|
18612
|
+
if (!allowed.has(key)) {
|
|
18613
|
+
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.`);
|
|
18614
|
+
continue;
|
|
18615
|
+
}
|
|
18616
|
+
if (value === void 0) continue;
|
|
18617
|
+
const existing = parsed[key];
|
|
18618
|
+
parsed[key] = isPlainObject(existing) && isPlainObject(value) ? {
|
|
18619
|
+
...existing,
|
|
18620
|
+
...value
|
|
18621
|
+
} : value;
|
|
18622
|
+
}
|
|
18623
|
+
}
|
|
18624
|
+
function extractCodexcliOverride(table) {
|
|
18625
|
+
const override = {};
|
|
18626
|
+
for (const key of CODEXCLI_OVERRIDE_KEYS) if (table[key] !== void 0) override[key] = table[key];
|
|
18627
|
+
return override;
|
|
18628
|
+
}
|
|
18310
18629
|
function toFilesystemRecord(value) {
|
|
18311
18630
|
if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
18312
18631
|
const result = {};
|
|
@@ -19221,7 +19540,7 @@ var GoosePermissions = class GoosePermissions extends ToolPermissions {
|
|
|
19221
19540
|
const existingContent = await readFileContentOrNull(filePath) ?? "";
|
|
19222
19541
|
let parsed;
|
|
19223
19542
|
try {
|
|
19224
|
-
parsed = existingContent.trim() === "" ? {} :
|
|
19543
|
+
parsed = existingContent.trim() === "" ? {} : loadYaml(existingContent);
|
|
19225
19544
|
} catch (error) {
|
|
19226
19545
|
throw new Error(`Failed to parse existing Goose permission.yaml at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
19227
19546
|
}
|
|
@@ -19243,7 +19562,7 @@ var GoosePermissions = class GoosePermissions extends ToolPermissions {
|
|
|
19243
19562
|
let parsed;
|
|
19244
19563
|
try {
|
|
19245
19564
|
const content = this.getFileContent();
|
|
19246
|
-
parsed = content.trim() === "" ? {} :
|
|
19565
|
+
parsed = content.trim() === "" ? {} : loadYaml(content);
|
|
19247
19566
|
} catch (error) {
|
|
19248
19567
|
throw new Error(`Failed to parse Goose permissions content in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
19249
19568
|
}
|
|
@@ -20150,6 +20469,13 @@ const KiroAgentSchema = z.looseObject({
|
|
|
20150
20469
|
toolsSettings: z.optional(z.record(z.string(), z.unknown()))
|
|
20151
20470
|
});
|
|
20152
20471
|
const UnknownRecordSchema = z.record(z.string(), z.unknown());
|
|
20472
|
+
const CANONICAL_SHELL_KEYS = /* @__PURE__ */ new Set(["allowedCommands", "deniedCommands"]);
|
|
20473
|
+
const CANONICAL_TOOL_SETTINGS_KEYS = /* @__PURE__ */ new Set([
|
|
20474
|
+
"read",
|
|
20475
|
+
"write",
|
|
20476
|
+
"grep",
|
|
20477
|
+
"glob"
|
|
20478
|
+
]);
|
|
20153
20479
|
var KiroPermissions = class KiroPermissions extends ToolPermissions {
|
|
20154
20480
|
static getSettablePaths(_options = {}) {
|
|
20155
20481
|
return {
|
|
@@ -20213,7 +20539,10 @@ var KiroPermissions = class KiroPermissions extends ToolPermissions {
|
|
|
20213
20539
|
const allowedTools = new Set(parsed.allowedTools ?? []);
|
|
20214
20540
|
if (allowedTools.has("web_fetch")) permission.webfetch = { "*": "allow" };
|
|
20215
20541
|
if (allowedTools.has("web_search")) permission.websearch = { "*": "allow" };
|
|
20216
|
-
|
|
20542
|
+
const kiroOverride = extractKiroOverride(toolsSettings);
|
|
20543
|
+
const result = { permission };
|
|
20544
|
+
if (kiroOverride !== void 0) result.kiro = kiroOverride;
|
|
20545
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(result, null, 2) });
|
|
20217
20546
|
}
|
|
20218
20547
|
validate() {
|
|
20219
20548
|
return {
|
|
@@ -20263,19 +20592,71 @@ function buildKiroPermissionsFromRulesync({ config, logger, existing }) {
|
|
|
20263
20592
|
});
|
|
20264
20593
|
else logger?.warn(`Kiro permissions do not support category: ${category}. Skipping.`);
|
|
20265
20594
|
}
|
|
20266
|
-
nextToolsSettings.shell =
|
|
20595
|
+
nextToolsSettings.shell = {
|
|
20596
|
+
...preservedShellFlags(existing),
|
|
20597
|
+
...shell
|
|
20598
|
+
};
|
|
20267
20599
|
nextToolsSettings.read = pathTable(pathBuckets.read);
|
|
20268
20600
|
nextToolsSettings.write = pathTable(pathBuckets.write);
|
|
20269
20601
|
for (const key of ["grep", "glob"]) {
|
|
20270
20602
|
const bucket = pathBuckets[key];
|
|
20271
20603
|
if (bucket && (bucket.allow.length > 0 || bucket.deny.length > 0)) nextToolsSettings[key] = pathTable(bucket);
|
|
20272
20604
|
}
|
|
20605
|
+
applyKiroOverride({
|
|
20606
|
+
override: config.kiro,
|
|
20607
|
+
nextToolsSettings,
|
|
20608
|
+
logger
|
|
20609
|
+
});
|
|
20273
20610
|
return {
|
|
20274
20611
|
...existing,
|
|
20275
20612
|
allowedTools: [...nextAllowedTools].toSorted(),
|
|
20276
20613
|
toolsSettings: nextToolsSettings
|
|
20277
20614
|
};
|
|
20278
20615
|
}
|
|
20616
|
+
/**
|
|
20617
|
+
* Non-canonical `toolsSettings.shell` keys already present in the existing agent
|
|
20618
|
+
* config (everything except the canonical `allowed`/`deniedCommands`), so a
|
|
20619
|
+
* regenerate does not silently drop a hand-set `autoAllowReadonly` /
|
|
20620
|
+
* `denyByDefault` when no `kiro` override re-authors it.
|
|
20621
|
+
*/
|
|
20622
|
+
function preservedShellFlags(existing) {
|
|
20623
|
+
const existingShell = asRecord$1(asRecord$1(existing.toolsSettings).shell);
|
|
20624
|
+
const flags = {};
|
|
20625
|
+
for (const [key, value] of Object.entries(existingShell)) if (!CANONICAL_SHELL_KEYS.has(key)) flags[key] = value;
|
|
20626
|
+
return flags;
|
|
20627
|
+
}
|
|
20628
|
+
/**
|
|
20629
|
+
* Deep-merge the `kiro` override's `toolsSettings` block into the generated
|
|
20630
|
+
* settings, one `toolsSettings` key at a time so the override's leaf fields win
|
|
20631
|
+
* without clobbering canonical-generated siblings.
|
|
20632
|
+
*
|
|
20633
|
+
* Guards, so the override can only author the non-canonical surfaces it is meant
|
|
20634
|
+
* for and can never weaken a canonical-generated deny:
|
|
20635
|
+
* - prototype-pollution keys are skipped before being used as object keys;
|
|
20636
|
+
* - fully-canonical `toolsSettings` keys (`read`/`write`/`grep`/`glob`) are
|
|
20637
|
+
* rejected outright with a warning (their paths are owned by the canonical
|
|
20638
|
+
* `permission` block);
|
|
20639
|
+
* - for `shell` (partly canonical), the canonical command-list leaves
|
|
20640
|
+
* (`allowed`/`deniedCommands`) are stripped from the override value so only the
|
|
20641
|
+
* auto-trust flags merge.
|
|
20642
|
+
*/
|
|
20643
|
+
function applyKiroOverride({ override, nextToolsSettings, logger }) {
|
|
20644
|
+
const overrideToolsSettings = override?.toolsSettings;
|
|
20645
|
+
if (!isPlainObject$1(overrideToolsSettings)) return;
|
|
20646
|
+
for (const [key, value] of Object.entries(overrideToolsSettings)) {
|
|
20647
|
+
if (isPrototypePollutionKey(key)) continue;
|
|
20648
|
+
if (!isPlainObject$1(value)) continue;
|
|
20649
|
+
if (CANONICAL_TOOL_SETTINGS_KEYS.has(key)) {
|
|
20650
|
+
logger?.warn(`Kiro permissions: ignoring 'kiro.toolsSettings.${key}' override; '${key}' paths are driven by the canonical permission block.`);
|
|
20651
|
+
continue;
|
|
20652
|
+
}
|
|
20653
|
+
const mergeValue = key === "shell" ? Object.fromEntries(Object.entries(value).filter(([leaf]) => !CANONICAL_SHELL_KEYS.has(leaf))) : value;
|
|
20654
|
+
nextToolsSettings[key] = {
|
|
20655
|
+
...asRecord$1(nextToolsSettings[key]),
|
|
20656
|
+
...mergeValue
|
|
20657
|
+
};
|
|
20658
|
+
}
|
|
20659
|
+
}
|
|
20279
20660
|
function pathTable(bucket) {
|
|
20280
20661
|
return {
|
|
20281
20662
|
allowedPaths: bucket?.allow ?? [],
|
|
@@ -20295,6 +20676,31 @@ function asRecord$1(value) {
|
|
|
20295
20676
|
const result = UnknownRecordSchema.safeParse(value);
|
|
20296
20677
|
return result.success ? result.data : {};
|
|
20297
20678
|
}
|
|
20679
|
+
/**
|
|
20680
|
+
* Build the `kiro` permissions override from a parsed agent config's
|
|
20681
|
+
* `toolsSettings`, lifting the Kiro-specific knobs with no canonical category:
|
|
20682
|
+
* - `shell.*` flags other than the canonical `allowed`/`deniedCommands`
|
|
20683
|
+
* (e.g. `autoAllowReadonly`, `denyByDefault`), verbatim.
|
|
20684
|
+
* - the whole `aws` object (`allowedServices` / `deniedServices` / …), verbatim.
|
|
20685
|
+
* - the whole `web_fetch` object (`trusted` / `blocked`), verbatim.
|
|
20686
|
+
*
|
|
20687
|
+
* Returns `undefined` when none are present so the override key is omitted.
|
|
20688
|
+
*/
|
|
20689
|
+
function extractKiroOverride(toolsSettings) {
|
|
20690
|
+
const overrideToolsSettings = {};
|
|
20691
|
+
const shellFlags = {};
|
|
20692
|
+
for (const [key, value] of Object.entries(asRecord$1(toolsSettings.shell))) {
|
|
20693
|
+
if (isPrototypePollutionKey(key)) continue;
|
|
20694
|
+
if (!CANONICAL_SHELL_KEYS.has(key)) shellFlags[key] = value;
|
|
20695
|
+
}
|
|
20696
|
+
if (Object.keys(shellFlags).length > 0) overrideToolsSettings.shell = shellFlags;
|
|
20697
|
+
const aws = asRecord$1(toolsSettings.aws);
|
|
20698
|
+
if (Object.keys(aws).length > 0) overrideToolsSettings.aws = aws;
|
|
20699
|
+
const webFetch = asRecord$1(toolsSettings.web_fetch);
|
|
20700
|
+
if (Object.keys(webFetch).length > 0) overrideToolsSettings.web_fetch = webFetch;
|
|
20701
|
+
if (Object.keys(overrideToolsSettings).length === 0) return void 0;
|
|
20702
|
+
return { toolsSettings: overrideToolsSettings };
|
|
20703
|
+
}
|
|
20298
20704
|
function asStringArray(value) {
|
|
20299
20705
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
20300
20706
|
}
|
|
@@ -21107,7 +21513,7 @@ var RovodevPermissions = class RovodevPermissions extends ToolPermissions {
|
|
|
21107
21513
|
const existingContent = await readFileContentOrNull(filePath) ?? "";
|
|
21108
21514
|
let parsed;
|
|
21109
21515
|
try {
|
|
21110
|
-
parsed = existingContent.trim() === "" ? {} :
|
|
21516
|
+
parsed = existingContent.trim() === "" ? {} : loadYaml(existingContent);
|
|
21111
21517
|
} catch (error) {
|
|
21112
21518
|
throw new Error(`Failed to parse existing Rovodev config at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
21113
21519
|
}
|
|
@@ -21133,7 +21539,7 @@ var RovodevPermissions = class RovodevPermissions extends ToolPermissions {
|
|
|
21133
21539
|
let parsed;
|
|
21134
21540
|
try {
|
|
21135
21541
|
const content = this.getFileContent();
|
|
21136
|
-
parsed = content.trim() === "" ? {} :
|
|
21542
|
+
parsed = content.trim() === "" ? {} : loadYaml(content);
|
|
21137
21543
|
} catch (error) {
|
|
21138
21544
|
throw new Error(`Failed to parse Rovodev permissions content in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
21139
21545
|
}
|
|
@@ -21328,9 +21734,9 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
|
|
|
21328
21734
|
const rulesyncJson = rulesyncPermissions.getJson();
|
|
21329
21735
|
const provider = resolveActiveProvider(config);
|
|
21330
21736
|
const mode = deriveTaktPermissionMode(rulesyncJson);
|
|
21331
|
-
const override = isPlainObject(rulesyncJson.takt) ? rulesyncJson.takt : void 0;
|
|
21332
|
-
const stepOverrides = isPlainObject(override?.[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? override[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21333
|
-
const overrideProviderOptions = isPlainObject(override?.[TAKT_PROVIDER_OPTIONS_KEY]) ? override[TAKT_PROVIDER_OPTIONS_KEY] : void 0;
|
|
21737
|
+
const override = isPlainObject$1(rulesyncJson.takt) ? rulesyncJson.takt : void 0;
|
|
21738
|
+
const stepOverrides = isPlainObject$1(override?.[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? override[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21739
|
+
const overrideProviderOptions = isPlainObject$1(override?.[TAKT_PROVIDER_OPTIONS_KEY]) ? override[TAKT_PROVIDER_OPTIONS_KEY] : void 0;
|
|
21334
21740
|
const patch = {
|
|
21335
21741
|
[TAKT_PROVIDER_PROFILES_KEY]: { [provider]: {
|
|
21336
21742
|
[TAKT_DEFAULT_PERMISSION_MODE_KEY]: mode,
|
|
@@ -21361,12 +21767,12 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
|
|
|
21361
21767
|
invalidRootPolicy: "error"
|
|
21362
21768
|
});
|
|
21363
21769
|
const provider = resolveActiveProvider(config);
|
|
21364
|
-
const profiles = isPlainObject(config[TAKT_PROVIDER_PROFILES_KEY]) ? config[TAKT_PROVIDER_PROFILES_KEY] : {};
|
|
21365
|
-
const profile = isPlainObject(profiles[provider]) ? profiles[provider] : {};
|
|
21770
|
+
const profiles = isPlainObject$1(config[TAKT_PROVIDER_PROFILES_KEY]) ? config[TAKT_PROVIDER_PROFILES_KEY] : {};
|
|
21771
|
+
const profile = isPlainObject$1(profiles[provider]) ? profiles[provider] : {};
|
|
21366
21772
|
const mode = profile[TAKT_DEFAULT_PERMISSION_MODE_KEY];
|
|
21367
21773
|
const rulesyncConfig = taktModeToRulesyncConfig(mode);
|
|
21368
|
-
const stepOverrides = isPlainObject(profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21369
|
-
const providerOptions = isPlainObject(config[TAKT_PROVIDER_OPTIONS_KEY]) ? config[TAKT_PROVIDER_OPTIONS_KEY] : void 0;
|
|
21774
|
+
const stepOverrides = isPlainObject$1(profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY]) ? profile[TAKT_STEP_PERMISSION_OVERRIDES_KEY] : void 0;
|
|
21775
|
+
const providerOptions = isPlainObject$1(config[TAKT_PROVIDER_OPTIONS_KEY]) ? config[TAKT_PROVIDER_OPTIONS_KEY] : void 0;
|
|
21370
21776
|
const taktOverride = {};
|
|
21371
21777
|
if (stepOverrides && Object.keys(stepOverrides).length > 0) taktOverride[TAKT_STEP_PERMISSION_OVERRIDES_KEY] = stepOverrides;
|
|
21372
21778
|
if (providerOptions && Object.keys(providerOptions).length > 0) taktOverride[TAKT_PROVIDER_OPTIONS_KEY] = providerOptions;
|
|
@@ -21398,7 +21804,7 @@ var TaktPermissions = class TaktPermissions extends ToolPermissions {
|
|
|
21398
21804
|
function resolveActiveProvider(config) {
|
|
21399
21805
|
if (typeof config[TAKT_PROVIDER_KEY] === "string" && config[TAKT_PROVIDER_KEY].trim() !== "") return config[TAKT_PROVIDER_KEY];
|
|
21400
21806
|
const profiles = config[TAKT_PROVIDER_PROFILES_KEY];
|
|
21401
|
-
if (isPlainObject(profiles)) {
|
|
21807
|
+
if (isPlainObject$1(profiles)) {
|
|
21402
21808
|
const keys = Object.keys(profiles);
|
|
21403
21809
|
if (keys.length === 1) return keys[0];
|
|
21404
21810
|
}
|
|
@@ -24387,7 +24793,7 @@ function extractOpenaiYamlFile(otherFiles) {
|
|
|
24387
24793
|
const rest = [];
|
|
24388
24794
|
for (const file of otherFiles) {
|
|
24389
24795
|
if (toPosixPath(file.relativeFilePathToDirPath) === target) try {
|
|
24390
|
-
const loaded =
|
|
24796
|
+
const loaded = loadYaml(file.fileBuffer.toString("utf-8"));
|
|
24391
24797
|
if (loaded !== null && typeof loaded === "object" && !Array.isArray(loaded)) {
|
|
24392
24798
|
parsed = loaded;
|
|
24393
24799
|
continue;
|
|
@@ -26638,6 +27044,139 @@ var QwencodeSkill = class QwencodeSkill extends ToolSkill {
|
|
|
26638
27044
|
}
|
|
26639
27045
|
};
|
|
26640
27046
|
//#endregion
|
|
27047
|
+
//#region src/features/skills/reasonix-skill.ts
|
|
27048
|
+
const ReasonixSkillFrontmatterSchema = z.looseObject({
|
|
27049
|
+
name: z.string(),
|
|
27050
|
+
description: z.string()
|
|
27051
|
+
});
|
|
27052
|
+
/**
|
|
27053
|
+
* Represents a DeepSeek-Reasonix skill directory.
|
|
27054
|
+
*
|
|
27055
|
+
* Reasonix discovers directory-layout skills (`<name>/SKILL.md`) under
|
|
27056
|
+
* `.reasonix/skills/` (project) and `~/.reasonix/skills/` (global); the global
|
|
27057
|
+
* scope is served by the processor supplying the home directory as outputRoot.
|
|
27058
|
+
* @see https://github.com/esengine/DeepSeek-Reasonix/blob/main-v2/docs/GUIDE.md
|
|
27059
|
+
*/
|
|
27060
|
+
var ReasonixSkill = class ReasonixSkill extends ToolSkill {
|
|
27061
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = REASONIX_SKILLS_DIR_PATH, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
27062
|
+
super({
|
|
27063
|
+
outputRoot,
|
|
27064
|
+
relativeDirPath,
|
|
27065
|
+
dirName,
|
|
27066
|
+
mainFile: {
|
|
27067
|
+
name: SKILL_FILE_NAME,
|
|
27068
|
+
body,
|
|
27069
|
+
frontmatter: { ...frontmatter }
|
|
27070
|
+
},
|
|
27071
|
+
otherFiles,
|
|
27072
|
+
global
|
|
27073
|
+
});
|
|
27074
|
+
if (validate) {
|
|
27075
|
+
const result = this.validate();
|
|
27076
|
+
if (!result.success) throw result.error;
|
|
27077
|
+
}
|
|
27078
|
+
}
|
|
27079
|
+
static getSettablePaths({ global: _global = false } = {}) {
|
|
27080
|
+
return { relativeDirPath: REASONIX_SKILLS_DIR_PATH };
|
|
27081
|
+
}
|
|
27082
|
+
getFrontmatter() {
|
|
27083
|
+
return ReasonixSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
27084
|
+
}
|
|
27085
|
+
getBody() {
|
|
27086
|
+
return this.mainFile?.body ?? "";
|
|
27087
|
+
}
|
|
27088
|
+
validate() {
|
|
27089
|
+
if (this.mainFile === void 0) return {
|
|
27090
|
+
success: false,
|
|
27091
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
27092
|
+
};
|
|
27093
|
+
const result = ReasonixSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
27094
|
+
if (!result.success) return {
|
|
27095
|
+
success: false,
|
|
27096
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
27097
|
+
};
|
|
27098
|
+
return {
|
|
27099
|
+
success: true,
|
|
27100
|
+
error: null
|
|
27101
|
+
};
|
|
27102
|
+
}
|
|
27103
|
+
toRulesyncSkill() {
|
|
27104
|
+
const frontmatter = this.getFrontmatter();
|
|
27105
|
+
const rulesyncFrontmatter = {
|
|
27106
|
+
name: frontmatter.name,
|
|
27107
|
+
description: frontmatter.description,
|
|
27108
|
+
targets: ["*"]
|
|
27109
|
+
};
|
|
27110
|
+
return new RulesyncSkill({
|
|
27111
|
+
outputRoot: this.outputRoot,
|
|
27112
|
+
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
27113
|
+
dirName: this.getDirName(),
|
|
27114
|
+
frontmatter: rulesyncFrontmatter,
|
|
27115
|
+
body: this.getBody(),
|
|
27116
|
+
otherFiles: this.getOtherFiles(),
|
|
27117
|
+
validate: true,
|
|
27118
|
+
global: this.global
|
|
27119
|
+
});
|
|
27120
|
+
}
|
|
27121
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
27122
|
+
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
27123
|
+
const reasonixFrontmatter = {
|
|
27124
|
+
name: rulesyncFrontmatter.name,
|
|
27125
|
+
description: rulesyncFrontmatter.description
|
|
27126
|
+
};
|
|
27127
|
+
return new ReasonixSkill({
|
|
27128
|
+
outputRoot,
|
|
27129
|
+
relativeDirPath: ReasonixSkill.getSettablePaths({ global }).relativeDirPath,
|
|
27130
|
+
dirName: rulesyncSkill.getDirName(),
|
|
27131
|
+
frontmatter: reasonixFrontmatter,
|
|
27132
|
+
body: rulesyncSkill.getBody(),
|
|
27133
|
+
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
27134
|
+
validate,
|
|
27135
|
+
global
|
|
27136
|
+
});
|
|
27137
|
+
}
|
|
27138
|
+
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
27139
|
+
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
27140
|
+
return targets.includes("*") || targets.includes("reasonix");
|
|
27141
|
+
}
|
|
27142
|
+
static async fromDir(params) {
|
|
27143
|
+
const loaded = await this.loadSkillDirContent({
|
|
27144
|
+
...params,
|
|
27145
|
+
getSettablePaths: ReasonixSkill.getSettablePaths
|
|
27146
|
+
});
|
|
27147
|
+
const result = ReasonixSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
27148
|
+
if (!result.success) {
|
|
27149
|
+
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
27150
|
+
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
27151
|
+
}
|
|
27152
|
+
return new ReasonixSkill({
|
|
27153
|
+
outputRoot: loaded.outputRoot,
|
|
27154
|
+
relativeDirPath: loaded.relativeDirPath,
|
|
27155
|
+
dirName: loaded.dirName,
|
|
27156
|
+
frontmatter: result.data,
|
|
27157
|
+
body: loaded.body,
|
|
27158
|
+
otherFiles: loaded.otherFiles,
|
|
27159
|
+
validate: true,
|
|
27160
|
+
global: loaded.global
|
|
27161
|
+
});
|
|
27162
|
+
}
|
|
27163
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
27164
|
+
return new ReasonixSkill({
|
|
27165
|
+
outputRoot,
|
|
27166
|
+
relativeDirPath,
|
|
27167
|
+
dirName,
|
|
27168
|
+
frontmatter: {
|
|
27169
|
+
name: "",
|
|
27170
|
+
description: ""
|
|
27171
|
+
},
|
|
27172
|
+
body: "",
|
|
27173
|
+
otherFiles: [],
|
|
27174
|
+
validate: false,
|
|
27175
|
+
global
|
|
27176
|
+
});
|
|
27177
|
+
}
|
|
27178
|
+
};
|
|
27179
|
+
//#endregion
|
|
26641
27180
|
//#region src/constants/replit-paths.ts
|
|
26642
27181
|
const REPLIT_RULE_FILE_NAME = "replit.md";
|
|
26643
27182
|
const REPLIT_SKILLS_DIR_PATH = join(".agents", "skills");
|
|
@@ -27732,6 +28271,14 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
27732
28271
|
supportsGlobal: true
|
|
27733
28272
|
}
|
|
27734
28273
|
}],
|
|
28274
|
+
["reasonix", {
|
|
28275
|
+
class: ReasonixSkill,
|
|
28276
|
+
meta: {
|
|
28277
|
+
supportsProject: true,
|
|
28278
|
+
supportsSimulated: false,
|
|
28279
|
+
supportsGlobal: true
|
|
28280
|
+
}
|
|
28281
|
+
}],
|
|
27735
28282
|
["replit", {
|
|
27736
28283
|
class: ReplitSkill,
|
|
27737
28284
|
meta: {
|
|
@@ -30015,7 +30562,7 @@ var GooseSubagent = class GooseSubagent extends ToolSubagent {
|
|
|
30015
30562
|
const fileContent = await readFileContent(filePath);
|
|
30016
30563
|
let parsed;
|
|
30017
30564
|
try {
|
|
30018
|
-
parsed =
|
|
30565
|
+
parsed = loadYaml(fileContent);
|
|
30019
30566
|
} catch (error) {
|
|
30020
30567
|
throw new Error(`Failed to parse Goose recipe (${filePath}): ${formatError(error)}`, { cause: error });
|
|
30021
30568
|
}
|
|
@@ -31403,7 +31950,7 @@ var RooSubagent = class RooSubagent extends ToolSubagent {
|
|
|
31403
31950
|
const fileContent = await readFileContent(filePath);
|
|
31404
31951
|
let parsed;
|
|
31405
31952
|
try {
|
|
31406
|
-
parsed =
|
|
31953
|
+
parsed = loadYaml(fileContent);
|
|
31407
31954
|
} catch (error) {
|
|
31408
31955
|
throw new Error(`Failed to parse .roomodes (${filePath}): ${formatError(error)}`, { cause: error });
|
|
31409
31956
|
}
|
|
@@ -35596,6 +36143,72 @@ var QwencodeRule = class QwencodeRule extends ToolRule {
|
|
|
35596
36143
|
}
|
|
35597
36144
|
};
|
|
35598
36145
|
//#endregion
|
|
36146
|
+
//#region src/features/rules/reasonix-rule.ts
|
|
36147
|
+
var ReasonixRule = class ReasonixRule extends ToolRule {
|
|
36148
|
+
constructor({ fileContent, root, ...rest }) {
|
|
36149
|
+
super({
|
|
36150
|
+
...rest,
|
|
36151
|
+
fileContent,
|
|
36152
|
+
root: root ?? false
|
|
36153
|
+
});
|
|
36154
|
+
}
|
|
36155
|
+
static getSettablePaths({ global = false } = {}) {
|
|
36156
|
+
return { root: {
|
|
36157
|
+
relativeDirPath: global ? REASONIX_GLOBAL_DIR : ".",
|
|
36158
|
+
relativeFilePath: REASONIX_RULE_FILE_NAME
|
|
36159
|
+
} };
|
|
36160
|
+
}
|
|
36161
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, validate = true, global = false }) {
|
|
36162
|
+
const { root } = this.getSettablePaths({ global });
|
|
36163
|
+
const fileContent = await readFileContent(join(outputRoot, join(root.relativeDirPath, root.relativeFilePath)));
|
|
36164
|
+
return new ReasonixRule({
|
|
36165
|
+
outputRoot,
|
|
36166
|
+
relativeDirPath: root.relativeDirPath,
|
|
36167
|
+
relativeFilePath: root.relativeFilePath,
|
|
36168
|
+
fileContent,
|
|
36169
|
+
validate,
|
|
36170
|
+
root: true
|
|
36171
|
+
});
|
|
36172
|
+
}
|
|
36173
|
+
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
36174
|
+
const { root } = this.getSettablePaths({ global });
|
|
36175
|
+
const isRoot = rulesyncRule.getFrontmatter().root ?? false;
|
|
36176
|
+
return new ReasonixRule({
|
|
36177
|
+
outputRoot,
|
|
36178
|
+
relativeDirPath: root.relativeDirPath,
|
|
36179
|
+
relativeFilePath: root.relativeFilePath,
|
|
36180
|
+
fileContent: rulesyncRule.getBody(),
|
|
36181
|
+
validate,
|
|
36182
|
+
root: isRoot
|
|
36183
|
+
});
|
|
36184
|
+
}
|
|
36185
|
+
toRulesyncRule() {
|
|
36186
|
+
return this.toRulesyncRuleDefault();
|
|
36187
|
+
}
|
|
36188
|
+
validate() {
|
|
36189
|
+
return {
|
|
36190
|
+
success: true,
|
|
36191
|
+
error: null
|
|
36192
|
+
};
|
|
36193
|
+
}
|
|
36194
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
36195
|
+
return new ReasonixRule({
|
|
36196
|
+
outputRoot,
|
|
36197
|
+
relativeDirPath,
|
|
36198
|
+
relativeFilePath,
|
|
36199
|
+
fileContent: "",
|
|
36200
|
+
validate: false,
|
|
36201
|
+
root: relativeFilePath === "REASONIX.md" && (relativeDirPath === "." || relativeDirPath === ".reasonix")
|
|
36202
|
+
});
|
|
36203
|
+
}
|
|
36204
|
+
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
36205
|
+
return this.isTargetedByRulesyncRuleDefault({
|
|
36206
|
+
rulesyncRule,
|
|
36207
|
+
toolTarget: "reasonix"
|
|
36208
|
+
});
|
|
36209
|
+
}
|
|
36210
|
+
};
|
|
36211
|
+
//#endregion
|
|
35599
36212
|
//#region src/features/rules/replit-rule.ts
|
|
35600
36213
|
/**
|
|
35601
36214
|
* Rule generator for Replit Agent
|
|
@@ -36508,6 +37121,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
36508
37121
|
additionalConventions: { subagents: { subagentClass: QwencodeSubagent } }
|
|
36509
37122
|
}
|
|
36510
37123
|
}],
|
|
37124
|
+
["reasonix", {
|
|
37125
|
+
class: ReasonixRule,
|
|
37126
|
+
meta: {
|
|
37127
|
+
extension: "md",
|
|
37128
|
+
supportsGlobal: true,
|
|
37129
|
+
ruleDiscoveryMode: "auto",
|
|
37130
|
+
foldsNonRootIntoRoot: true
|
|
37131
|
+
}
|
|
37132
|
+
}],
|
|
36511
37133
|
["replit", {
|
|
36512
37134
|
class: ReplitRule,
|
|
36513
37135
|
meta: {
|
|
@@ -37393,7 +38015,6 @@ const SHARED_WRITE_FEATURE_ORDER = [
|
|
|
37393
38015
|
"permissions",
|
|
37394
38016
|
"rules"
|
|
37395
38017
|
];
|
|
37396
|
-
const SHARED_WRITE_FEATURES = new Set(SHARED_WRITE_FEATURE_ORDER);
|
|
37397
38018
|
const TARGETS_NOT_DERIVED = /* @__PURE__ */ new Set(["augmentcode-legacy", "claudecode-legacy"]);
|
|
37398
38019
|
const sharedFileKey = (path) => {
|
|
37399
38020
|
const dir = path.relativeDirPath.replace(/\\/g, "/").replace(/\/$/, "");
|
|
@@ -37416,7 +38037,13 @@ const settablePathsForScope = (cls, global) => {
|
|
|
37416
38037
|
paths.push(settable.root);
|
|
37417
38038
|
for (const alt of settable.alternativeRoots ?? []) paths.push(alt);
|
|
37418
38039
|
}
|
|
37419
|
-
|
|
38040
|
+
let extra;
|
|
38041
|
+
try {
|
|
38042
|
+
extra = cls.getExtraSharedWritePaths?.({ global }) ?? [];
|
|
38043
|
+
} catch {
|
|
38044
|
+
return paths;
|
|
38045
|
+
}
|
|
38046
|
+
for (const path of extra) if (path.relativeFilePath) paths.push(path);
|
|
37420
38047
|
return paths;
|
|
37421
38048
|
};
|
|
37422
38049
|
const collectFactoryPaths = (factory) => [...settablePathsForScope(factory.class, false), ...settablePathsForScope(factory.class, true)];
|
|
@@ -37429,7 +38056,6 @@ const deriveSharedFileWriters = () => {
|
|
|
37429
38056
|
const byKey = /* @__PURE__ */ new Map();
|
|
37430
38057
|
const pathByKey = /* @__PURE__ */ new Map();
|
|
37431
38058
|
for (const entry of PROCESSOR_REGISTRY) {
|
|
37432
|
-
if (!SHARED_WRITE_FEATURES.has(entry.feature)) continue;
|
|
37433
38059
|
const factories = entry.factory;
|
|
37434
38060
|
for (const [tool, factory] of factories) {
|
|
37435
38061
|
if (TARGETS_NOT_DERIVED.has(tool)) continue;
|
|
@@ -37818,7 +38444,7 @@ async function generateRulesCore(params) {
|
|
|
37818
38444
|
targets: config.getConfigFileTargets(),
|
|
37819
38445
|
global: config.getGlobal()
|
|
37820
38446
|
}) : /* @__PURE__ */ new Map();
|
|
37821
|
-
for (const
|
|
38447
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
37822
38448
|
if (!config.getFeatures(toolTarget).includes("rules")) continue;
|
|
37823
38449
|
const processor = new RulesProcessor({
|
|
37824
38450
|
outputRoot,
|
|
@@ -37873,7 +38499,7 @@ async function generateIgnoreCore(params) {
|
|
|
37873
38499
|
let hasDiff = false;
|
|
37874
38500
|
for (const toolTarget of intersection(config.getTargets(), supportedIgnoreTargets)) {
|
|
37875
38501
|
if (!config.getFeatures(toolTarget).includes("ignore")) continue;
|
|
37876
|
-
for (const outputRoot of config.getOutputRoots()) try {
|
|
38502
|
+
for (const outputRoot of config.getOutputRoots(toolTarget)) try {
|
|
37877
38503
|
const processor = new IgnoreProcessor({
|
|
37878
38504
|
outputRoot,
|
|
37879
38505
|
inputRoot: config.getInputRoot(),
|
|
@@ -37914,7 +38540,7 @@ async function generateMcpCore(params) {
|
|
|
37914
38540
|
featureName: "mcp",
|
|
37915
38541
|
logger
|
|
37916
38542
|
});
|
|
37917
|
-
for (const
|
|
38543
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
37918
38544
|
if (!config.getFeatures(toolTarget).includes("mcp")) continue;
|
|
37919
38545
|
const processor = new McpProcessor({
|
|
37920
38546
|
outputRoot,
|
|
@@ -37956,7 +38582,7 @@ async function generateCommandsCore(params) {
|
|
|
37956
38582
|
featureName: "commands",
|
|
37957
38583
|
logger
|
|
37958
38584
|
});
|
|
37959
|
-
for (const
|
|
38585
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
37960
38586
|
if (!config.getFeatures(toolTarget).includes("commands")) continue;
|
|
37961
38587
|
const processor = new CommandsProcessor({
|
|
37962
38588
|
outputRoot,
|
|
@@ -37998,7 +38624,7 @@ async function generateSubagentsCore(params) {
|
|
|
37998
38624
|
featureName: "subagents",
|
|
37999
38625
|
logger
|
|
38000
38626
|
});
|
|
38001
|
-
for (const
|
|
38627
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
38002
38628
|
if (!config.getFeatures(toolTarget).includes("subagents")) continue;
|
|
38003
38629
|
const processor = new SubagentsProcessor({
|
|
38004
38630
|
outputRoot,
|
|
@@ -38041,7 +38667,7 @@ async function generateSkillsCore(params) {
|
|
|
38041
38667
|
featureName: "skills",
|
|
38042
38668
|
logger
|
|
38043
38669
|
});
|
|
38044
|
-
for (const
|
|
38670
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
38045
38671
|
if (!config.getFeatures(toolTarget).includes("skills")) continue;
|
|
38046
38672
|
const processor = new SkillsProcessor({
|
|
38047
38673
|
outputRoot,
|
|
@@ -38082,7 +38708,7 @@ async function generateHooksCore(params) {
|
|
|
38082
38708
|
featureName: "hooks",
|
|
38083
38709
|
logger
|
|
38084
38710
|
});
|
|
38085
|
-
for (const
|
|
38711
|
+
for (const toolTarget of toolTargets) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
38086
38712
|
if (!config.getFeatures(toolTarget).includes("hooks")) continue;
|
|
38087
38713
|
const processor = new HooksProcessor({
|
|
38088
38714
|
outputRoot,
|
|
@@ -38119,7 +38745,7 @@ async function generatePermissionsCore(params) {
|
|
|
38119
38745
|
let totalCount = 0;
|
|
38120
38746
|
const allPaths = [];
|
|
38121
38747
|
let hasDiff = false;
|
|
38122
|
-
for (const
|
|
38748
|
+
for (const toolTarget of intersection(config.getTargets(), supportedPermissionsTargets)) for (const outputRoot of config.getOutputRoots(toolTarget)) {
|
|
38123
38749
|
if (!config.getFeatures(toolTarget).includes("permissions")) continue;
|
|
38124
38750
|
try {
|
|
38125
38751
|
const processor = new PermissionsProcessor({
|
|
@@ -38395,6 +39021,6 @@ async function importPermissionsCore(params) {
|
|
|
38395
39021
|
return writtenCount;
|
|
38396
39022
|
}
|
|
38397
39023
|
//#endregion
|
|
38398
|
-
export {
|
|
39024
|
+
export { removeTempDirectory as $, RulesyncCommand as A, checkPathTraversal as B, RulesyncHooks as C, RULESYNC_RULES_RELATIVE_DIR_PATH as Ct, CLAUDECODE_MEMORIES_DIR_NAME as D, formatError as Dt, CLAUDECODE_LOCAL_RULE_FILE_NAME as E, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Et, findControlCharacter as F, findFilesByGlobs as G, directoryExists as H, ConsoleLogger as I, isSymlink as J, getFileSize as K, JsonLogger as L, stringifyFrontmatter as M, loadYaml as N, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as O, ALL_FEATURES as Ot, ConfigResolver as P, removeFile as Q, CLIError as R, HooksProcessor as S, RULESYNC_RELATIVE_DIR_PATH as St, CLAUDECODE_DIR as T, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Tt, ensureDir as U, createTempDirectory as V, fileExists as W, readFileContent as X, listDirectoryFiles as Y, removeDirectory as Z, RulesyncPermissions as _, RULESYNC_MCP_RELATIVE_FILE_PATH as _t, convertFromTool as a, MAX_FILE_SIZE as at, IgnoreProcessor as b, RULESYNC_PERMISSIONS_FILE_NAME as bt, RulesyncRuleFrontmatterSchema as c, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as ct, RulesyncSubagentFrontmatterSchema as d, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as dt, toPosixPath as et, SkillsProcessor as f, RULESYNC_HOOKS_FILE_NAME as ft, SKILL_FILE_NAME as g, RULESYNC_MCP_FILE_NAME as gt, RulesyncSkillFrontmatterSchema as h, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as ht, getProcessorRegistryEntry as i, ToolTargetSchema as it, RulesyncCommandFrontmatterSchema as j, CLAUDECODE_SKILLS_DIR_PATH as k, ALL_FEATURES_WITH_WILDCARD as kt, SubagentsProcessor as l, RULESYNC_CONFIG_RELATIVE_FILE_PATH as lt, RulesyncSkill as m, RULESYNC_IGNORE_RELATIVE_FILE_PATH as mt, checkRulesyncDirExists as n, ALL_TOOL_TARGETS as nt, RulesProcessor as o, RULESYNC_AIIGNORE_FILE_NAME as ot, getLocalSkillDirNames as p, RULESYNC_HOOKS_RELATIVE_FILE_PATH as pt, getHomeDirectory as q, generate as r, ALL_TOOL_TARGETS_WITH_WILDCARD as rt, RulesyncRule as s, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as st, importFromTool as t, writeFileContent as tt, RulesyncSubagent as u, RULESYNC_CONFIG_SCHEMA_URL as ut, McpProcessor as v, RULESYNC_MCP_SCHEMA_URL as vt, CommandsProcessor as w, RULESYNC_SKILLS_RELATIVE_DIR_PATH as wt, RulesyncIgnore as x, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as xt, RulesyncMcp as y, RULESYNC_OVERVIEW_FILE_NAME as yt, ErrorCodes as z };
|
|
38399
39025
|
|
|
38400
|
-
//# sourceMappingURL=import-
|
|
39026
|
+
//# sourceMappingURL=import-B6nwZmGl.js.map
|