rulesync 43.6.1 → 43.6.3
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/dist/cli/index.cjs +7 -10
- package/dist/cli/index.js +7 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-BzVkMPd5.cjs → import-DCvPujUW.cjs} +656 -344
- package/dist/{import-Safgeb1C.js → import-w0JlEoNl.js} +652 -340
- package/dist/{import-Safgeb1C.js.map → import-w0JlEoNl.js.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +8 -8
|
@@ -2269,6 +2269,190 @@ async function removeTempDirectory(tempDir) {
|
|
|
2269
2269
|
});
|
|
2270
2270
|
} catch {}
|
|
2271
2271
|
}
|
|
2272
|
+
// src/utils/prototype-pollution.ts
|
|
2273
|
+
/**
|
|
2274
|
+
* Keys that, if walked into when constructing or merging objects from
|
|
2275
|
+
* untrusted input, can mutate `Object.prototype` (or otherwise the prototype
|
|
2276
|
+
* chain) and propagate state to every other object in the runtime. Any code
|
|
2277
|
+
* that copies arbitrary user-supplied keys into a fresh object — frontmatter
|
|
2278
|
+
* parsing, MCP config conversion, settings round-trip — should skip these.
|
|
2279
|
+
*/
|
|
2280
|
+
const PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
|
|
2281
|
+
"__proto__",
|
|
2282
|
+
"constructor",
|
|
2283
|
+
"prototype"
|
|
2284
|
+
]);
|
|
2285
|
+
function isPrototypePollutionKey(key) {
|
|
2286
|
+
return PROTOTYPE_POLLUTION_KEYS.has(key);
|
|
2287
|
+
}
|
|
2288
|
+
/**
|
|
2289
|
+
* Returns a shallow copy of a record's own entries with every
|
|
2290
|
+
* prototype-pollution key (`__proto__`, `constructor`, `prototype`) dropped.
|
|
2291
|
+
*
|
|
2292
|
+
* Use when copying a nested, user-supplied string map — an MCP server's `env`
|
|
2293
|
+
* or `headers` table — into freshly generated config. Carrying such a map by
|
|
2294
|
+
* reference, or re-assigning its keys via bracket notation, would let a literal
|
|
2295
|
+
* `__proto__` key ride along (and re-assigning it would mutate the target's
|
|
2296
|
+
* prototype). Walking the entries through this helper severs that path while
|
|
2297
|
+
* preserving every legitimate key.
|
|
2298
|
+
*/
|
|
2299
|
+
function omitPrototypePollutionKeys(record) {
|
|
2300
|
+
const sanitized = {};
|
|
2301
|
+
for (const [key, value] of Object.entries(record)) {
|
|
2302
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
2303
|
+
sanitized[key] = value;
|
|
2304
|
+
}
|
|
2305
|
+
return sanitized;
|
|
2306
|
+
}
|
|
2307
|
+
/**
|
|
2308
|
+
* Like {@link omitPrototypePollutionKeys}, but recursive: every plain object
|
|
2309
|
+
* reachable through the value (directly, or through an array) is copied with
|
|
2310
|
+
* its prototype-pollution keys dropped. Scalars and arrays of scalars come back
|
|
2311
|
+
* as they are.
|
|
2312
|
+
*
|
|
2313
|
+
* Use when passing an unknown, user-supplied value through unchanged — an MCP
|
|
2314
|
+
* server field a tool documents that rulesync does not model — so a
|
|
2315
|
+
* `__proto__` key nested anywhere inside it cannot ride into the generated
|
|
2316
|
+
* config, where the consuming tool may merge the object without the same care.
|
|
2317
|
+
*/
|
|
2318
|
+
function omitPrototypePollutionKeysDeep(value) {
|
|
2319
|
+
if (Array.isArray(value)) return value.map((entry) => omitPrototypePollutionKeysDeep(entry));
|
|
2320
|
+
if (value === null || typeof value !== "object") return value;
|
|
2321
|
+
const sanitized = {};
|
|
2322
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2323
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
2324
|
+
sanitized[key] = omitPrototypePollutionKeysDeep(entry);
|
|
2325
|
+
}
|
|
2326
|
+
return sanitized;
|
|
2327
|
+
}
|
|
2328
|
+
// src/utils/jsonc.ts
|
|
2329
|
+
/**
|
|
2330
|
+
* Rebuild the parsed value from its own enumerable entries, dropping
|
|
2331
|
+
* prototype-pollution keys (`__proto__`, `constructor`, `prototype`).
|
|
2332
|
+
* `jsonc-parser` assigns keys with plain `obj[key] = value` semantics, so a
|
|
2333
|
+
* literal `__proto__` key would replace the containing object's prototype
|
|
2334
|
+
* instead of becoming an own property; rebuilding gives every object a clean
|
|
2335
|
+
* `Object.prototype` again and severs that path.
|
|
2336
|
+
*/
|
|
2337
|
+
function deepSanitize(value) {
|
|
2338
|
+
if (Array.isArray(value)) return value.map((item) => deepSanitize(item));
|
|
2339
|
+
if (value !== null && typeof value === "object") {
|
|
2340
|
+
const sanitized = {};
|
|
2341
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2342
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
2343
|
+
sanitized[key] = deepSanitize(entry);
|
|
2344
|
+
}
|
|
2345
|
+
return sanitized;
|
|
2346
|
+
}
|
|
2347
|
+
return value;
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* Collect the paths of every prototype-pollution key in the document, walking
|
|
2351
|
+
* the syntax tree rather than the parsed value.
|
|
2352
|
+
*
|
|
2353
|
+
* The parsed value cannot answer this question for `__proto__`:
|
|
2354
|
+
* `obj["__proto__"] = "deny"` is discarded by the engine outright, and
|
|
2355
|
+
* `obj["__proto__"] = {...}` replaces the prototype instead of adding an own
|
|
2356
|
+
* property — either way the key is already gone by the time
|
|
2357
|
+
* {@link deepSanitize} could look for it, and only the source text still knows
|
|
2358
|
+
* the user wrote it. `constructor` and `prototype` do become own properties and
|
|
2359
|
+
* could be found in the parsed value, but they are collected here too so all
|
|
2360
|
+
* three keys are answered the same way.
|
|
2361
|
+
*
|
|
2362
|
+
* A pollution key's subtree is not descended into, matching
|
|
2363
|
+
* {@link deepSanitize}, which drops the whole value along with the key.
|
|
2364
|
+
*/
|
|
2365
|
+
function collectPollutionKeyPaths({ node, path, found }) {
|
|
2366
|
+
if (node.type === "array") {
|
|
2367
|
+
for (const [index, child] of (node.children ?? []).entries()) collectPollutionKeyPaths({
|
|
2368
|
+
node: child,
|
|
2369
|
+
path: `${path}[${index}]`,
|
|
2370
|
+
found
|
|
2371
|
+
});
|
|
2372
|
+
return;
|
|
2373
|
+
}
|
|
2374
|
+
if (node.type !== "object") return;
|
|
2375
|
+
for (const property of node.children ?? []) {
|
|
2376
|
+
const [keyNode, valueNode] = property.children ?? [];
|
|
2377
|
+
if (typeof keyNode?.value !== "string") continue;
|
|
2378
|
+
const keyPath = path === "" ? keyNode.value : `${path}.${keyNode.value}`;
|
|
2379
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(keyNode.value)) {
|
|
2380
|
+
found.push(keyPath);
|
|
2381
|
+
continue;
|
|
2382
|
+
}
|
|
2383
|
+
if (valueNode !== void 0) collectPollutionKeyPaths({
|
|
2384
|
+
node: valueNode,
|
|
2385
|
+
path: keyPath,
|
|
2386
|
+
found
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
/**
|
|
2391
|
+
* Parse a JSONC (JSON with Comments) document strictly.
|
|
2392
|
+
*
|
|
2393
|
+
* Unlike `jsonc-parser`'s bare `parse` (which silently tolerates syntax
|
|
2394
|
+
* errors and returns a best-effort value), this throws on any parse error so
|
|
2395
|
+
* a malformed source file fails loudly instead of generating half-empty tool
|
|
2396
|
+
* configs. Plain JSON is valid JSONC, so this is a drop-in replacement for
|
|
2397
|
+
* `JSON.parse` on files that may contain comments or trailing commas.
|
|
2398
|
+
*/
|
|
2399
|
+
function parseJsonc(content) {
|
|
2400
|
+
return deepSanitize(parseStrict(content));
|
|
2401
|
+
}
|
|
2402
|
+
/**
|
|
2403
|
+
* The strict parse shared by both entry points. Kept separate from the syntax
|
|
2404
|
+
* tree walk so the many callers that never look at `droppedKeys` do not pay
|
|
2405
|
+
* for building a second representation of the document.
|
|
2406
|
+
*/
|
|
2407
|
+
function parseStrict(content) {
|
|
2408
|
+
const errors = [];
|
|
2409
|
+
const result = parse$1(content, errors, {
|
|
2410
|
+
allowTrailingComma: true,
|
|
2411
|
+
disallowComments: false
|
|
2412
|
+
});
|
|
2413
|
+
if (errors.length > 0) {
|
|
2414
|
+
const details = errors.map((e) => `${printParseErrorCode(e.error)} at offset ${e.offset}`).join(", ");
|
|
2415
|
+
throw new SyntaxError(`Failed to parse JSONC content: ${details}`);
|
|
2416
|
+
}
|
|
2417
|
+
return result;
|
|
2418
|
+
}
|
|
2419
|
+
/**
|
|
2420
|
+
* The error a source document should fail with when
|
|
2421
|
+
* {@link parseJsoncReportingDroppedKeys} reports keys the parser removed.
|
|
2422
|
+
*
|
|
2423
|
+
* Shared by every source that reports them, so the three files a user can
|
|
2424
|
+
* author explain the same removal the same way rather than each inventing its
|
|
2425
|
+
* own wording.
|
|
2426
|
+
*/
|
|
2427
|
+
function droppedPollutionKeysError({ sourcePath, droppedKeys }) {
|
|
2428
|
+
return /* @__PURE__ */ new Error(`${JSON.stringify(sourcePath)} uses ${droppedKeys.map((key) => JSON.stringify(key)).join(", ")} as ${droppedKeys.length === 1 ? "a key" : "keys"}. Rulesync removes __proto__, constructor and prototype from every source document it parses, because assigning them would reach the prototype chain instead of the object. They are therefore never written to any tool's config — rename them rather than leaving entries that silently do nothing.`);
|
|
2429
|
+
}
|
|
2430
|
+
/**
|
|
2431
|
+
* The same parse as {@link parseJsonc}, additionally reporting which
|
|
2432
|
+
* prototype-pollution keys were removed, as dotted paths
|
|
2433
|
+
* (`permission.bash.__proto__`).
|
|
2434
|
+
*
|
|
2435
|
+
* The parse itself is identical — the paths only let a caller tell the user
|
|
2436
|
+
* about entries that vanished, which they would otherwise have to notice by
|
|
2437
|
+
* comparing their source file against the generated output.
|
|
2438
|
+
*/
|
|
2439
|
+
function parseJsoncReportingDroppedKeys({ content }) {
|
|
2440
|
+
const result = parseStrict(content);
|
|
2441
|
+
const found = [];
|
|
2442
|
+
const tree = parseTree(content, [], {
|
|
2443
|
+
allowTrailingComma: true,
|
|
2444
|
+
disallowComments: false
|
|
2445
|
+
});
|
|
2446
|
+
if (tree !== void 0) collectPollutionKeyPaths({
|
|
2447
|
+
node: tree,
|
|
2448
|
+
path: "",
|
|
2449
|
+
found
|
|
2450
|
+
});
|
|
2451
|
+
return {
|
|
2452
|
+
value: deepSanitize(result),
|
|
2453
|
+
droppedKeys: [...new Set(found)]
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2272
2456
|
// src/types/json-output.ts
|
|
2273
2457
|
/**
|
|
2274
2458
|
* Error codes for CLI errors
|
|
@@ -3558,10 +3742,37 @@ const getDefaults = () => ({
|
|
|
3558
3742
|
inputRoots: void 0,
|
|
3559
3743
|
sources: []
|
|
3560
3744
|
});
|
|
3745
|
+
/**
|
|
3746
|
+
* Parse the content of a config file (`rulesync.jsonc` / `rulesync.local.jsonc`)
|
|
3747
|
+
* through the same strict, sanitizing JSONC parser as every other rulesync
|
|
3748
|
+
* source.
|
|
3749
|
+
*
|
|
3750
|
+
* `jsonc-parser`'s bare `parse` recovers from syntax errors without reporting
|
|
3751
|
+
* them, which let a typo (a missing comma, unquoted keys, trailing garbage)
|
|
3752
|
+
* silently drop keys and run rulesync with a different config than the user
|
|
3753
|
+
* wrote. Prototype-pollution keys are rejected rather than silently removed,
|
|
3754
|
+
* matching the hooks, MCP and permissions sources.
|
|
3755
|
+
*/
|
|
3756
|
+
function parseConfigFileContent({ content, filePath }) {
|
|
3757
|
+
let result;
|
|
3758
|
+
try {
|
|
3759
|
+
result = parseJsoncReportingDroppedKeys({ content });
|
|
3760
|
+
} catch (error) {
|
|
3761
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
3762
|
+
throw new Error(`Failed to parse ${JSON.stringify(filePath)}: ${detail}`, { cause: error });
|
|
3763
|
+
}
|
|
3764
|
+
if (result.droppedKeys.length > 0) throw droppedPollutionKeysError({
|
|
3765
|
+
sourcePath: filePath,
|
|
3766
|
+
droppedKeys: result.droppedKeys
|
|
3767
|
+
});
|
|
3768
|
+
return result.value;
|
|
3769
|
+
}
|
|
3561
3770
|
const loadConfigFromFile = async (filePath) => {
|
|
3562
3771
|
if (!await fileExists(filePath)) return {};
|
|
3563
|
-
const
|
|
3564
|
-
|
|
3772
|
+
const jsonData = parseConfigFileContent({
|
|
3773
|
+
content: await readFileContent(filePath),
|
|
3774
|
+
filePath
|
|
3775
|
+
});
|
|
3565
3776
|
const { $schema: _schema, ...configParams } = ConfigFileSchema.parse(jsonData);
|
|
3566
3777
|
assertTargetsFeaturesExclusive({
|
|
3567
3778
|
targets: configParams.targets,
|
|
@@ -4174,62 +4385,6 @@ function createBoundedWalk({ subject, limits, root }) {
|
|
|
4174
4385
|
leave
|
|
4175
4386
|
};
|
|
4176
4387
|
}
|
|
4177
|
-
// src/utils/prototype-pollution.ts
|
|
4178
|
-
/**
|
|
4179
|
-
* Keys that, if walked into when constructing or merging objects from
|
|
4180
|
-
* untrusted input, can mutate `Object.prototype` (or otherwise the prototype
|
|
4181
|
-
* chain) and propagate state to every other object in the runtime. Any code
|
|
4182
|
-
* that copies arbitrary user-supplied keys into a fresh object — frontmatter
|
|
4183
|
-
* parsing, MCP config conversion, settings round-trip — should skip these.
|
|
4184
|
-
*/
|
|
4185
|
-
const PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
|
|
4186
|
-
"__proto__",
|
|
4187
|
-
"constructor",
|
|
4188
|
-
"prototype"
|
|
4189
|
-
]);
|
|
4190
|
-
function isPrototypePollutionKey(key) {
|
|
4191
|
-
return PROTOTYPE_POLLUTION_KEYS.has(key);
|
|
4192
|
-
}
|
|
4193
|
-
/**
|
|
4194
|
-
* Returns a shallow copy of a record's own entries with every
|
|
4195
|
-
* prototype-pollution key (`__proto__`, `constructor`, `prototype`) dropped.
|
|
4196
|
-
*
|
|
4197
|
-
* Use when copying a nested, user-supplied string map — an MCP server's `env`
|
|
4198
|
-
* or `headers` table — into freshly generated config. Carrying such a map by
|
|
4199
|
-
* reference, or re-assigning its keys via bracket notation, would let a literal
|
|
4200
|
-
* `__proto__` key ride along (and re-assigning it would mutate the target's
|
|
4201
|
-
* prototype). Walking the entries through this helper severs that path while
|
|
4202
|
-
* preserving every legitimate key.
|
|
4203
|
-
*/
|
|
4204
|
-
function omitPrototypePollutionKeys(record) {
|
|
4205
|
-
const sanitized = {};
|
|
4206
|
-
for (const [key, value] of Object.entries(record)) {
|
|
4207
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
4208
|
-
sanitized[key] = value;
|
|
4209
|
-
}
|
|
4210
|
-
return sanitized;
|
|
4211
|
-
}
|
|
4212
|
-
/**
|
|
4213
|
-
* Like {@link omitPrototypePollutionKeys}, but recursive: every plain object
|
|
4214
|
-
* reachable through the value (directly, or through an array) is copied with
|
|
4215
|
-
* its prototype-pollution keys dropped. Scalars and arrays of scalars come back
|
|
4216
|
-
* as they are.
|
|
4217
|
-
*
|
|
4218
|
-
* Use when passing an unknown, user-supplied value through unchanged — an MCP
|
|
4219
|
-
* server field a tool documents that rulesync does not model — so a
|
|
4220
|
-
* `__proto__` key nested anywhere inside it cannot ride into the generated
|
|
4221
|
-
* config, where the consuming tool may merge the object without the same care.
|
|
4222
|
-
*/
|
|
4223
|
-
function omitPrototypePollutionKeysDeep(value) {
|
|
4224
|
-
if (Array.isArray(value)) return value.map((entry) => omitPrototypePollutionKeysDeep(entry));
|
|
4225
|
-
if (value === null || typeof value !== "object") return value;
|
|
4226
|
-
const sanitized = {};
|
|
4227
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
4228
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
4229
|
-
sanitized[key] = omitPrototypePollutionKeysDeep(entry);
|
|
4230
|
-
}
|
|
4231
|
-
return sanitized;
|
|
4232
|
-
}
|
|
4233
4388
|
// src/utils/type-guards.ts
|
|
4234
4389
|
/**
|
|
4235
4390
|
* Type guard to check if a value is a plain object (Record<string, unknown>).
|
|
@@ -4857,6 +5012,24 @@ function lookupOwn({ record, key }) {
|
|
|
4857
5012
|
return Object.hasOwn(record, key) ? record[key] : void 0;
|
|
4858
5013
|
}
|
|
4859
5014
|
/**
|
|
5015
|
+
* Return the value a map holds under its own `key`, first storing `create()`
|
|
5016
|
+
* there when it has none. A bracket read would find an inherited member for a
|
|
5017
|
+
* key such as `toString`, so an importer bucketing rules by a user-supplied
|
|
5018
|
+
* name would write into an `Object.prototype` function instead of a fresh
|
|
5019
|
+
* bucket.
|
|
5020
|
+
*/
|
|
5021
|
+
function getOrCreateOwn({ record, key, create }) {
|
|
5022
|
+
if (Object.hasOwn(record, key)) return record[key];
|
|
5023
|
+
const created = create();
|
|
5024
|
+
Object.defineProperty(record, key, {
|
|
5025
|
+
value: created,
|
|
5026
|
+
writable: true,
|
|
5027
|
+
enumerable: true,
|
|
5028
|
+
configurable: true
|
|
5029
|
+
});
|
|
5030
|
+
return created;
|
|
5031
|
+
}
|
|
5032
|
+
/**
|
|
4860
5033
|
* Swap the keys and values of a name map, e.g. a canonical-to-tool map into
|
|
4861
5034
|
* the tool-to-canonical map an importer reads. When two keys share a value,
|
|
4862
5035
|
* the later key wins.
|
|
@@ -6391,14 +6564,16 @@ const CANONICAL_TO_GOOSE_EVENT_NAMES = {
|
|
|
6391
6564
|
* Hook events supported by Goose.
|
|
6392
6565
|
*
|
|
6393
6566
|
* Goose adopts the Open Plugins hooks spec: each plugin's `hooks/hooks.json`
|
|
6394
|
-
* maps PascalCase event names to matcher/handler arrays.
|
|
6395
|
-
*
|
|
6396
|
-
*
|
|
6397
|
-
*
|
|
6398
|
-
*
|
|
6399
|
-
*
|
|
6567
|
+
* maps PascalCase event names to matcher/handler arrays.
|
|
6568
|
+
*
|
|
6569
|
+
* Goose's `HookEvent` enum defines these 11 events plus `PreToolUseResult`
|
|
6570
|
+
* (added in v1.48.0), which has no canonical equivalent: it is authored under
|
|
6571
|
+
* the `goose.hooks` override block by its native name, emitted verbatim, and
|
|
6572
|
+
* imported back into that block. Notably the enum has NO
|
|
6573
|
+
* `SubagentStart`/`SubagentStop` arms — emitting them would write keys Goose
|
|
6574
|
+
* silently ignores, so `subagentStart`/`subagentStop` are intentionally
|
|
6400
6575
|
* excluded here and from `CANONICAL_TO_GOOSE_EVENT_NAMES`.
|
|
6401
|
-
* @see https://github.com/aaif-goose/goose/blob/v1.
|
|
6576
|
+
* @see https://github.com/aaif-goose/goose/blob/v1.48.0/crates/goose/src/hooks/mod.rs
|
|
6402
6577
|
* @see https://goose-docs.ai/docs/guides/context-engineering/hooks/
|
|
6403
6578
|
*/
|
|
6404
6579
|
const GOOSE_HOOK_EVENTS = eventsOf(CANONICAL_TO_GOOSE_EVENT_NAMES);
|
|
@@ -6876,134 +7051,6 @@ const GROKCLI_HOOK_EVENTS = eventsOf(CANONICAL_TO_GROKCLI_EVENT_NAMES);
|
|
|
6876
7051
|
* Map Grok CLI PascalCase event names to canonical camelCase.
|
|
6877
7052
|
*/
|
|
6878
7053
|
const GROKCLI_TO_CANONICAL_EVENT_NAMES = invertRecord(CANONICAL_TO_GROKCLI_EVENT_NAMES);
|
|
6879
|
-
// src/utils/jsonc.ts
|
|
6880
|
-
/**
|
|
6881
|
-
* Rebuild the parsed value from its own enumerable entries, dropping
|
|
6882
|
-
* prototype-pollution keys (`__proto__`, `constructor`, `prototype`).
|
|
6883
|
-
* `jsonc-parser` assigns keys with plain `obj[key] = value` semantics, so a
|
|
6884
|
-
* literal `__proto__` key would replace the containing object's prototype
|
|
6885
|
-
* instead of becoming an own property; rebuilding gives every object a clean
|
|
6886
|
-
* `Object.prototype` again and severs that path.
|
|
6887
|
-
*/
|
|
6888
|
-
function deepSanitize(value) {
|
|
6889
|
-
if (Array.isArray(value)) return value.map((item) => deepSanitize(item));
|
|
6890
|
-
if (value !== null && typeof value === "object") {
|
|
6891
|
-
const sanitized = {};
|
|
6892
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
6893
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
|
|
6894
|
-
sanitized[key] = deepSanitize(entry);
|
|
6895
|
-
}
|
|
6896
|
-
return sanitized;
|
|
6897
|
-
}
|
|
6898
|
-
return value;
|
|
6899
|
-
}
|
|
6900
|
-
/**
|
|
6901
|
-
* Collect the paths of every prototype-pollution key in the document, walking
|
|
6902
|
-
* the syntax tree rather than the parsed value.
|
|
6903
|
-
*
|
|
6904
|
-
* The parsed value cannot answer this question for `__proto__`:
|
|
6905
|
-
* `obj["__proto__"] = "deny"` is discarded by the engine outright, and
|
|
6906
|
-
* `obj["__proto__"] = {...}` replaces the prototype instead of adding an own
|
|
6907
|
-
* property — either way the key is already gone by the time
|
|
6908
|
-
* {@link deepSanitize} could look for it, and only the source text still knows
|
|
6909
|
-
* the user wrote it. `constructor` and `prototype` do become own properties and
|
|
6910
|
-
* could be found in the parsed value, but they are collected here too so all
|
|
6911
|
-
* three keys are answered the same way.
|
|
6912
|
-
*
|
|
6913
|
-
* A pollution key's subtree is not descended into, matching
|
|
6914
|
-
* {@link deepSanitize}, which drops the whole value along with the key.
|
|
6915
|
-
*/
|
|
6916
|
-
function collectPollutionKeyPaths({ node, path, found }) {
|
|
6917
|
-
if (node.type === "array") {
|
|
6918
|
-
for (const [index, child] of (node.children ?? []).entries()) collectPollutionKeyPaths({
|
|
6919
|
-
node: child,
|
|
6920
|
-
path: `${path}[${index}]`,
|
|
6921
|
-
found
|
|
6922
|
-
});
|
|
6923
|
-
return;
|
|
6924
|
-
}
|
|
6925
|
-
if (node.type !== "object") return;
|
|
6926
|
-
for (const property of node.children ?? []) {
|
|
6927
|
-
const [keyNode, valueNode] = property.children ?? [];
|
|
6928
|
-
if (typeof keyNode?.value !== "string") continue;
|
|
6929
|
-
const keyPath = path === "" ? keyNode.value : `${path}.${keyNode.value}`;
|
|
6930
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(keyNode.value)) {
|
|
6931
|
-
found.push(keyPath);
|
|
6932
|
-
continue;
|
|
6933
|
-
}
|
|
6934
|
-
if (valueNode !== void 0) collectPollutionKeyPaths({
|
|
6935
|
-
node: valueNode,
|
|
6936
|
-
path: keyPath,
|
|
6937
|
-
found
|
|
6938
|
-
});
|
|
6939
|
-
}
|
|
6940
|
-
}
|
|
6941
|
-
/**
|
|
6942
|
-
* Parse a JSONC (JSON with Comments) document strictly.
|
|
6943
|
-
*
|
|
6944
|
-
* Unlike `jsonc-parser`'s bare `parse` (which silently tolerates syntax
|
|
6945
|
-
* errors and returns a best-effort value), this throws on any parse error so
|
|
6946
|
-
* a malformed source file fails loudly instead of generating half-empty tool
|
|
6947
|
-
* configs. Plain JSON is valid JSONC, so this is a drop-in replacement for
|
|
6948
|
-
* `JSON.parse` on files that may contain comments or trailing commas.
|
|
6949
|
-
*/
|
|
6950
|
-
function parseJsonc(content) {
|
|
6951
|
-
return deepSanitize(parseStrict(content));
|
|
6952
|
-
}
|
|
6953
|
-
/**
|
|
6954
|
-
* The strict parse shared by both entry points. Kept separate from the syntax
|
|
6955
|
-
* tree walk so the many callers that never look at `droppedKeys` do not pay
|
|
6956
|
-
* for building a second representation of the document.
|
|
6957
|
-
*/
|
|
6958
|
-
function parseStrict(content) {
|
|
6959
|
-
const errors = [];
|
|
6960
|
-
const result = parse$1(content, errors, {
|
|
6961
|
-
allowTrailingComma: true,
|
|
6962
|
-
disallowComments: false
|
|
6963
|
-
});
|
|
6964
|
-
if (errors.length > 0) {
|
|
6965
|
-
const details = errors.map((e) => `${printParseErrorCode(e.error)} at offset ${e.offset}`).join(", ");
|
|
6966
|
-
throw new SyntaxError(`Failed to parse JSONC content: ${details}`);
|
|
6967
|
-
}
|
|
6968
|
-
return result;
|
|
6969
|
-
}
|
|
6970
|
-
/**
|
|
6971
|
-
* The error a source document should fail with when
|
|
6972
|
-
* {@link parseJsoncReportingDroppedKeys} reports keys the parser removed.
|
|
6973
|
-
*
|
|
6974
|
-
* Shared by every source that reports them, so the three files a user can
|
|
6975
|
-
* author explain the same removal the same way rather than each inventing its
|
|
6976
|
-
* own wording.
|
|
6977
|
-
*/
|
|
6978
|
-
function droppedPollutionKeysError({ sourcePath, droppedKeys }) {
|
|
6979
|
-
return /* @__PURE__ */ new Error(`${JSON.stringify(sourcePath)} uses ${droppedKeys.map((key) => JSON.stringify(key)).join(", ")} as ${droppedKeys.length === 1 ? "a key" : "keys"}. Rulesync removes __proto__, constructor and prototype from every source document it parses, because assigning them would reach the prototype chain instead of the object. They are therefore never written to any tool's config — rename them rather than leaving entries that silently do nothing.`);
|
|
6980
|
-
}
|
|
6981
|
-
/**
|
|
6982
|
-
* The same parse as {@link parseJsonc}, additionally reporting which
|
|
6983
|
-
* prototype-pollution keys were removed, as dotted paths
|
|
6984
|
-
* (`permission.bash.__proto__`).
|
|
6985
|
-
*
|
|
6986
|
-
* The parse itself is identical — the paths only let a caller tell the user
|
|
6987
|
-
* about entries that vanished, which they would otherwise have to notice by
|
|
6988
|
-
* comparing their source file against the generated output.
|
|
6989
|
-
*/
|
|
6990
|
-
function parseJsoncReportingDroppedKeys({ content }) {
|
|
6991
|
-
const result = parseStrict(content);
|
|
6992
|
-
const found = [];
|
|
6993
|
-
const tree = parseTree(content, [], {
|
|
6994
|
-
allowTrailingComma: true,
|
|
6995
|
-
disallowComments: false
|
|
6996
|
-
});
|
|
6997
|
-
if (tree !== void 0) collectPollutionKeyPaths({
|
|
6998
|
-
node: tree,
|
|
6999
|
-
path: "",
|
|
7000
|
-
found
|
|
7001
|
-
});
|
|
7002
|
-
return {
|
|
7003
|
-
value: deepSanitize(result),
|
|
7004
|
-
droppedKeys: [...new Set(found)]
|
|
7005
|
-
};
|
|
7006
|
-
}
|
|
7007
7054
|
// src/utils/rulesync-source-path.ts
|
|
7008
7055
|
/**
|
|
7009
7056
|
* No `.rulesync/` source file exists for a feature at any of its candidate
|
|
@@ -9676,11 +9723,9 @@ var RulesyncPermissions = class RulesyncPermissions extends RulesyncFile {
|
|
|
9676
9723
|
* `PERMISSION_OVERRIDE_KEY_ALIASES`) is replaced wholesale, and removed when
|
|
9677
9724
|
* the import does not produce one: only that tool's import ever writes it, so
|
|
9678
9725
|
* a setting removed from the tool's own config has to disappear here too.
|
|
9679
|
-
*
|
|
9680
|
-
*
|
|
9681
|
-
*
|
|
9682
|
-
* `permission` record inside the block merges per category. Every other
|
|
9683
|
-
* top-level key — `$schema` included — is kept unless the import names it.
|
|
9726
|
+
* Every other top-level key — `$schema` included — is kept unless the import
|
|
9727
|
+
* names it, in which case the imported value replaces it. No importer emits
|
|
9728
|
+
* another tool's override block, so those blocks are never touched here.
|
|
9684
9729
|
*/
|
|
9685
9730
|
static mergeImportedJson({ existing, imported, toolTarget }) {
|
|
9686
9731
|
const ownOverrideKey = PERMISSION_OVERRIDE_KEY_ALIASES[toolTarget] ?? toolTarget;
|
|
@@ -9692,14 +9737,7 @@ var RulesyncPermissions = class RulesyncPermissions extends RulesyncFile {
|
|
|
9692
9737
|
existing: current,
|
|
9693
9738
|
imported: value
|
|
9694
9739
|
});
|
|
9695
|
-
else
|
|
9696
|
-
const block = { ...current };
|
|
9697
|
-
for (const [field, fieldValue] of Object.entries(value)) block[field] = field === "permission" ? mergePermissionCategories({
|
|
9698
|
-
existing: block[field],
|
|
9699
|
-
imported: fieldValue
|
|
9700
|
-
}) : fieldValue;
|
|
9701
|
-
merged[key] = block;
|
|
9702
|
-
} else merged[key] = value;
|
|
9740
|
+
else merged[key] = value;
|
|
9703
9741
|
}
|
|
9704
9742
|
return merged;
|
|
9705
9743
|
}
|
|
@@ -19558,9 +19596,6 @@ var KiloCommand = class extends FrontmatterMarkdownToolCommand {
|
|
|
19558
19596
|
static getFrontmatterSchema() {
|
|
19559
19597
|
return KiloCommandFrontmatterSchema;
|
|
19560
19598
|
}
|
|
19561
|
-
getRulesyncOutputRoot() {
|
|
19562
|
-
return process.cwd();
|
|
19563
|
-
}
|
|
19564
19599
|
/**
|
|
19565
19600
|
* Refuse to write a command Kilo reserves (see
|
|
19566
19601
|
* {@link KILO_RESERVED_COMMAND_NAMES}). The name Kilo loads is the file
|
|
@@ -20849,9 +20884,6 @@ var OpenCodeCommand = class extends FrontmatterMarkdownToolCommand {
|
|
|
20849
20884
|
...Object.keys(restFields).length > 0 && { [toolTarget]: restFields }
|
|
20850
20885
|
};
|
|
20851
20886
|
}
|
|
20852
|
-
getRulesyncOutputRoot() {
|
|
20853
|
-
return process.cwd();
|
|
20854
|
-
}
|
|
20855
20887
|
/**
|
|
20856
20888
|
* Imports commands defined inline in `opencode.json` / `opencode.jsonc` (and,
|
|
20857
20889
|
* at project scope, `.opencode/opencode.json(c)`, which OpenCode merges over
|
|
@@ -52366,6 +52398,29 @@ var AmpHooks = class AmpHooks extends ToolHooks {
|
|
|
52366
52398
|
});
|
|
52367
52399
|
}
|
|
52368
52400
|
};
|
|
52401
|
+
// src/features/hooks/hook-matcher-tool-names.ts
|
|
52402
|
+
const MATCHER_TOKEN_PATTERN = /\\.|\[(?:\\.|[^\]\\])*\]|(?<=^|[^\w\\]|\\\w)[A-Za-z_]\w*(?!\w)/gs;
|
|
52403
|
+
function isMatcherName(token) {
|
|
52404
|
+
return !token.startsWith("\\") && !token.startsWith("[");
|
|
52405
|
+
}
|
|
52406
|
+
/**
|
|
52407
|
+
* The distinct whole names in a matcher that start with a capital letter, for
|
|
52408
|
+
* adapters whose runtime tool names are all lowercase: after translation, such
|
|
52409
|
+
* a name has no runtime equivalent and can never match.
|
|
52410
|
+
*/
|
|
52411
|
+
function capitalizedMatcherNames(matcher) {
|
|
52412
|
+
const names = [...matcher.matchAll(MATCHER_TOKEN_PATTERN)].map(([token]) => token).filter((token) => isMatcherName(token) && /^[A-Z]/.test(token));
|
|
52413
|
+
return [...new Set(names)];
|
|
52414
|
+
}
|
|
52415
|
+
/**
|
|
52416
|
+
* Rewrites the whole tool names inside a hook `matcher` regex through `names`,
|
|
52417
|
+
* leaving escapes, character classes and unknown names as written. Used by
|
|
52418
|
+
* adapters whose runtime tool names differ from the canonical (Claude Code)
|
|
52419
|
+
* spelling.
|
|
52420
|
+
*/
|
|
52421
|
+
function replaceMatcherNames(matcher, names) {
|
|
52422
|
+
return matcher.replace(MATCHER_TOKEN_PATTERN, (token) => isMatcherName(token) && Object.hasOwn(names, token) ? names[token] ?? token : token);
|
|
52423
|
+
}
|
|
52369
52424
|
// src/utils/object.ts
|
|
52370
52425
|
/**
|
|
52371
52426
|
* Return a shallow copy of `obj` keeping only the entries whose value is
|
|
@@ -54286,6 +54341,48 @@ const ANTIGRAVITY_FLAT_HANDLER_EVENTS = new Set([...ANTIGRAVITY_NO_MATCHER_EVENT
|
|
|
54286
54341
|
record: CANONICAL_TO_ANTIGRAVITY_EVENT_NAMES,
|
|
54287
54342
|
key: event
|
|
54288
54343
|
}) ?? event));
|
|
54344
|
+
const CANONICAL_TO_ANTIGRAVITY_TOOL_NAMES = {
|
|
54345
|
+
Bash: "run_command",
|
|
54346
|
+
Read: "(?:read_file|view_file)",
|
|
54347
|
+
Write: "(?:write_file|write_to_file)",
|
|
54348
|
+
Edit: "(?:edit_file|replace_file_content)",
|
|
54349
|
+
WebSearch: "search_web"
|
|
54350
|
+
};
|
|
54351
|
+
const ANTIGRAVITY_TO_CANONICAL_TOOL_NAMES = {
|
|
54352
|
+
run_command: "Bash",
|
|
54353
|
+
read_file: "Read",
|
|
54354
|
+
view_file: "Read",
|
|
54355
|
+
write_file: "Write",
|
|
54356
|
+
write_to_file: "Write",
|
|
54357
|
+
edit_file: "Edit",
|
|
54358
|
+
replace_file_content: "Edit",
|
|
54359
|
+
search_web: "WebSearch"
|
|
54360
|
+
};
|
|
54361
|
+
const ANTIGRAVITY_GROUP_TO_CANONICAL_TOOL_NAMES = Object.entries(CANONICAL_TO_ANTIGRAVITY_TOOL_NAMES).filter(([, name]) => name.startsWith("(?:")).map(([canonical, group]) => [group, canonical]);
|
|
54362
|
+
const ANTIGRAVITY_DOCUMENTED_TOOL_CANDIDATES = {
|
|
54363
|
+
Grep: "grep_search",
|
|
54364
|
+
Glob: "find_by_name",
|
|
54365
|
+
WebFetch: "read_url_content",
|
|
54366
|
+
MultiEdit: "multi_replace_file_content"
|
|
54367
|
+
};
|
|
54368
|
+
function canonicalToAntigravityMatcher({ matcher, eventName, warn }) {
|
|
54369
|
+
const translated = replaceMatcherNames(matcher, CANONICAL_TO_ANTIGRAVITY_TOOL_NAMES);
|
|
54370
|
+
const unmatched = capitalizedMatcherNames(translated);
|
|
54371
|
+
if (unmatched.length > 0) {
|
|
54372
|
+
const candidates = unmatched.flatMap((name) => {
|
|
54373
|
+
const candidate = lookupOwn({
|
|
54374
|
+
record: ANTIGRAVITY_DOCUMENTED_TOOL_CANDIDATES,
|
|
54375
|
+
key: name
|
|
54376
|
+
});
|
|
54377
|
+
return candidate ? [`\`${candidate}\` for ${quoteValueForWarning(name)}`] : [];
|
|
54378
|
+
});
|
|
54379
|
+
warn(`Antigravity reports lowercase tool names in the hook payload (\`toolCall.name\`), and ${unmatched.map((name) => quoteValueForWarning(name)).join(", ")} in the "${eventName}" matcher ${quoteValueForWarning(matcher)} has no verified Antigravity equivalent, so it is written as-is and will likely never match. Use the tool name Antigravity reports instead` + (candidates.length > 0 ? ` (the Antigravity hooks docs list ${candidates.join(", ")}, unverified).` : "."));
|
|
54380
|
+
}
|
|
54381
|
+
return translated;
|
|
54382
|
+
}
|
|
54383
|
+
function antigravityToCanonicalMatcher(matcher) {
|
|
54384
|
+
return replaceMatcherNames(ANTIGRAVITY_GROUP_TO_CANONICAL_TOOL_NAMES.reduce((current, [group, canonical]) => current.split(group).join(canonical), matcher), ANTIGRAVITY_TO_CANONICAL_TOOL_NAMES);
|
|
54385
|
+
}
|
|
54289
54386
|
/**
|
|
54290
54387
|
* Antigravity uses a Claude-Code-like `hooks.json`. The `PreToolUse` /
|
|
54291
54388
|
* `PostToolUse` events use the matcher-based group shape, while `SessionStart`,
|
|
@@ -54299,7 +54396,11 @@ const ANTIGRAVITY_CONVERTER_CONFIG = {
|
|
|
54299
54396
|
toolToCanonicalEventNames: ANTIGRAVITY_TO_CANONICAL_EVENT_NAMES,
|
|
54300
54397
|
projectDirVar: "",
|
|
54301
54398
|
noMatcherEvents: ANTIGRAVITY_NO_MATCHER_EVENTS,
|
|
54302
|
-
supportedHookTypes: /* @__PURE__ */ new Set(["command"])
|
|
54399
|
+
supportedHookTypes: /* @__PURE__ */ new Set(["command"]),
|
|
54400
|
+
matcherTranslation: {
|
|
54401
|
+
toTool: canonicalToAntigravityMatcher,
|
|
54402
|
+
toCanonical: antigravityToCanonicalMatcher
|
|
54403
|
+
}
|
|
54303
54404
|
};
|
|
54304
54405
|
/**
|
|
54305
54406
|
* Turn the shared converter's matcher-group output into Antigravity's shape:
|
|
@@ -57332,20 +57433,6 @@ var CopilotcliHooks = class CopilotcliHooks extends ToolHooks {
|
|
|
57332
57433
|
}), null, 2) });
|
|
57333
57434
|
}
|
|
57334
57435
|
};
|
|
57335
|
-
// src/features/hooks/hook-matcher-tool-names.ts
|
|
57336
|
-
const MATCHER_TOKEN_PATTERN = /\\.|\[(?:\\.|[^\]\\])*\]|(?<=^|[^\w\\]|\\\w)[A-Za-z_]\w*(?!\w)/gs;
|
|
57337
|
-
function isMatcherName(token) {
|
|
57338
|
-
return !token.startsWith("\\") && !token.startsWith("[");
|
|
57339
|
-
}
|
|
57340
|
-
/**
|
|
57341
|
-
* Rewrites the whole tool names inside a hook `matcher` regex through `names`,
|
|
57342
|
-
* leaving escapes, character classes and unknown names as written. Used by
|
|
57343
|
-
* adapters whose runtime tool names differ from the canonical (Claude Code)
|
|
57344
|
-
* spelling.
|
|
57345
|
-
*/
|
|
57346
|
-
function replaceMatcherNames(matcher, names) {
|
|
57347
|
-
return matcher.replace(MATCHER_TOKEN_PATTERN, (token) => isMatcherName(token) && Object.hasOwn(names, token) ? names[token] ?? token : token);
|
|
57348
|
-
}
|
|
57349
57436
|
// src/features/hooks/cortexcode-hooks.ts
|
|
57350
57437
|
const CORTEXCODE_NO_MATCHER_EVENTS = /* @__PURE__ */ new Set(["beforeSubmitPrompt", "stop"]);
|
|
57351
57438
|
const CANONICAL_TO_CORTEXCODE_TOOL_NAMES = {
|
|
@@ -57359,8 +57446,8 @@ const CANONICAL_TO_CORTEXCODE_TOOL_NAMES = {
|
|
|
57359
57446
|
const CORTEXCODE_TO_CANONICAL_TOOL_NAMES = invertRecord(CANONICAL_TO_CORTEXCODE_TOOL_NAMES);
|
|
57360
57447
|
function canonicalToCortexcodeMatcher({ matcher, eventName, warn }) {
|
|
57361
57448
|
const translated = replaceMatcherNames(matcher, CANONICAL_TO_CORTEXCODE_TOOL_NAMES);
|
|
57362
|
-
const unmatched =
|
|
57363
|
-
if (unmatched.length > 0) warn(`Cortex Code matches hook matchers case-sensitively against lowercase runtime tool names, and ${
|
|
57449
|
+
const unmatched = capitalizedMatcherNames(translated);
|
|
57450
|
+
if (unmatched.length > 0) warn(`Cortex Code matches hook matchers case-sensitively against lowercase runtime tool names, and ${unmatched.map((name) => quoteValueForWarning(name)).join(", ")} in the "${eventName}" matcher ${quoteValueForWarning(matcher)} is not a tool Cortex Code documents a runtime ID for, so it is written as-is and will likely never match. Replace it with the lowercase name Cortex Code uses for that tool, if it has one.`);
|
|
57364
57451
|
return translated;
|
|
57365
57452
|
}
|
|
57366
57453
|
function cortexcodeToCanonicalMatcher(matcher) {
|
|
@@ -58608,12 +58695,11 @@ var GooseHooks = class GooseHooks extends ToolHooks {
|
|
|
58608
58695
|
});
|
|
58609
58696
|
}
|
|
58610
58697
|
toRulesyncHooks({ logger } = {}) {
|
|
58611
|
-
const parsed = this.parseFileContentOrThrow({
|
|
58612
|
-
displayName: "Goose",
|
|
58613
|
-
parse: (fileContent) => JSON.parse(fileContent)
|
|
58614
|
-
});
|
|
58615
58698
|
const hooks = toolHooksToCanonical({
|
|
58616
|
-
hooks:
|
|
58699
|
+
hooks: this.parseFileContentOrThrow({
|
|
58700
|
+
displayName: "Goose",
|
|
58701
|
+
parse: (fileContent) => JSON.parse(fileContent)
|
|
58702
|
+
}).hooks,
|
|
58617
58703
|
converterConfig: GOOSE_CONVERTER_CONFIG,
|
|
58618
58704
|
logger
|
|
58619
58705
|
});
|
|
@@ -61880,12 +61966,14 @@ var ReasonixHooks = class ReasonixHooks extends ToolHooks {
|
|
|
61880
61966
|
const paths = ReasonixHooks.getSettablePaths({ global });
|
|
61881
61967
|
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
61882
61968
|
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
61883
|
-
let
|
|
61969
|
+
let parsed;
|
|
61884
61970
|
try {
|
|
61885
|
-
|
|
61971
|
+
parsed = JSON.parse(existingContent);
|
|
61886
61972
|
} catch (error) {
|
|
61887
61973
|
throw new Error(`Failed to parse existing Reasonix settings at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
61888
61974
|
}
|
|
61975
|
+
if (!isPlainObject(parsed)) throw new Error(`Failed to parse existing Reasonix settings at ${filePath}: expected a mapping at the root`);
|
|
61976
|
+
const settings = parsed;
|
|
61889
61977
|
const config = rulesyncHooks.getJson();
|
|
61890
61978
|
const reasonixHooks = canonicalToReasonixHooks({
|
|
61891
61979
|
config,
|
|
@@ -62844,15 +62932,18 @@ function emittedEventsWithHook(params) {
|
|
|
62844
62932
|
}
|
|
62845
62933
|
/**
|
|
62846
62934
|
* The capabilities an adapter built on the shared hooks converter already
|
|
62847
|
-
* declares in its converter config: the events it emits
|
|
62848
|
-
* keeps
|
|
62849
|
-
*
|
|
62935
|
+
* declares in its converter config: the events it emits, the hook types it
|
|
62936
|
+
* keeps, and whether it writes the per-hook `enabled` flag. Reading them from
|
|
62937
|
+
* there keeps the processor's skip and dropped-field warnings in step with
|
|
62938
|
+
* what the converter actually writes.
|
|
62850
62939
|
*/
|
|
62851
62940
|
function converterCapabilities(config) {
|
|
62852
62941
|
if (!config.supportedHookTypes) throw new Error("Hooks converter config must declare its supportedHookTypes");
|
|
62942
|
+
const passesThroughEnabled = (config.booleanPassthroughFields ?? []).some((field) => field.canonical === "enabled");
|
|
62853
62943
|
return {
|
|
62854
62944
|
supportedEvents: config.supportedEvents,
|
|
62855
|
-
supportedHookTypes: [...config.supportedHookTypes]
|
|
62945
|
+
supportedHookTypes: [...config.supportedHookTypes],
|
|
62946
|
+
...passesThroughEnabled ? { supportsPerHookEnabled: true } : {}
|
|
62856
62947
|
};
|
|
62857
62948
|
}
|
|
62858
62949
|
const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
@@ -63474,8 +63565,7 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
63474
63565
|
supportsImport: true
|
|
63475
63566
|
},
|
|
63476
63567
|
...converterCapabilities(ZCODE_CONVERTER_CONFIG),
|
|
63477
|
-
supportsMatcher: true
|
|
63478
|
-
supportsPerHookEnabled: true
|
|
63568
|
+
supportsMatcher: true
|
|
63479
63569
|
}],
|
|
63480
63570
|
["zcode-plugin", {
|
|
63481
63571
|
class: ZcodePluginHooks,
|
|
@@ -63485,10 +63575,12 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
63485
63575
|
supportsImport: true
|
|
63486
63576
|
},
|
|
63487
63577
|
...converterCapabilities(ZCODE_PLUGIN_CONVERTER_CONFIG),
|
|
63488
|
-
supportsMatcher: true
|
|
63489
|
-
supportsPerHookEnabled: true
|
|
63578
|
+
supportsMatcher: true
|
|
63490
63579
|
}]
|
|
63491
63580
|
]);
|
|
63581
|
+
function targetsSupporting(capability) {
|
|
63582
|
+
return [...toolHooksFactories.entries()].filter(([, f]) => f[capability]).map(([t]) => t).join(" / ");
|
|
63583
|
+
}
|
|
63492
63584
|
const hooksProcessorToolTargets = [...toolHooksFactories.entries()].filter(([, f]) => f.meta.supportsProject).map(([t]) => t);
|
|
63493
63585
|
const hooksProcessorToolTargetsGlobal = [...toolHooksFactories.entries()].filter(([, f]) => f.meta.supportsGlobal).map(([t]) => t);
|
|
63494
63586
|
const hooksProcessorToolTargetsImportable = [...toolHooksFactories.entries()].filter(([, f]) => f.meta.supportsProject && f.meta.supportsImport).map(([t]) => t);
|
|
@@ -63649,7 +63741,7 @@ var HooksProcessor = class extends FeatureProcessor {
|
|
|
63649
63741
|
effectiveHooks,
|
|
63650
63742
|
predicate: (def) => def.enabled === false
|
|
63651
63743
|
});
|
|
63652
|
-
if (eventsWithDisabledHooks.length > 0) this.logger.warn(`Emitting "enabled: false" hook(s) as active for ${this.toolTarget} (only the
|
|
63744
|
+
if (eventsWithDisabledHooks.length > 0) this.logger.warn(`Emitting "enabled: false" hook(s) as active for ${this.toolTarget} (only the ${targetsSupporting("supportsPerHookEnabled")} hooks formats support the flag): ${eventsWithDisabledHooks.join(", ")}`);
|
|
63653
63745
|
}
|
|
63654
63746
|
if (!factory.supportsPerHookConfirm) {
|
|
63655
63747
|
const eventsWithConfirmHooks = emittedEventsWithHook({
|
|
@@ -63658,7 +63750,7 @@ var HooksProcessor = class extends FeatureProcessor {
|
|
|
63658
63750
|
effectiveHooks,
|
|
63659
63751
|
predicate: (def) => def.confirm !== void 0
|
|
63660
63752
|
});
|
|
63661
|
-
if (eventsWithConfirmHooks.length > 0) this.logger.warn(`Emitting hook(s) without their "confirm" prompt for ${this.toolTarget} (only the
|
|
63753
|
+
if (eventsWithConfirmHooks.length > 0) this.logger.warn(`Emitting hook(s) without their "confirm" prompt for ${this.toolTarget} (only the ${targetsSupporting("supportsPerHookConfirm")} hooks formats support it): ${eventsWithConfirmHooks.join(", ")}`);
|
|
63662
63754
|
}
|
|
63663
63755
|
}
|
|
63664
63756
|
async convertToolFilesToRulesyncFiles(toolFiles) {
|
|
@@ -64921,12 +65013,7 @@ var ReasonixIgnore = class ReasonixIgnore extends ToolIgnore {
|
|
|
64921
65013
|
}
|
|
64922
65014
|
toRulesyncIgnore() {
|
|
64923
65015
|
const rulesyncPatterns = uniq(this.patterns.map((entry) => readDenyPatternOf(entry)).filter((pattern) => pattern !== null && pattern.length > 0));
|
|
64924
|
-
return
|
|
64925
|
-
outputRoot: this.outputRoot,
|
|
64926
|
-
relativeDirPath: RulesyncIgnore.getSettablePaths().recommended.relativeDirPath,
|
|
64927
|
-
relativeFilePath: RulesyncIgnore.getSettablePaths().recommended.relativeFilePath,
|
|
64928
|
-
fileContent: rulesyncPatterns.join("\n")
|
|
64929
|
-
});
|
|
65016
|
+
return this.toRulesyncIgnoreDefault({ fileContent: rulesyncPatterns.join("\n") });
|
|
64930
65017
|
}
|
|
64931
65018
|
static async fromRulesyncIgnore({ outputRoot = process.cwd(), rulesyncIgnore, global = false, logger }) {
|
|
64932
65019
|
await warnIfReasonixNestedProjectConfigShadowed({
|
|
@@ -78633,11 +78720,17 @@ function convertAmpToRulesync({ disable, permissions }) {
|
|
|
78633
78720
|
const assign = (tool, pattern, action) => {
|
|
78634
78721
|
if (isPrototypePollutionKey(tool)) return;
|
|
78635
78722
|
if (isPrototypePollutionKey(pattern)) return;
|
|
78636
|
-
const bucket =
|
|
78637
|
-
|
|
78723
|
+
const bucket = getOrCreateOwn({
|
|
78724
|
+
record: permission,
|
|
78725
|
+
key: tool,
|
|
78726
|
+
create: () => ({})
|
|
78727
|
+
});
|
|
78638
78728
|
if (isStricterAction({
|
|
78639
78729
|
action,
|
|
78640
|
-
existing
|
|
78730
|
+
existing: lookupOwn({
|
|
78731
|
+
record: bucket,
|
|
78732
|
+
key: pattern
|
|
78733
|
+
})
|
|
78641
78734
|
})) bucket[pattern] = action;
|
|
78642
78735
|
};
|
|
78643
78736
|
for (const tool of disable) assign(tool, "*", "deny");
|
|
@@ -80040,8 +80133,12 @@ function convertAntigravityToRulesyncPermissions({ allow, ask, deny, toCategory,
|
|
|
80040
80133
|
logger,
|
|
80041
80134
|
toolLabel
|
|
80042
80135
|
}) : target;
|
|
80043
|
-
if (
|
|
80044
|
-
|
|
80136
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(pattern)) continue;
|
|
80137
|
+
getOrCreateOwn({
|
|
80138
|
+
record: permission,
|
|
80139
|
+
key: canonical,
|
|
80140
|
+
create: () => ({})
|
|
80141
|
+
})[pattern] = action;
|
|
80045
80142
|
}
|
|
80046
80143
|
};
|
|
80047
80144
|
processEntries(allow, "allow");
|
|
@@ -80109,10 +80206,16 @@ const ANTIGRAVITY_CLI_TO_CANONICAL_TOOL_NAMES = {
|
|
|
80109
80206
|
};
|
|
80110
80207
|
function toAntigravityCliToolName(canonical) {
|
|
80111
80208
|
if (canonical.startsWith("mcp__")) return "mcp";
|
|
80112
|
-
return
|
|
80209
|
+
return lookupOwn({
|
|
80210
|
+
record: CANONICAL_TO_ANTIGRAVITY_CLI_TOOL_NAMES,
|
|
80211
|
+
key: canonical
|
|
80212
|
+
}) ?? canonical;
|
|
80113
80213
|
}
|
|
80114
80214
|
function toCanonicalToolName$9(cliName) {
|
|
80115
|
-
return
|
|
80215
|
+
return lookupOwn({
|
|
80216
|
+
record: ANTIGRAVITY_CLI_TO_CANONICAL_TOOL_NAMES,
|
|
80217
|
+
key: cliName
|
|
80218
|
+
}) ?? cliName;
|
|
80116
80219
|
}
|
|
80117
80220
|
/**
|
|
80118
80221
|
* Permissions generator for the Google Antigravity CLI (`agy`, Antigravity 2.0).
|
|
@@ -80275,10 +80378,16 @@ const IDE_ACTION_TO_CANONICAL = {
|
|
|
80275
80378
|
};
|
|
80276
80379
|
function toIdeAction(canonical) {
|
|
80277
80380
|
if (canonical.startsWith("mcp__")) return "mcp";
|
|
80278
|
-
return
|
|
80381
|
+
return lookupOwn({
|
|
80382
|
+
record: CANONICAL_TO_IDE_ACTION,
|
|
80383
|
+
key: canonical
|
|
80384
|
+
}) ?? canonical;
|
|
80279
80385
|
}
|
|
80280
80386
|
function toCanonicalCategory$2(ideAction) {
|
|
80281
|
-
return
|
|
80387
|
+
return lookupOwn({
|
|
80388
|
+
record: IDE_ACTION_TO_CANONICAL,
|
|
80389
|
+
key: ideAction
|
|
80390
|
+
}) ?? ideAction;
|
|
80282
80391
|
}
|
|
80283
80392
|
/**
|
|
80284
80393
|
* Permissions generator for the Google Antigravity IDE (Antigravity 2.0).
|
|
@@ -81817,10 +81926,16 @@ const CANONICAL_TO_CLAUDE_TOOL_NAMES = {
|
|
|
81817
81926
|
*/
|
|
81818
81927
|
const CLAUDE_TO_CANONICAL_TOOL_NAMES = invertRecord(CANONICAL_TO_CLAUDE_TOOL_NAMES);
|
|
81819
81928
|
function toClaudeToolName(canonical) {
|
|
81820
|
-
return
|
|
81929
|
+
return lookupOwn({
|
|
81930
|
+
record: CANONICAL_TO_CLAUDE_TOOL_NAMES,
|
|
81931
|
+
key: canonical
|
|
81932
|
+
}) ?? canonical;
|
|
81821
81933
|
}
|
|
81822
81934
|
function toCanonicalToolName$7(claudeName) {
|
|
81823
|
-
return
|
|
81935
|
+
return lookupOwn({
|
|
81936
|
+
record: CLAUDE_TO_CANONICAL_TOOL_NAMES,
|
|
81937
|
+
key: claudeName
|
|
81938
|
+
}) ?? claudeName;
|
|
81824
81939
|
}
|
|
81825
81940
|
/**
|
|
81826
81941
|
* Merge `patch` into `base`, recursing into plain objects so a sibling key at
|
|
@@ -82792,7 +82907,10 @@ const CLAUDE_PATH_RULE_ALIASES = {
|
|
|
82792
82907
|
*/
|
|
82793
82908
|
function buildClaudePermissionEntry(toolName, pattern) {
|
|
82794
82909
|
if (pattern === "*") return toolName;
|
|
82795
|
-
return `${
|
|
82910
|
+
return `${lookupOwn({
|
|
82911
|
+
record: CLAUDE_PATH_RULE_ALIASES,
|
|
82912
|
+
key: toolName
|
|
82913
|
+
}) ?? toolName}(${pattern})`;
|
|
82796
82914
|
}
|
|
82797
82915
|
/**
|
|
82798
82916
|
* The Claude tool names the canonical config manages. Deliberately the tool
|
|
@@ -83030,8 +83148,12 @@ function convertClaudeToRulesyncPermissions(params) {
|
|
|
83030
83148
|
for (const entry of entries) {
|
|
83031
83149
|
const { toolName, pattern } = parseToolPatternEntry(entry);
|
|
83032
83150
|
const canonical = toCanonicalToolName$7(toolName);
|
|
83033
|
-
if (
|
|
83034
|
-
|
|
83151
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(pattern)) continue;
|
|
83152
|
+
getOrCreateOwn({
|
|
83153
|
+
record: permission,
|
|
83154
|
+
key: canonical,
|
|
83155
|
+
create: () => ({})
|
|
83156
|
+
})[pattern] = action;
|
|
83035
83157
|
}
|
|
83036
83158
|
};
|
|
83037
83159
|
processEntries(params.allow, "allow");
|
|
@@ -83613,11 +83735,17 @@ var CodebuddyPermissions = class CodebuddyPermissions extends ToolPermissions {
|
|
|
83613
83735
|
for (const action of CODEBUDDY_RULE_LISTS) for (const rule of toStringArray$1(permissions[action])) {
|
|
83614
83736
|
const parsed = parseCodebuddyRule(rule);
|
|
83615
83737
|
if (parsed === null || isPrototypePollutionKey(parsed.category) || isPrototypePollutionKey(parsed.pattern)) continue;
|
|
83616
|
-
const rules =
|
|
83617
|
-
|
|
83738
|
+
const rules = getOrCreateOwn({
|
|
83739
|
+
record: permission,
|
|
83740
|
+
key: parsed.category,
|
|
83741
|
+
create: () => ({})
|
|
83742
|
+
});
|
|
83618
83743
|
if (isStricterAction({
|
|
83619
83744
|
action,
|
|
83620
|
-
existing
|
|
83745
|
+
existing: lookupOwn({
|
|
83746
|
+
record: rules,
|
|
83747
|
+
key: parsed.pattern
|
|
83748
|
+
})
|
|
83621
83749
|
})) rules[parsed.pattern] = action;
|
|
83622
83750
|
}
|
|
83623
83751
|
const codebuddy = liftCodebuddyOverride({
|
|
@@ -84127,7 +84255,10 @@ const CODEWHALE_WRITE_RESTRICTION_TOOLS = ["write_file", "apply_patch"];
|
|
|
84127
84255
|
/** The Codewhale tools a canonical path rule is written for. */
|
|
84128
84256
|
function pathCategoryTools(category, action) {
|
|
84129
84257
|
if (category === "write" && action !== "allow") return CODEWHALE_WRITE_RESTRICTION_TOOLS;
|
|
84130
|
-
return
|
|
84258
|
+
return lookupOwn({
|
|
84259
|
+
record: CODEWHALE_PATH_CATEGORY_TOOLS,
|
|
84260
|
+
key: category
|
|
84261
|
+
}) ?? [];
|
|
84131
84262
|
}
|
|
84132
84263
|
/** The canonical path categories a Codewhale file tool's rule can come from. */
|
|
84133
84264
|
const CODEWHALE_TOOL_TO_PATH_CATEGORIES = {
|
|
@@ -84446,7 +84577,10 @@ function canonicalToCodewhaleRules({ config, preservedRules, logger }) {
|
|
|
84446
84577
|
});
|
|
84447
84578
|
for (const [category, rules] of Object.entries(config.permission)) {
|
|
84448
84579
|
if (category === "bash" || category === "*") continue;
|
|
84449
|
-
if (
|
|
84580
|
+
if (lookupOwn({
|
|
84581
|
+
record: CODEWHALE_PATH_CATEGORY_TOOLS,
|
|
84582
|
+
key: category
|
|
84583
|
+
}) === void 0) {
|
|
84450
84584
|
if (Object.keys(rules).length > 0) logger?.warn(`Codewhale permissions: skipping unsupported category "${category}"; Codewhale evaluates permission rules only for shell commands and file tools.`);
|
|
84451
84585
|
continue;
|
|
84452
84586
|
}
|
|
@@ -84556,7 +84690,10 @@ function toImportCandidates(rule) {
|
|
|
84556
84690
|
}
|
|
84557
84691
|
const pattern = pathRuleToPattern(rule);
|
|
84558
84692
|
if (pattern === null) return [];
|
|
84559
|
-
return (
|
|
84693
|
+
return (lookupOwn({
|
|
84694
|
+
record: CODEWHALE_TOOL_TO_PATH_CATEGORIES,
|
|
84695
|
+
key: rule.tool
|
|
84696
|
+
}) ?? []).map((category) => ({
|
|
84560
84697
|
category,
|
|
84561
84698
|
pattern,
|
|
84562
84699
|
rule
|
|
@@ -86164,10 +86301,17 @@ function parseCommandcodeRuleLists(permissions) {
|
|
|
86164
86301
|
rule,
|
|
86165
86302
|
category
|
|
86166
86303
|
})) fallbackLogger.warn(`Command Code matches '${rule}' in "${action}" against MCP tool names case-insensitively, but the '${category}' rule generated from it matches the server and tool names exactly as written; spell the rule the way the tool is registered if they differ.`);
|
|
86167
|
-
const rules =
|
|
86304
|
+
const rules = getOrCreateOwn({
|
|
86305
|
+
record: permission,
|
|
86306
|
+
key: category,
|
|
86307
|
+
create: () => ({})
|
|
86308
|
+
});
|
|
86168
86309
|
if (isStricterAction({
|
|
86169
86310
|
action,
|
|
86170
|
-
existing:
|
|
86311
|
+
existing: lookupOwn({
|
|
86312
|
+
record: rules,
|
|
86313
|
+
key: pattern
|
|
86314
|
+
})
|
|
86171
86315
|
})) rules[pattern] = action;
|
|
86172
86316
|
}
|
|
86173
86317
|
}
|
|
@@ -86528,9 +86672,12 @@ function convertContinuePermissionsToRulesync(lists) {
|
|
|
86528
86672
|
const parsedEntry = parseContinuePermissionEntry(entry);
|
|
86529
86673
|
if (parsedEntry === void 0) continue;
|
|
86530
86674
|
if (isPrototypePollutionKey(parsedEntry.toolName) || isPrototypePollutionKey(parsedEntry.pattern)) continue;
|
|
86531
|
-
const
|
|
86532
|
-
|
|
86533
|
-
|
|
86675
|
+
const rules = getOrCreateOwn({
|
|
86676
|
+
record: permission,
|
|
86677
|
+
key: toCanonicalToolName$6(parsedEntry.toolName),
|
|
86678
|
+
create: () => ({})
|
|
86679
|
+
});
|
|
86680
|
+
if (!Object.hasOwn(rules, parsedEntry.pattern)) rules[parsedEntry.pattern] = action;
|
|
86534
86681
|
}
|
|
86535
86682
|
}
|
|
86536
86683
|
return { permission };
|
|
@@ -87159,7 +87306,9 @@ var CopilotcliPermissions = class CopilotcliPermissions extends ToolPermissions
|
|
|
87159
87306
|
const settings = this.parseSettings();
|
|
87160
87307
|
const rules = {};
|
|
87161
87308
|
for (const pattern of toUrlList(settings[DENIED_URLS_KEY])) rules[pattern] = "deny";
|
|
87162
|
-
if (this.global)
|
|
87309
|
+
if (this.global) {
|
|
87310
|
+
for (const pattern of toUrlList(settings[ALLOWED_URLS_KEY])) if (!Object.hasOwn(rules, pattern)) rules[pattern] = "allow";
|
|
87311
|
+
}
|
|
87163
87312
|
const permission = Object.keys(rules).length > 0 ? { [WEBFETCH_CATEGORY$1]: rules } : {};
|
|
87164
87313
|
const defaultPermissionMode = this.global ? settings[DEFAULT_PERMISSION_MODE_KEY] : void 0;
|
|
87165
87314
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({
|
|
@@ -87506,7 +87655,10 @@ function isMcpScopedCategory(canonical) {
|
|
|
87506
87655
|
}
|
|
87507
87656
|
function toCursorType(canonical) {
|
|
87508
87657
|
if (isMcpScopedCategory(canonical)) return "Mcp";
|
|
87509
|
-
return
|
|
87658
|
+
return lookupOwn({
|
|
87659
|
+
record: CANONICAL_TO_CURSOR_TYPE,
|
|
87660
|
+
key: canonical
|
|
87661
|
+
}) ?? canonical;
|
|
87510
87662
|
}
|
|
87511
87663
|
/**
|
|
87512
87664
|
* For per-tool MCP canonical categories like `mcp__puppeteer__navigate`, the
|
|
@@ -87538,9 +87690,15 @@ function toCanonicalCategory$1(cursorType, pattern) {
|
|
|
87538
87690
|
const tool = match[2] ?? "*";
|
|
87539
87691
|
return `${MCP_CATEGORY_PREFIX}${server}__${tool}`;
|
|
87540
87692
|
}
|
|
87541
|
-
return
|
|
87693
|
+
return lookupOwn({
|
|
87694
|
+
record: CURSOR_TYPE_TO_CANONICAL,
|
|
87695
|
+
key: cursorType
|
|
87696
|
+
}) ?? cursorType.toLowerCase();
|
|
87542
87697
|
}
|
|
87543
|
-
return
|
|
87698
|
+
return lookupOwn({
|
|
87699
|
+
record: CURSOR_TYPE_TO_CANONICAL,
|
|
87700
|
+
key: cursorType
|
|
87701
|
+
}) ?? cursorType.toLowerCase();
|
|
87544
87702
|
}
|
|
87545
87703
|
/**
|
|
87546
87704
|
* Narrow `JSON.parse` output to a plain object before treating it as a Cursor
|
|
@@ -87785,9 +87943,13 @@ function convertCursorToRulesyncPermissions(params) {
|
|
|
87785
87943
|
for (const entry of entries) {
|
|
87786
87944
|
const { toolName: type, pattern } = parseToolPatternEntry(entry);
|
|
87787
87945
|
const canonical = toCanonicalCategory$1(type, pattern);
|
|
87788
|
-
if (!permission[canonical]) permission[canonical] = {};
|
|
87789
87946
|
const canonicalPattern = type === "Mcp" && canonical.startsWith("mcp__") ? "*" : pattern;
|
|
87790
|
-
|
|
87947
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(canonicalPattern)) continue;
|
|
87948
|
+
getOrCreateOwn({
|
|
87949
|
+
record: permission,
|
|
87950
|
+
key: canonical,
|
|
87951
|
+
create: () => ({})
|
|
87952
|
+
})[canonicalPattern] = action;
|
|
87791
87953
|
}
|
|
87792
87954
|
};
|
|
87793
87955
|
processEntries(params.allow, "allow");
|
|
@@ -88714,10 +88876,16 @@ const DEVIN_SCOPE_TO_CANONICAL = {
|
|
|
88714
88876
|
*/
|
|
88715
88877
|
const DEVIN_BARE_ONLY_SCOPES = /* @__PURE__ */ new Set(["web_search"]);
|
|
88716
88878
|
function toDevinScope(canonical) {
|
|
88717
|
-
return
|
|
88879
|
+
return lookupOwn({
|
|
88880
|
+
record: CANONICAL_TO_DEVIN_SCOPE,
|
|
88881
|
+
key: canonical
|
|
88882
|
+
}) ?? canonical;
|
|
88718
88883
|
}
|
|
88719
88884
|
function toCanonicalCategory(devinScope) {
|
|
88720
|
-
return
|
|
88885
|
+
return lookupOwn({
|
|
88886
|
+
record: DEVIN_SCOPE_TO_CANONICAL,
|
|
88887
|
+
key: devinScope
|
|
88888
|
+
}) ?? devinScope;
|
|
88721
88889
|
}
|
|
88722
88890
|
function asDevinRecord(value) {
|
|
88723
88891
|
return isRecord(value) ? { ...value } : {};
|
|
@@ -89069,7 +89237,11 @@ function convertDevinToRulesyncPermissions(params) {
|
|
|
89069
89237
|
const { toolName: scope, pattern } = parseToolPatternEntry(entry);
|
|
89070
89238
|
if (isPrototypePollutionKey(scope) || isPrototypePollutionKey(pattern)) continue;
|
|
89071
89239
|
const canonical = toCanonicalCategory(scope);
|
|
89072
|
-
(
|
|
89240
|
+
getOrCreateOwn({
|
|
89241
|
+
record: permission,
|
|
89242
|
+
key: canonical,
|
|
89243
|
+
create: () => ({})
|
|
89244
|
+
})[pattern] = action;
|
|
89073
89245
|
}
|
|
89074
89246
|
};
|
|
89075
89247
|
processEntries(params.allow, "allow");
|
|
@@ -89375,7 +89547,10 @@ function convertRulesyncToGoosePermissionConfig({ config, logger }) {
|
|
|
89375
89547
|
};
|
|
89376
89548
|
const assigned = /* @__PURE__ */ new Map();
|
|
89377
89549
|
for (const [category, rules] of Object.entries(honorAllToolsOnBash(config.permission))) {
|
|
89378
|
-
const toolName =
|
|
89550
|
+
const toolName = lookupOwn({
|
|
89551
|
+
record: RULESYNC_TO_GOOSE_TOOL_NAME,
|
|
89552
|
+
key: category
|
|
89553
|
+
}) ?? category;
|
|
89379
89554
|
for (const [pattern, action] of Object.entries(rules)) if (pattern !== "*") logger?.warn(`Goose permission.yaml lists whole tool names, so the per-pattern "${action}" rule for "${category}" (pattern "${pattern}") cannot be expressed and was skipped.`);
|
|
89380
89555
|
const { action, downgraded } = failSafeCatchAllAction({
|
|
89381
89556
|
rules,
|
|
@@ -89407,9 +89582,16 @@ function convertGoosePermissionConfigToRulesync(userPermission) {
|
|
|
89407
89582
|
for (const key of GOOSE_PERMISSION_LIST_KEYS) {
|
|
89408
89583
|
const toolNames = isStringArray(userPermission[key]) ? userPermission[key] : [];
|
|
89409
89584
|
const action = GOOSE_LIST_TO_ACTION[key];
|
|
89410
|
-
for (const toolName of toolNames) for (const category of
|
|
89411
|
-
|
|
89412
|
-
|
|
89585
|
+
for (const toolName of toolNames) for (const category of lookupOwn({
|
|
89586
|
+
record: GOOSE_TO_RULESYNC_TOOL_NAMES,
|
|
89587
|
+
key: toolName
|
|
89588
|
+
}) ?? [toolName]) {
|
|
89589
|
+
if (isPrototypePollutionKey(category)) continue;
|
|
89590
|
+
getOrCreateOwn({
|
|
89591
|
+
record: permission,
|
|
89592
|
+
key: category,
|
|
89593
|
+
create: () => ({})
|
|
89594
|
+
})["*"] = action;
|
|
89413
89595
|
}
|
|
89414
89596
|
}
|
|
89415
89597
|
return { permission };
|
|
@@ -89490,7 +89672,10 @@ function buildGrokEntry(category, pattern) {
|
|
|
89490
89672
|
return remainder.length > 0 ? `${GROK_MCP_TOOL}(${remainder})` : GROK_MCP_TOOL;
|
|
89491
89673
|
}
|
|
89492
89674
|
if (category === "mcp") return pattern === "*" || pattern === "" ? GROK_MCP_TOOL : `${GROK_MCP_TOOL}(${pattern})`;
|
|
89493
|
-
const tool =
|
|
89675
|
+
const tool = lookupOwn({
|
|
89676
|
+
record: CATEGORY_TO_GROK_TOOL,
|
|
89677
|
+
key: category
|
|
89678
|
+
});
|
|
89494
89679
|
if (tool === void 0) return null;
|
|
89495
89680
|
return pattern === "*" || pattern === "" ? tool : `${tool}(${toGrokPathPattern(category, pattern)})`;
|
|
89496
89681
|
}
|
|
@@ -89524,7 +89709,10 @@ function parseGrokEntry(entry) {
|
|
|
89524
89709
|
category: "mcp",
|
|
89525
89710
|
pattern: "*"
|
|
89526
89711
|
};
|
|
89527
|
-
const category =
|
|
89712
|
+
const category = lookupOwn({
|
|
89713
|
+
record: GROK_TOOL_TO_CATEGORY,
|
|
89714
|
+
key: tool
|
|
89715
|
+
});
|
|
89528
89716
|
if (category === void 0) return null;
|
|
89529
89717
|
return {
|
|
89530
89718
|
category,
|
|
@@ -89845,11 +90033,17 @@ function parseGrokPermissionArrays(permission) {
|
|
|
89845
90033
|
if ((allow?.length ?? 0) + (deny?.length ?? 0) + (ask?.length ?? 0) + (rules?.length ?? 0) === 0) return null;
|
|
89846
90034
|
const result = {};
|
|
89847
90035
|
const record = ({ category, pattern, action }) => {
|
|
89848
|
-
const bucket =
|
|
89849
|
-
|
|
90036
|
+
const bucket = getOrCreateOwn({
|
|
90037
|
+
record: result,
|
|
90038
|
+
key: category,
|
|
90039
|
+
create: () => ({})
|
|
90040
|
+
});
|
|
89850
90041
|
if (isStricterAction({
|
|
89851
90042
|
action,
|
|
89852
|
-
existing
|
|
90043
|
+
existing: lookupOwn({
|
|
90044
|
+
record: bucket,
|
|
90045
|
+
key: pattern
|
|
90046
|
+
})
|
|
89853
90047
|
})) bucket[pattern] = action;
|
|
89854
90048
|
};
|
|
89855
90049
|
const applyEntries = (entries, action) => {
|
|
@@ -89906,7 +90100,11 @@ function deleteRulesByAction(rules, action) {
|
|
|
89906
90100
|
for (const [pattern, currentAction] of Object.entries(rules)) if (currentAction === action) delete rules[pattern];
|
|
89907
90101
|
}
|
|
89908
90102
|
function ensureCategory(permission, category) {
|
|
89909
|
-
return
|
|
90103
|
+
return getOrCreateOwn({
|
|
90104
|
+
record: permission,
|
|
90105
|
+
key: category,
|
|
90106
|
+
create: () => ({})
|
|
90107
|
+
});
|
|
89910
90108
|
}
|
|
89911
90109
|
function removeEmptyCategories(permission) {
|
|
89912
90110
|
for (const [category, rules] of Object.entries(permission)) if (Object.keys(rules).length === 0) delete permission[category];
|
|
@@ -90087,6 +90285,22 @@ function unmanagedMcpAllowlistEntries({ existing, previousPermission, nextPermis
|
|
|
90087
90285
|
return category === null || !Object.hasOwn(previousPermission, category) && !Object.hasOwn(nextPermission, category);
|
|
90088
90286
|
});
|
|
90089
90287
|
}
|
|
90288
|
+
/** Hermes's override key in the canonical document (`hermes`). */
|
|
90289
|
+
const HERMES_OVERRIDE_KEY = PERMISSION_OVERRIDE_KEY_ALIASES.hermesagent ?? "hermesagent";
|
|
90290
|
+
/**
|
|
90291
|
+
* The part of a canonical permissions document Hermes keeps as provenance: the
|
|
90292
|
+
* `$schema`, the canonical `permission` block, and Hermes's own override.
|
|
90293
|
+
*/
|
|
90294
|
+
function hermesProvenanceOf(permissions) {
|
|
90295
|
+
const source = permissions;
|
|
90296
|
+
const provenance = {};
|
|
90297
|
+
for (const key of [
|
|
90298
|
+
"$schema",
|
|
90299
|
+
"permission",
|
|
90300
|
+
HERMES_OVERRIDE_KEY
|
|
90301
|
+
]) if (source[key] !== void 0) provenance[key] = source[key];
|
|
90302
|
+
return provenance;
|
|
90303
|
+
}
|
|
90090
90304
|
/** The validated canonical `permission` block of a config's `permissions.rulesync` blob. */
|
|
90091
90305
|
function canonicalPermissionOf(config) {
|
|
90092
90306
|
const parsed = RulesyncPermissionsFileSchema.safeParse({ permission: provenancePermissionOf(config) });
|
|
@@ -90254,10 +90468,10 @@ var HermesagentPermissions = class HermesagentPermissions extends ToolPermission
|
|
|
90254
90468
|
patterns: websiteBlocklist.enabled === true && isStringArray(websiteBlocklist.domains) ? websiteBlocklist.domains : []
|
|
90255
90469
|
});
|
|
90256
90470
|
removeEmptyCategories(permission);
|
|
90257
|
-
const { permission: _permission, hermes: _hermes, ...otherProvenance } = provenance;
|
|
90258
90471
|
const hermes = buildHermesOverride(config, provenance);
|
|
90472
|
+
const { $schema } = provenance;
|
|
90259
90473
|
const imported = {
|
|
90260
|
-
|
|
90474
|
+
...$schema !== void 0 && { $schema },
|
|
90261
90475
|
permission,
|
|
90262
90476
|
...Object.keys(hermes).length > 0 && { hermes }
|
|
90263
90477
|
};
|
|
@@ -90315,7 +90529,7 @@ var HermesagentPermissions = class HermesagentPermissions extends ToolPermission
|
|
|
90315
90529
|
base: config,
|
|
90316
90530
|
patch: permissions.hermes
|
|
90317
90531
|
});
|
|
90318
|
-
config.permissions = { rulesync: permissions };
|
|
90532
|
+
config.permissions = { rulesync: hermesProvenanceOf(permissions) };
|
|
90319
90533
|
return new HermesagentPermissions({
|
|
90320
90534
|
outputRoot,
|
|
90321
90535
|
fileContent: stringifySharedConfig({
|
|
@@ -90549,7 +90763,10 @@ var JuniePermissions = class JuniePermissions extends ToolPermissions {
|
|
|
90549
90763
|
function convertRulesyncToJunieRules({ config, logger, existingRules, overrideSecretFile, overrideRuleDefaults }) {
|
|
90550
90764
|
const ruleLists = {};
|
|
90551
90765
|
for (const [category, patterns] of Object.entries(honorAllToolsOnBash(config.permission))) {
|
|
90552
|
-
const group =
|
|
90766
|
+
const group = lookupOwn({
|
|
90767
|
+
record: CANONICAL_TO_JUNIE_GROUP,
|
|
90768
|
+
key: category
|
|
90769
|
+
});
|
|
90553
90770
|
if (!group) {
|
|
90554
90771
|
if (Object.keys(patterns).length > 0) logger?.warn(`Junie allowlist only models executables/fileEditing/mcpTools/readOutsideProject (canonical bash/edit/write/read/mcp); '${category}' rules cannot be represented and were skipped.`);
|
|
90555
90772
|
continue;
|
|
@@ -90947,7 +91164,10 @@ const CATEGORY_TO_KIMI_CODE_TOOL = {
|
|
|
90947
91164
|
function buildKimiCodePattern(category, pattern) {
|
|
90948
91165
|
if (category.startsWith("mcp__")) return pattern === "*" || pattern === "" ? category : null;
|
|
90949
91166
|
if (category === "mcp") return pattern === "*" || pattern === "" ? "mcp__*" : `mcp__${pattern}`;
|
|
90950
|
-
const tool =
|
|
91167
|
+
const tool = lookupOwn({
|
|
91168
|
+
record: CATEGORY_TO_KIMI_CODE_TOOL,
|
|
91169
|
+
key: category
|
|
91170
|
+
});
|
|
90951
91171
|
if (!tool) return null;
|
|
90952
91172
|
return pattern === "*" || pattern === "" ? tool : `${tool}(${pattern})`;
|
|
90953
91173
|
}
|
|
@@ -91043,7 +91263,10 @@ function canonicalToKimiCodeRules({ config, logger }) {
|
|
|
91043
91263
|
const canonicalRules = [];
|
|
91044
91264
|
const overrideRules = config["kimi-code"]?.rules ?? [];
|
|
91045
91265
|
for (const [category, rules] of Object.entries(config.permission)) {
|
|
91046
|
-
if (!(category === "mcp" || category.startsWith("mcp__") ||
|
|
91266
|
+
if (!(category === "mcp" || category.startsWith("mcp__") || lookupOwn({
|
|
91267
|
+
record: CATEGORY_TO_KIMI_CODE_TOOL,
|
|
91268
|
+
key: category
|
|
91269
|
+
}) !== void 0)) {
|
|
91047
91270
|
if (Object.keys(rules).length > 0) logger?.warn(`Kimi Code permissions: skipping unsupported category "${category}".`);
|
|
91048
91271
|
continue;
|
|
91049
91272
|
}
|
|
@@ -91860,9 +92083,16 @@ var KiroCapabilityPermissions = class KiroCapabilityPermissions extends ToolPerm
|
|
|
91860
92083
|
if (!isImportableRule(rule) || !isRecord(rule) || !isPermissionAction(rule.effect)) continue;
|
|
91861
92084
|
const category = KIRO_CAPABILITY_TO_CANONICAL[rule.capability];
|
|
91862
92085
|
const patterns = rule.match === void 0 ? ["*"] : isStringArray(rule.match) ? rule.match : [];
|
|
91863
|
-
const categoryRules =
|
|
92086
|
+
const categoryRules = getOrCreateOwn({
|
|
92087
|
+
record: permission,
|
|
92088
|
+
key: category,
|
|
92089
|
+
create: () => ({})
|
|
92090
|
+
});
|
|
91864
92091
|
for (const pattern of patterns) {
|
|
91865
|
-
const previous =
|
|
92092
|
+
const previous = lookupOwn({
|
|
92093
|
+
record: categoryRules,
|
|
92094
|
+
key: pattern
|
|
92095
|
+
});
|
|
91866
92096
|
if (isStricterAction({
|
|
91867
92097
|
action: rule.effect,
|
|
91868
92098
|
existing: previous
|
|
@@ -91880,7 +92110,10 @@ var KiroCapabilityPermissions = class KiroCapabilityPermissions extends ToolPerm
|
|
|
91880
92110
|
function buildKiroCapabilityRules({ config, logger }) {
|
|
91881
92111
|
const buckets = /* @__PURE__ */ new Map();
|
|
91882
92112
|
for (const [category, rules] of Object.entries(honorAllToolsOnBash(config.permission))) {
|
|
91883
|
-
const capability =
|
|
92113
|
+
const capability = lookupOwn({
|
|
92114
|
+
record: CANONICAL_TO_KIRO_CAPABILITY,
|
|
92115
|
+
key: category
|
|
92116
|
+
});
|
|
91884
92117
|
if (capability === void 0) {
|
|
91885
92118
|
logger?.warn(`Kiro permissions.yaml has no capability for category: ${category}. Skipping.`);
|
|
91886
92119
|
continue;
|
|
@@ -92040,7 +92273,10 @@ function toLettacodeBashPayload({ pattern, action, logger }) {
|
|
|
92040
92273
|
return prefix === "" ? "" : `${prefix}${PREFIX_SUFFIX}`;
|
|
92041
92274
|
}
|
|
92042
92275
|
function toLettacodeRule({ category, pattern, action, logger }) {
|
|
92043
|
-
const tool =
|
|
92276
|
+
const tool = lookupOwn({
|
|
92277
|
+
record: CATEGORY_TO_LETTACODE_TOOL,
|
|
92278
|
+
key: category
|
|
92279
|
+
});
|
|
92044
92280
|
if (tool === void 0) return null;
|
|
92045
92281
|
const payload = category === "bash" ? toLettacodeBashPayload({
|
|
92046
92282
|
pattern,
|
|
@@ -92195,11 +92431,17 @@ var LettacodePermissions = class LettacodePermissions extends ToolPermissions {
|
|
|
92195
92431
|
for (const [key, action] of Object.entries(LIST_TO_ACTION)) for (const rule of toStringArray$1(permissions[key])) {
|
|
92196
92432
|
const parsed = fromLettacodeRule(rule);
|
|
92197
92433
|
if (parsed === null || isPrototypePollutionKey(parsed.pattern)) continue;
|
|
92198
|
-
const rules =
|
|
92199
|
-
|
|
92434
|
+
const rules = getOrCreateOwn({
|
|
92435
|
+
record: permission,
|
|
92436
|
+
key: parsed.category,
|
|
92437
|
+
create: () => ({})
|
|
92438
|
+
});
|
|
92200
92439
|
if (isStricterAction({
|
|
92201
92440
|
action,
|
|
92202
|
-
existing
|
|
92441
|
+
existing: lookupOwn({
|
|
92442
|
+
record: rules,
|
|
92443
|
+
key: parsed.pattern
|
|
92444
|
+
})
|
|
92203
92445
|
})) rules[parsed.pattern] = action;
|
|
92204
92446
|
}
|
|
92205
92447
|
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({ permission }, null, 2) });
|
|
@@ -92386,7 +92628,13 @@ function getOpencodeMcpServerNames(mcp) {
|
|
|
92386
92628
|
return isRecord(lowered) ? Object.keys(lowered) : [];
|
|
92387
92629
|
}
|
|
92388
92630
|
function toOpencodePermissionKey(canonical) {
|
|
92389
|
-
return
|
|
92631
|
+
return lookupOwn({
|
|
92632
|
+
record: CANONICAL_TO_OPENCODE_PERMISSION_KEYS,
|
|
92633
|
+
key: canonical
|
|
92634
|
+
}) ?? lookupOwn({
|
|
92635
|
+
record: CANONICAL_TO_OPENCODE_FOLDED_PERMISSION_KEYS,
|
|
92636
|
+
key: canonical
|
|
92637
|
+
}) ?? canonical;
|
|
92390
92638
|
}
|
|
92391
92639
|
/**
|
|
92392
92640
|
* Merge two canonical pattern maps that land on the same OpenCode key.
|
|
@@ -92410,7 +92658,10 @@ function mergePatternRules(base, incoming) {
|
|
|
92410
92658
|
return Object.fromEntries(Object.entries(merged).toSorted(([, a], [, b]) => PERMISSION_ACTION_PRIORITY[a] - PERMISSION_ACTION_PRIORITY[b]));
|
|
92411
92659
|
}
|
|
92412
92660
|
function toCanonicalPermissionKey(opencodeKey) {
|
|
92413
|
-
return
|
|
92661
|
+
return lookupOwn({
|
|
92662
|
+
record: OPENCODE_TO_CANONICAL_PERMISSION_KEYS,
|
|
92663
|
+
key: opencodeKey
|
|
92664
|
+
}) ?? opencodeKey;
|
|
92414
92665
|
}
|
|
92415
92666
|
function isSharedPermissionCategory(category) {
|
|
92416
92667
|
return category === "*" || CANONICAL_PERMISSION_CATEGORIES$1.has(category) || category.startsWith("mcp__");
|
|
@@ -93692,7 +93943,14 @@ function importPoolTool({ toolConfig, bucket }) {
|
|
|
93692
93943
|
const denied = isStringArray(toolConfig[POOL_DENY_KEY]) ? toolConfig[POOL_DENY_KEY] : [];
|
|
93693
93944
|
const allowed = isStringArray(toolConfig[POOL_ALLOW_KEY]) ? toolConfig[POOL_ALLOW_KEY] : [];
|
|
93694
93945
|
for (const pattern of denied.filter(isUsablePattern)) bucket[pattern] = "deny";
|
|
93695
|
-
for (const pattern of allowed.filter(isUsablePattern)) bucket[pattern]
|
|
93946
|
+
for (const pattern of allowed.filter(isUsablePattern)) if (!Object.hasOwn(bucket, pattern)) bucket[pattern] = "allow";
|
|
93947
|
+
}
|
|
93948
|
+
/**
|
|
93949
|
+
* Record `allow` for a path no deny has claimed. The check is on own
|
|
93950
|
+
* properties, so a path such as `toString` is not mistaken for a stored rule.
|
|
93951
|
+
*/
|
|
93952
|
+
function allowUnlessSet(bucket, path) {
|
|
93953
|
+
if (!Object.hasOwn(bucket, path)) bucket[path] = "allow";
|
|
93696
93954
|
}
|
|
93697
93955
|
/**
|
|
93698
93956
|
* Convert Pool's `tools` and `paths` blocks back into a rulesync config.
|
|
@@ -93727,8 +93985,8 @@ function convertPoolToRulesync({ tools, paths }) {
|
|
|
93727
93985
|
for (const entry of allowEntries) {
|
|
93728
93986
|
const path = pathOfEntry(entry);
|
|
93729
93987
|
if (path === void 0) continue;
|
|
93730
|
-
bucketFor(READ_CATEGORY)
|
|
93731
|
-
if (isRecord(entry) && entry[POOL_WRITE_KEY] === true) for (const category of WRITE_CATEGORIES) bucketFor(category)
|
|
93988
|
+
allowUnlessSet(bucketFor(READ_CATEGORY), path);
|
|
93989
|
+
if (isRecord(entry) && entry[POOL_WRITE_KEY] === true) for (const category of WRITE_CATEGORIES) allowUnlessSet(bucketFor(category), path);
|
|
93732
93990
|
}
|
|
93733
93991
|
return { permission };
|
|
93734
93992
|
}
|
|
@@ -93857,7 +94115,10 @@ function toQoderRule({ category, pattern, logger }) {
|
|
|
93857
94115
|
logger?.warn(`Qoder permissions: skipping the '${category}' rule '${pattern}' — Qoder matches MCP tools by name only.`);
|
|
93858
94116
|
return null;
|
|
93859
94117
|
}
|
|
93860
|
-
const tool =
|
|
94118
|
+
const tool = lookupOwn({
|
|
94119
|
+
record: CATEGORY_TO_QODER_TOOL,
|
|
94120
|
+
key: category
|
|
94121
|
+
});
|
|
93861
94122
|
if (tool === void 0) return null;
|
|
93862
94123
|
if (pattern === "*") return tool;
|
|
93863
94124
|
return `${QODER_PATH_RULE_ALIASES[tool] ?? tool}(${pattern})`;
|
|
@@ -94143,11 +94404,17 @@ var QoderPermissions = class QoderPermissions extends ToolPermissions {
|
|
|
94143
94404
|
for (const action of ACTIONS) for (const rule of toStringArray$1(permissions[action])) {
|
|
94144
94405
|
const parsed = fromQoderRule(rule);
|
|
94145
94406
|
if (parsed === null || isPrototypePollutionKey(parsed.category) || isPrototypePollutionKey(parsed.pattern)) continue;
|
|
94146
|
-
const rules =
|
|
94147
|
-
|
|
94407
|
+
const rules = getOrCreateOwn({
|
|
94408
|
+
record: permission,
|
|
94409
|
+
key: parsed.category,
|
|
94410
|
+
create: () => ({})
|
|
94411
|
+
});
|
|
94148
94412
|
if (isStricterAction({
|
|
94149
94413
|
action,
|
|
94150
|
-
existing
|
|
94414
|
+
existing: lookupOwn({
|
|
94415
|
+
record: rules,
|
|
94416
|
+
key: parsed.pattern
|
|
94417
|
+
})
|
|
94151
94418
|
})) rules[parsed.pattern] = action;
|
|
94152
94419
|
}
|
|
94153
94420
|
const outputStyle = settings[QODER_OUTPUT_STYLE_KEY];
|
|
@@ -94211,10 +94478,16 @@ const CANONICAL_TO_QWEN_TOOL_NAMES = {
|
|
|
94211
94478
|
};
|
|
94212
94479
|
const QWEN_TO_CANONICAL_TOOL_NAMES = invertRecord(CANONICAL_TO_QWEN_TOOL_NAMES);
|
|
94213
94480
|
function toQwenToolName(canonical) {
|
|
94214
|
-
return
|
|
94481
|
+
return lookupOwn({
|
|
94482
|
+
record: CANONICAL_TO_QWEN_TOOL_NAMES,
|
|
94483
|
+
key: canonical
|
|
94484
|
+
}) ?? canonical;
|
|
94215
94485
|
}
|
|
94216
94486
|
function toCanonicalToolName$3(qwenName) {
|
|
94217
|
-
return
|
|
94487
|
+
return lookupOwn({
|
|
94488
|
+
record: QWEN_TO_CANONICAL_TOOL_NAMES,
|
|
94489
|
+
key: qwenName
|
|
94490
|
+
}) ?? qwenName;
|
|
94218
94491
|
}
|
|
94219
94492
|
function parseQwenPermissionEntry(entry, options = {}) {
|
|
94220
94493
|
const parenIndex = entry.indexOf("(");
|
|
@@ -94830,15 +95103,23 @@ function convertQwenToRulesyncPermissions(params) {
|
|
|
94830
95103
|
if (!parsed.ok) {
|
|
94831
95104
|
if (action === "deny") {
|
|
94832
95105
|
const canonical = toCanonicalToolName$3(parsed.toolName);
|
|
94833
|
-
if (
|
|
94834
|
-
|
|
95106
|
+
if (isPrototypePollutionKey(canonical)) continue;
|
|
95107
|
+
getOrCreateOwn({
|
|
95108
|
+
record: permission,
|
|
95109
|
+
key: canonical,
|
|
95110
|
+
create: () => ({})
|
|
95111
|
+
})["*"] = action;
|
|
94835
95112
|
}
|
|
94836
95113
|
continue;
|
|
94837
95114
|
}
|
|
94838
95115
|
const { toolName, pattern } = parsed;
|
|
94839
95116
|
const canonical = toCanonicalToolName$3(toolName);
|
|
94840
|
-
if (
|
|
94841
|
-
|
|
95117
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(pattern)) continue;
|
|
95118
|
+
getOrCreateOwn({
|
|
95119
|
+
record: permission,
|
|
95120
|
+
key: canonical,
|
|
95121
|
+
create: () => ({})
|
|
95122
|
+
})[pattern] = action;
|
|
94842
95123
|
}
|
|
94843
95124
|
};
|
|
94844
95125
|
processEntries(params.allow, "allow");
|
|
@@ -94902,15 +95183,24 @@ const REASONIX_TO_CANONICAL_TOOL_NAMES = {
|
|
|
94902
95183
|
};
|
|
94903
95184
|
/** Every Reasonix tool name a canonical category owns on generate. */
|
|
94904
95185
|
function reasonixToolNamesOf(canonical) {
|
|
94905
|
-
const legacy =
|
|
95186
|
+
const legacy = lookupOwn({
|
|
95187
|
+
record: CANONICAL_TO_LEGACY_REASONIX_TOOL_NAMES,
|
|
95188
|
+
key: canonical
|
|
95189
|
+
});
|
|
94906
95190
|
const current = toReasonixToolName(canonical);
|
|
94907
95191
|
return legacy === void 0 ? [current] : [current, legacy];
|
|
94908
95192
|
}
|
|
94909
95193
|
function toReasonixToolName(canonical) {
|
|
94910
|
-
return
|
|
95194
|
+
return lookupOwn({
|
|
95195
|
+
record: CANONICAL_TO_REASONIX_TOOL_NAMES,
|
|
95196
|
+
key: canonical
|
|
95197
|
+
}) ?? canonical;
|
|
94911
95198
|
}
|
|
94912
95199
|
function toCanonicalToolName$2(reasonixName) {
|
|
94913
|
-
return
|
|
95200
|
+
return lookupOwn({
|
|
95201
|
+
record: REASONIX_TO_CANONICAL_TOOL_NAMES,
|
|
95202
|
+
key: reasonixName
|
|
95203
|
+
}) ?? reasonixName;
|
|
94914
95204
|
}
|
|
94915
95205
|
/**
|
|
94916
95206
|
* Whether an entry uses the first-class `Tool=<literal>` exact-command form
|
|
@@ -95533,8 +95823,12 @@ function convertReasonixToRulesyncPermissions(params) {
|
|
|
95533
95823
|
for (const entry of entries) {
|
|
95534
95824
|
const { toolName, pattern } = parseToolPatternEntry(entry);
|
|
95535
95825
|
const canonical = toCanonicalToolName$2(toolName);
|
|
95536
|
-
if (
|
|
95537
|
-
|
|
95826
|
+
if (isPrototypePollutionKey(canonical) || isPrototypePollutionKey(pattern)) continue;
|
|
95827
|
+
getOrCreateOwn({
|
|
95828
|
+
record: permission,
|
|
95829
|
+
key: canonical,
|
|
95830
|
+
create: () => ({})
|
|
95831
|
+
})[pattern] = action;
|
|
95538
95832
|
}
|
|
95539
95833
|
};
|
|
95540
95834
|
processEntries(params.allow, "allow");
|
|
@@ -96086,7 +96380,10 @@ function convertRulesyncToRovodevToolPermissions({ config, logger }) {
|
|
|
96086
96380
|
if (bash) toolPermissions.bash = bash;
|
|
96087
96381
|
continue;
|
|
96088
96382
|
}
|
|
96089
|
-
const toolKeys =
|
|
96383
|
+
const toolKeys = lookupOwn({
|
|
96384
|
+
record: CATEGORY_TO_TOOL_KEYS,
|
|
96385
|
+
key: category
|
|
96386
|
+
});
|
|
96090
96387
|
if (!toolKeys) {
|
|
96091
96388
|
logger?.warn(`Rovo Dev permissions have no target for the "${category}" category. Skipping it.`);
|
|
96092
96389
|
markSharedToolKeyRestriction({
|
|
@@ -96234,7 +96531,7 @@ function convertRovodevToolPermissionsToRulesync(toolPermissions) {
|
|
|
96234
96531
|
}
|
|
96235
96532
|
if (isStringArray(toolPermissions.allowedExternalPaths)) for (const path of toolPermissions.allowedExternalPaths) {
|
|
96236
96533
|
permission.read ??= {};
|
|
96237
|
-
permission.read[path]
|
|
96534
|
+
if (!Object.hasOwn(permission.read, path)) permission.read[path] = "allow";
|
|
96238
96535
|
}
|
|
96239
96536
|
return { permission };
|
|
96240
96537
|
}
|
|
@@ -97971,11 +98268,17 @@ var TraePermissions = class TraePermissions extends ToolPermissions {
|
|
|
97971
98268
|
const permission = {};
|
|
97972
98269
|
const put = (category, pattern, action) => {
|
|
97973
98270
|
if (isPrototypePollutionKey(category) || isPrototypePollutionKey(pattern)) return;
|
|
97974
|
-
const rules =
|
|
97975
|
-
|
|
98271
|
+
const rules = getOrCreateOwn({
|
|
98272
|
+
record: permission,
|
|
98273
|
+
key: category,
|
|
98274
|
+
create: () => ({})
|
|
98275
|
+
});
|
|
97976
98276
|
if (isStricterAction({
|
|
97977
98277
|
action,
|
|
97978
|
-
existing
|
|
98278
|
+
existing: lookupOwn({
|
|
98279
|
+
record: rules,
|
|
98280
|
+
key: pattern
|
|
98281
|
+
})
|
|
97979
98282
|
})) rules[pattern] = action;
|
|
97980
98283
|
};
|
|
97981
98284
|
for (const [key, value] of Object.entries(readRecord(customProfiles, COMMAND_RULES_PATH))) {
|
|
@@ -100318,7 +100621,10 @@ function toZedToolName(canonical) {
|
|
|
100318
100621
|
const address = toolParts.length > 0 ? `${server}:${toolParts.join("__")}` : server ?? "";
|
|
100319
100622
|
return `${MCP_ZED_PREFIX}${address}`;
|
|
100320
100623
|
}
|
|
100321
|
-
return
|
|
100624
|
+
return lookupOwn({
|
|
100625
|
+
record: CANONICAL_TO_ZED_TOOL_NAMES,
|
|
100626
|
+
key: canonical
|
|
100627
|
+
}) ?? canonical;
|
|
100322
100628
|
}
|
|
100323
100629
|
function toCanonicalToolName(zedName) {
|
|
100324
100630
|
if (zedName.startsWith(MCP_ZED_PREFIX)) {
|
|
@@ -100326,7 +100632,10 @@ function toCanonicalToolName(zedName) {
|
|
|
100326
100632
|
const address = toolParts.length > 0 ? `${server}__${toolParts.join(":")}` : server ?? "";
|
|
100327
100633
|
return `${MCP_CATEGORY_PREFIX}${address}`;
|
|
100328
100634
|
}
|
|
100329
|
-
return
|
|
100635
|
+
return lookupOwn({
|
|
100636
|
+
record: ZED_TO_CANONICAL_TOOL_NAMES,
|
|
100637
|
+
key: zedName
|
|
100638
|
+
}) ?? zedName;
|
|
100330
100639
|
}
|
|
100331
100640
|
/**
|
|
100332
100641
|
* Zed matches `always_allow`/`always_deny`/`always_confirm` regexes against the
|
|
@@ -100638,7 +100947,10 @@ var ZedPermissions = class ZedPermissions extends ToolPermissions {
|
|
|
100638
100947
|
const globalDefault = parsed.success ? parsed.data.default : void 0;
|
|
100639
100948
|
const permission = {};
|
|
100640
100949
|
const ensure = (category) => {
|
|
100641
|
-
const existing =
|
|
100950
|
+
const existing = lookupOwn({
|
|
100951
|
+
record: permission,
|
|
100952
|
+
key: category
|
|
100953
|
+
});
|
|
100642
100954
|
if (existing) return existing;
|
|
100643
100955
|
const created = {};
|
|
100644
100956
|
permission[category] = created;
|
|
@@ -101265,7 +101577,7 @@ function resolveToolOutputRoot({ outputRoot, toolTarget, global }) {
|
|
|
101265
101577
|
async function convertFromTool(params) {
|
|
101266
101578
|
resetRunWarningState();
|
|
101267
101579
|
const packagingTarget = [params.fromTool, ...params.toTools].find(isPackagingToolTarget);
|
|
101268
|
-
if (packagingTarget) throw new Error(`Plugin packaging target '${packagingTarget}' is not supported by convert. Use import and generate with
|
|
101580
|
+
if (packagingTarget) throw new Error(`Plugin packaging target '${packagingTarget}' is not supported by convert. Use import with the plugin directory as outputRoot (--output-root) and generate with it in outputRoots (--output-roots).`);
|
|
101269
101581
|
const ctx = params;
|
|
101270
101582
|
const [rulesCount, ignoreCount, mcpCount, commandsCount, subagentsCount, skillsCount, hooksCount, permissionsCount, checksCount] = [
|
|
101271
101583
|
await runFeatureConvert(ctx, buildRulesStrategy(ctx)),
|
|
@@ -103745,6 +104057,6 @@ const CHECKS_IMPORT_STRATEGY = {
|
|
|
103745
104057
|
missingLabel: "check files",
|
|
103746
104058
|
createdLabel: "check files"
|
|
103747
104059
|
};
|
|
103748
|
-
export {
|
|
104060
|
+
export { RulesyncMcp as $, RULESYNC_USER_CONFIG_DIR_NAME as $n, isRootDirPath as $t, CODEBUDDY_LOCAL_RULE_FILE_NAME as A, CURATED_RULES_FEATURE_SUBDIR as An, resetRunWarningState as At, CLAUDECODE_MEMORIES_DIR_NAME as B, RULESYNC_HOOKS_RELATIVE_FILE_PATH as Bn, assertTreeContainsNoSymlinks as Bt, LETTACODE_DIR as C, ALL_TOOL_TARGETS as Cn, findControlCharacter as Ct, COMMANDCODE_DIR as D, ALL_FEATURES as Dn, fallbackLogger as Dt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as E, ToolTargetSchema as En, WarningCollectingLogger as Et, CODEXCLI_BASH_RULES_FILE_NAME as F, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as Fn, PathOutsideRootError as Ft, getLocalSkillDirNames as G, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Gn, ensureDir as Gt, CLAUDECODE_SKILLS_DIR_PATH as H, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as Hn, checkPathTraversal as Ht, CODEXCLI_DIR as I, RULESYNC_CONFIG_RELATIVE_FILE_PATH as In, applyFileMode as It, RulesyncSkill as J, RULESYNC_RELATIVE_DIR_PATH as Jn, findFilesByGlobs as Jt, RulesyncSubagent as K, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as Kn, fileExists as Kt, CLAUDECODE_AGENT_MEMORY_LOCAL_DIR_NAME as L, RULESYNC_CONFIG_SCHEMA_URL as Ln, assertDirectoryIfExists as Lt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as M, MAX_FILE_SIZE as Mn, CLIError as Mt, caseFoldIdentity as N, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as Nn, ErrorCodes as Nt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as O, ALL_FEATURES_WITH_WILDCARD as On, warnOnConflictingFlags as Ot, groupSpellingsByCaseFoldedIdentity as P, RULESYNC_CHECKS_RELATIVE_DIR_PATH as Pn, parseJsonc as Pt, RulesyncPermissions as Q, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Qn, isFileSystemError as Qt, CLAUDECODE_DIR as R, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as Rn, assertNoSymlinkBelowRoot as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, mapWithConcurrency as Sn, getSourceTransport as St, FACTORYDROID_DIR as T, PACKAGING_TOOL_TARGETS as Tn, JsonLogger as Tt, findProjectInstructionBudgetFindings as U, RULESYNC_MCP_RELATIVE_FILE_PATH as Un, createTempDirectory as Ut, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as V, RULESYNC_IGNORE_RELATIVE_FILE_PATH as Vn, assertWritablePathInsideRoot as Vt, toInstructionBudgetFile as W, RULESYNC_MCP_SCHEMA_URL as Wn, directoryExists as Wt, RulesyncRule as X, RULESYNC_SKILLS_RELATIVE_DIR_PATH as Xn, getHomeDirectory as Xt, RulesyncSkillFrontmatterSchema as Y, RULESYNC_RULES_RELATIVE_DIR_PATH as Yn, getFileSizeAndExecutableBit as Yt, RulesyncRuleFrontmatterSchema as Z, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Zn, isFileNotFoundError as Zt, SkillsProcessor as _, runWithDirectoryRollback as _n, CONFLICTING_TARGET_PAIRS as _t, convertFromTool as a, pathEscapesRoot as an, hasEnclosingMarkOutsideKeycap as ar, RulesyncCommandFrontmatterSchema as at, REASONIX_LOCAL_RULE_FILE_NAME as b, writeFileBuffer as bn, SourceEntrySchema as bt, ELLIPSIS_WIDTH as c, readFileContentOrNull as cn, stripControlCharactersKeepingLineFeeds as cr, stringifyFrontmatter as ct, McpProcessor as d, removeFile as dn, SHARED_USER_MANAGED_CONFIG_PATHS as dt, isSymlink as en, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as er, RulesyncIgnore as et, IgnoreProcessor as f, removeFileStrict as fn, SKILL_FILE_NAME as ft, SubagentsProcessor as g, resolvedRelativePath as gn, resolveEffectiveInputRoots as gt, RulesProcessor as h, resolvePath as hn, parseConfigFileContent as ht, inspectInputRoots as i, normalizePosixDirPath as in, hasDeceptiveHiddenCharacters as ir, RulesyncCommand as it, AUGMENTCODE_DIR as j, FEATURE_SOURCE_TREE_ENTRIES as jn, withWarnOnceScope as jt, CODEBUDDY_DIR as k, DEPRECATED_FEATURE_REPLACEMENTS as kn, withFallbackLoggerTarget as kt, displayWidthOf as l, removeDirectory as ln, stripControlCharactersKeepingLineFeedsAndTabs as lr, loadYaml as lt, ChecksProcessor as m, removeTempDirectory as mn, mergeInputRootConfigs as mt, formatSourceLoadFailure as n, listFilePathsRecursively as nn, formatError as nr, getRulesyncSourceCandidates as nt, getProcessorRegistryEntry as o, posixRelativePathEscapesRoot as on, quoteForLog as or, RulesyncCheck as ot, HooksProcessor as p, removeFileWithoutFollowingSymlinks as pn, ConfigResolver as pt, RulesyncSubagentFrontmatterSchema as q, RULESYNC_PERMISSIONS_SCHEMA_URL as qn, fileExistsStrict as qt, generate as r, listSubdirectoryNames as rn, truncateText as rr, resolveRulesyncSourceWritePath as rt, parseCommaSeparatedList as s, readFileContent as sn, stripControlCharacters as sr, RulesyncCheckFrontmatterSchema as st, importFromTool as t, listDirectoryEntryNames as tn, formatMegabytes as tr, RulesyncHooks as tt, shortenToWidth as u, removeDirectoryStrict as un, stripHiddenCharacters as ur, isRecord as ut, CRUSH_LOCAL_RULE_FILE_NAME as v, splitPathSegments as vn, ConfigFileSchema as vt, LETTACODE_SETTINGS_LOCAL_FILE_NAME as w, ALL_TOOL_TARGETS_WITH_WILDCARD as wn, ConsoleLogger as wt, QWENCODE_DIR as x, writeFileContent as xn, assertTargetsFeaturesExclusive as xt, CommandsProcessor as y, toPosixPath as yn, GITIGNORE_DESTINATION_KEY as yt, CLAUDECODE_LOCAL_RULE_FILE_NAME as z, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as zn, assertPathResolvesInsideRoot as zt };
|
|
103749
104061
|
|
|
103750
|
-
//# sourceMappingURL=import-
|
|
104062
|
+
//# sourceMappingURL=import-w0JlEoNl.js.map
|