rulesync 16.33.0 → 16.34.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 -0
- package/dist/cli/index.cjs +4 -4
- package/dist/cli/index.js +4 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/{import-DVRU6cLB.cjs → import-9T9fnjNn.cjs} +1589 -23
- package/dist/{import-C6R-lXFF.js → import-De13gNpF.js} +1573 -7
- package/dist/import-De13gNpF.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/import-C6R-lXFF.js.map +0 -1
|
@@ -478,6 +478,7 @@ const rulesProcessorToolTargetTuple = [
|
|
|
478
478
|
"cline",
|
|
479
479
|
"codebuddy",
|
|
480
480
|
"codexcli",
|
|
481
|
+
"commandcode",
|
|
481
482
|
"continue",
|
|
482
483
|
"copilot",
|
|
483
484
|
"copilotcli",
|
|
@@ -554,6 +555,7 @@ const mcpProcessorToolTargetTuple = [
|
|
|
554
555
|
"claudecode-legacy",
|
|
555
556
|
"cline",
|
|
556
557
|
"codexcli",
|
|
558
|
+
"commandcode",
|
|
557
559
|
"continue",
|
|
558
560
|
"copilot",
|
|
559
561
|
"copilotcli",
|
|
@@ -597,6 +599,7 @@ const commandsProcessorToolTargetTuple = [
|
|
|
597
599
|
"claudecode-legacy",
|
|
598
600
|
"cline",
|
|
599
601
|
"codexcli",
|
|
602
|
+
"commandcode",
|
|
600
603
|
"continue",
|
|
601
604
|
"copilot",
|
|
602
605
|
"cursor",
|
|
@@ -634,6 +637,7 @@ const subagentsProcessorToolTargetTuple = [
|
|
|
634
637
|
"claudecode-legacy",
|
|
635
638
|
"cline",
|
|
636
639
|
"codexcli",
|
|
640
|
+
"commandcode",
|
|
637
641
|
"copilot",
|
|
638
642
|
"copilotcli",
|
|
639
643
|
"cortexcode",
|
|
@@ -675,6 +679,7 @@ const skillsProcessorToolTargetTuple = [
|
|
|
675
679
|
"claudecode-legacy",
|
|
676
680
|
"cline",
|
|
677
681
|
"codexcli",
|
|
682
|
+
"commandcode",
|
|
678
683
|
"continue",
|
|
679
684
|
"copilot",
|
|
680
685
|
"copilotcli",
|
|
@@ -722,6 +727,7 @@ const hooksProcessorToolTargetTuple = [
|
|
|
722
727
|
"claudecode",
|
|
723
728
|
"claudecode-plugin",
|
|
724
729
|
"codexcli",
|
|
730
|
+
"commandcode",
|
|
725
731
|
"continue",
|
|
726
732
|
"copilot",
|
|
727
733
|
"copilotcli",
|
|
@@ -756,6 +762,7 @@ const permissionsProcessorToolTargetTuple = [
|
|
|
756
762
|
"claudecode",
|
|
757
763
|
"cline",
|
|
758
764
|
"codexcli",
|
|
765
|
+
"commandcode",
|
|
759
766
|
"continue",
|
|
760
767
|
"copilot",
|
|
761
768
|
"copilotcli",
|
|
@@ -3270,6 +3277,7 @@ const SHARED_USER_MANAGED_CONFIG_PATHS = [
|
|
|
3270
3277
|
".claude/settings.json",
|
|
3271
3278
|
".claude/settings.local.json",
|
|
3272
3279
|
".codex/config.toml",
|
|
3280
|
+
".commandcode/settings.json",
|
|
3273
3281
|
".continue/settings.json",
|
|
3274
3282
|
".copilot/settings.json",
|
|
3275
3283
|
".github/copilot/settings.json",
|
|
@@ -4927,6 +4935,30 @@ const CANONICAL_TO_CORTEXCODE_EVENT_NAMES = {
|
|
|
4927
4935
|
};
|
|
4928
4936
|
const CORTEXCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_CORTEXCODE_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
4929
4937
|
/**
|
|
4938
|
+
* Hook events supported by Command Code.
|
|
4939
|
+
*
|
|
4940
|
+
* Command Code reads hooks from the `hooks` key of `.commandcode/settings.json`
|
|
4941
|
+
* (project) and `~/.commandcode/settings.json` (user) in the Claude-Code shape.
|
|
4942
|
+
* Four events are documented: PreToolUse, PostToolUse, Stop and SessionStart.
|
|
4943
|
+
* Only `command` hooks exist; `timeout` is in seconds (default 30, max 600)
|
|
4944
|
+
* and `$COMMANDCODE_PROJECT_DIR` resolves to the project root.
|
|
4945
|
+
*
|
|
4946
|
+
* @see https://commandcode.ai/docs/hooks
|
|
4947
|
+
*/
|
|
4948
|
+
const COMMANDCODE_HOOK_EVENTS = [
|
|
4949
|
+
"preToolUse",
|
|
4950
|
+
"postToolUse",
|
|
4951
|
+
"stop",
|
|
4952
|
+
"sessionStart"
|
|
4953
|
+
];
|
|
4954
|
+
const CANONICAL_TO_COMMANDCODE_EVENT_NAMES = {
|
|
4955
|
+
preToolUse: "PreToolUse",
|
|
4956
|
+
postToolUse: "PostToolUse",
|
|
4957
|
+
stop: "Stop",
|
|
4958
|
+
sessionStart: "SessionStart"
|
|
4959
|
+
};
|
|
4960
|
+
const COMMANDCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_COMMANDCODE_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
4961
|
+
/**
|
|
4930
4962
|
* Hook events supported by the Continue CLI (`cn`).
|
|
4931
4963
|
*
|
|
4932
4964
|
* Continue reads a Claude-Code-compatible `hooks` key from
|
|
@@ -5135,6 +5167,7 @@ const HooksConfigSchema = z.looseObject({
|
|
|
5135
5167
|
crush: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5136
5168
|
bob: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5137
5169
|
cortexcode: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5170
|
+
commandcode: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5138
5171
|
continue: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5139
5172
|
tabnine: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5140
5173
|
qwencode: z.optional(z.looseObject({
|
|
@@ -6154,6 +6187,7 @@ const RulesyncMcpFileSchema = z.looseObject({
|
|
|
6154
6187
|
claudecode: z.optional(toolScopedMcpSchema),
|
|
6155
6188
|
cline: z.optional(toolScopedMcpSchema),
|
|
6156
6189
|
codexcli: z.optional(toolScopedMcpSchema),
|
|
6190
|
+
commandcode: z.optional(toolScopedMcpSchema),
|
|
6157
6191
|
continue: z.optional(toolScopedMcpSchema),
|
|
6158
6192
|
copilot: z.optional(toolScopedMcpSchema),
|
|
6159
6193
|
copilotcli: z.optional(toolScopedMcpSchema),
|
|
@@ -6549,12 +6583,16 @@ var RulesyncMcp = class RulesyncMcp extends RulesyncFile {
|
|
|
6549
6583
|
* Targets that share one output file resolve identically so the shared
|
|
6550
6584
|
* file's content never depends on which of them generates last — see
|
|
6551
6585
|
* `resolveMcpTarget` for the alias groups (kiro trio, claudecode/-legacy,
|
|
6552
|
-
* and the
|
|
6586
|
+
* the Antigravity pair, and the project-scope Claude Code / Command Code
|
|
6587
|
+
* pair, which is why the scope is part of the resolution).
|
|
6553
6588
|
*
|
|
6554
6589
|
* Returns the same instance when neither mechanism is used.
|
|
6555
6590
|
*/
|
|
6556
|
-
forTarget({ toolTarget, logger }) {
|
|
6557
|
-
const { blockKeys, acceptedTargetNames } = resolveMcpTarget({
|
|
6591
|
+
forTarget({ toolTarget, global = false, logger }) {
|
|
6592
|
+
const { blockKeys, acceptedTargetNames } = resolveMcpTarget({
|
|
6593
|
+
toolTarget,
|
|
6594
|
+
global
|
|
6595
|
+
});
|
|
6558
6596
|
const json = this.json;
|
|
6559
6597
|
const sharedServers = this.json.mcpServers ?? {};
|
|
6560
6598
|
const serverNamesWithTargets = Object.entries(sharedServers).filter(([, serverConfig]) => serverConfig.targets !== void 0).map(([serverName]) => serverName);
|
|
@@ -6641,9 +6679,24 @@ const MCP_IGNORED_ALIAS_SOURCE_KEYS = Object.keys(MCP_BLOCK_KEY_ALIASES);
|
|
|
6641
6679
|
* `config`) — so both targets always apply both blocks in a fixed order
|
|
6642
6680
|
* (`antigravity-ide` first, `antigravity-cli` second — the CLI block wins
|
|
6643
6681
|
* per server on conflict).
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6682
|
+
* - `claudecode` (and its legacy alias) and `commandcode` share the root
|
|
6683
|
+
* `.mcp.json` in PROJECT mode only (their global files differ:
|
|
6684
|
+
* `~/.claude.json` vs `~/.commandcode/mcp.json`), so in project mode the
|
|
6685
|
+
* three apply the `claudecode` and `commandcode` blocks in that fixed order
|
|
6686
|
+
* (the `commandcode` block wins per server on conflict); in global mode
|
|
6687
|
+
* each reads its own block.
|
|
6688
|
+
*/
|
|
6689
|
+
function resolveMcpTarget({ toolTarget, global }) {
|
|
6690
|
+
const isClaudecode = toolTarget === "claudecode" || toolTarget === "claudecode-legacy";
|
|
6691
|
+
if (!global && (isClaudecode || toolTarget === "commandcode")) return {
|
|
6692
|
+
blockKeys: ["claudecode", "commandcode"],
|
|
6693
|
+
acceptedTargetNames: /* @__PURE__ */ new Set([
|
|
6694
|
+
"claudecode",
|
|
6695
|
+
"claudecode-legacy",
|
|
6696
|
+
"commandcode"
|
|
6697
|
+
])
|
|
6698
|
+
};
|
|
6699
|
+
if (isClaudecode) return {
|
|
6647
6700
|
blockKeys: ["claudecode"],
|
|
6648
6701
|
acceptedTargetNames: /* @__PURE__ */ new Set(["claudecode", "claudecode-legacy"])
|
|
6649
6702
|
};
|
|
@@ -7808,6 +7861,7 @@ const PermissionsConfigSchema = z.looseObject({
|
|
|
7808
7861
|
"antigravity-ide": z.optional(CanonicalPermissionsOverrideSchema),
|
|
7809
7862
|
continue: z.optional(CanonicalPermissionsOverrideSchema),
|
|
7810
7863
|
copilot: z.optional(CanonicalPermissionsOverrideSchema),
|
|
7864
|
+
commandcode: z.optional(CanonicalPermissionsOverrideSchema),
|
|
7811
7865
|
copilotcli: z.optional(CanonicalPermissionsOverrideSchema),
|
|
7812
7866
|
crush: z.optional(CanonicalPermissionsOverrideSchema),
|
|
7813
7867
|
goose: z.optional(CanonicalPermissionsOverrideSchema),
|
|
@@ -9530,6 +9584,7 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
|
|
|
9530
9584
|
"user-invocable": z.optional(z.boolean()),
|
|
9531
9585
|
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
|
|
9532
9586
|
})),
|
|
9587
|
+
commandcode: z.optional(z.looseObject({})),
|
|
9533
9588
|
cortexcode: z.optional(z.looseObject({})),
|
|
9534
9589
|
continue: z.optional(z.looseObject({})),
|
|
9535
9590
|
tabnine: z.optional(z.looseObject({})),
|
|
@@ -13141,6 +13196,20 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
13141
13196
|
ownedKeys: ["hooks"]
|
|
13142
13197
|
} }
|
|
13143
13198
|
},
|
|
13199
|
+
".commandcode/settings.json": {
|
|
13200
|
+
format: "json",
|
|
13201
|
+
invalidRootPolicy: "error",
|
|
13202
|
+
features: {
|
|
13203
|
+
hooks: {
|
|
13204
|
+
kind: "replace-owned-keys",
|
|
13205
|
+
ownedKeys: ["hooks"]
|
|
13206
|
+
},
|
|
13207
|
+
permissions: {
|
|
13208
|
+
kind: "replace-owned-keys",
|
|
13209
|
+
ownedKeys: ["permissions"]
|
|
13210
|
+
}
|
|
13211
|
+
}
|
|
13212
|
+
},
|
|
13144
13213
|
".cortex/settings.json": {
|
|
13145
13214
|
format: "json",
|
|
13146
13215
|
invalidRootPolicy: "error",
|
|
@@ -15342,6 +15411,94 @@ var CodexcliCommand = class CodexcliCommand extends ToolCommand {
|
|
|
15342
15411
|
}
|
|
15343
15412
|
};
|
|
15344
15413
|
//#endregion
|
|
15414
|
+
//#region src/constants/commandcode-paths.ts
|
|
15415
|
+
const COMMANDCODE_DIR = ".commandcode";
|
|
15416
|
+
const COMMANDCODE_RULE_FILE_NAME = "AGENTS.md";
|
|
15417
|
+
const COMMANDCODE_SETTINGS_FILE_NAME = "settings.json";
|
|
15418
|
+
const COMMANDCODE_PROJECT_MCP_FILE_NAME = ".mcp.json";
|
|
15419
|
+
const COMMANDCODE_GLOBAL_MCP_FILE_NAME = "mcp.json";
|
|
15420
|
+
const COMMANDCODE_COMMANDS_DIR_PATH = join(COMMANDCODE_DIR, "commands");
|
|
15421
|
+
const COMMANDCODE_AGENTS_DIR_PATH = join(COMMANDCODE_DIR, "agents");
|
|
15422
|
+
const COMMANDCODE_SKILLS_DIR_PATH = join(COMMANDCODE_DIR, "skills");
|
|
15423
|
+
//#endregion
|
|
15424
|
+
//#region src/features/commands/commandcode-command.ts
|
|
15425
|
+
/**
|
|
15426
|
+
* Custom slash command for Command Code.
|
|
15427
|
+
*
|
|
15428
|
+
* Command Code reads Markdown files from `.commandcode/commands/` (project)
|
|
15429
|
+
* and `~/.commandcode/commands/` (user), naming each command after the file's
|
|
15430
|
+
* basename; subdirectories only group the files. The file's full trimmed
|
|
15431
|
+
* body is the prompt that runs — YAML frontmatter is not stripped, only
|
|
15432
|
+
* skipped when the slash menu picks a summary line — so rulesync writes the
|
|
15433
|
+
* bare body. On import a hand-written file's frontmatter block is dropped
|
|
15434
|
+
* (Command Code would send it as prompt text; a rulesync command carries its
|
|
15435
|
+
* own frontmatter) and only the body is kept.
|
|
15436
|
+
*
|
|
15437
|
+
* @see https://commandcode.ai/docs/custom-slash-commands
|
|
15438
|
+
*/
|
|
15439
|
+
var CommandcodeCommand = class CommandcodeCommand extends ToolCommand {
|
|
15440
|
+
static getSettablePaths(_options = {}) {
|
|
15441
|
+
return { relativeDirPath: COMMANDCODE_COMMANDS_DIR_PATH };
|
|
15442
|
+
}
|
|
15443
|
+
toRulesyncCommand() {
|
|
15444
|
+
return new RulesyncCommand({
|
|
15445
|
+
outputRoot: process.cwd(),
|
|
15446
|
+
frontmatter: { targets: ["*"] },
|
|
15447
|
+
body: this.getFileContent(),
|
|
15448
|
+
relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
|
|
15449
|
+
relativeFilePath: this.relativeFilePath,
|
|
15450
|
+
fileContent: this.getFileContent(),
|
|
15451
|
+
validate: true
|
|
15452
|
+
});
|
|
15453
|
+
}
|
|
15454
|
+
static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true }) {
|
|
15455
|
+
const paths = this.getSettablePaths();
|
|
15456
|
+
return new CommandcodeCommand({
|
|
15457
|
+
outputRoot,
|
|
15458
|
+
fileContent: rulesyncCommand.getBody(),
|
|
15459
|
+
relativeDirPath: paths.relativeDirPath,
|
|
15460
|
+
relativeFilePath: rulesyncCommand.getRelativeFilePath(),
|
|
15461
|
+
validate
|
|
15462
|
+
});
|
|
15463
|
+
}
|
|
15464
|
+
validate() {
|
|
15465
|
+
return {
|
|
15466
|
+
success: true,
|
|
15467
|
+
error: null
|
|
15468
|
+
};
|
|
15469
|
+
}
|
|
15470
|
+
getBody() {
|
|
15471
|
+
return this.getFileContent();
|
|
15472
|
+
}
|
|
15473
|
+
static isTargetedByRulesyncCommand(rulesyncCommand) {
|
|
15474
|
+
return this.isTargetedByRulesyncCommandDefault({
|
|
15475
|
+
rulesyncCommand,
|
|
15476
|
+
toolTarget: "commandcode"
|
|
15477
|
+
});
|
|
15478
|
+
}
|
|
15479
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true }) {
|
|
15480
|
+
const paths = this.getSettablePaths();
|
|
15481
|
+
const filePath = join(outputRoot, paths.relativeDirPath, relativeFilePath);
|
|
15482
|
+
const { body: content } = parseFrontmatter(await readFileContent(filePath), filePath);
|
|
15483
|
+
return new CommandcodeCommand({
|
|
15484
|
+
outputRoot,
|
|
15485
|
+
relativeDirPath: paths.relativeDirPath,
|
|
15486
|
+
relativeFilePath,
|
|
15487
|
+
fileContent: content.trim(),
|
|
15488
|
+
validate
|
|
15489
|
+
});
|
|
15490
|
+
}
|
|
15491
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
15492
|
+
return new CommandcodeCommand({
|
|
15493
|
+
outputRoot,
|
|
15494
|
+
relativeDirPath,
|
|
15495
|
+
relativeFilePath,
|
|
15496
|
+
fileContent: "",
|
|
15497
|
+
validate: false
|
|
15498
|
+
});
|
|
15499
|
+
}
|
|
15500
|
+
};
|
|
15501
|
+
//#endregion
|
|
15345
15502
|
//#region src/constants/continue-paths.ts
|
|
15346
15503
|
const CONTINUE_DIR = ".continue";
|
|
15347
15504
|
const CONTINUE_ROOT_RULE_FILE_NAME = "AGENTS.md";
|
|
@@ -19585,6 +19742,16 @@ const toolCommandFactories = /* @__PURE__ */ new Map([
|
|
|
19585
19742
|
supportsSubdirectory: false
|
|
19586
19743
|
}
|
|
19587
19744
|
}],
|
|
19745
|
+
["commandcode", {
|
|
19746
|
+
class: CommandcodeCommand,
|
|
19747
|
+
meta: {
|
|
19748
|
+
extension: "md",
|
|
19749
|
+
supportsProject: true,
|
|
19750
|
+
supportsGlobal: true,
|
|
19751
|
+
isSimulated: false,
|
|
19752
|
+
supportsSubdirectory: true
|
|
19753
|
+
}
|
|
19754
|
+
}],
|
|
19588
19755
|
["continue", {
|
|
19589
19756
|
class: ContinueCommand,
|
|
19590
19757
|
meta: {
|
|
@@ -22955,6 +23122,132 @@ var CodexcliHooks = class CodexcliHooks extends ToolHooks {
|
|
|
22955
23122
|
}
|
|
22956
23123
|
};
|
|
22957
23124
|
//#endregion
|
|
23125
|
+
//#region src/features/hooks/commandcode-hooks.ts
|
|
23126
|
+
const COMMANDCODE_CONVERTER_CONFIG = {
|
|
23127
|
+
supportedEvents: COMMANDCODE_HOOK_EVENTS,
|
|
23128
|
+
canonicalToToolEventNames: CANONICAL_TO_COMMANDCODE_EVENT_NAMES,
|
|
23129
|
+
toolToCanonicalEventNames: COMMANDCODE_TO_CANONICAL_EVENT_NAMES,
|
|
23130
|
+
projectDirVar: "$COMMANDCODE_PROJECT_DIR",
|
|
23131
|
+
prefixDotRelativeCommandsOnly: true,
|
|
23132
|
+
noMatcherEvents: /* @__PURE__ */ new Set(["stop", "sessionStart"]),
|
|
23133
|
+
supportedHookTypes: /* @__PURE__ */ new Set(["command"])
|
|
23134
|
+
};
|
|
23135
|
+
/**
|
|
23136
|
+
* Single spelling of the settings codec/policy: fail closed on an unparseable
|
|
23137
|
+
* root rather than replacing the user's Command Code settings with generated
|
|
23138
|
+
* output.
|
|
23139
|
+
*/
|
|
23140
|
+
function parseCommandcodeSettings$1(fileContent, filePath) {
|
|
23141
|
+
return parseSharedConfig({
|
|
23142
|
+
format: "json",
|
|
23143
|
+
fileContent,
|
|
23144
|
+
filePath,
|
|
23145
|
+
invalidRootPolicy: "error"
|
|
23146
|
+
});
|
|
23147
|
+
}
|
|
23148
|
+
/**
|
|
23149
|
+
* Command Code hooks.
|
|
23150
|
+
*
|
|
23151
|
+
* Hooks live under the top-level `hooks` key of `<project>/.commandcode/settings.json`
|
|
23152
|
+
* (project scope) and `~/.commandcode/settings.json` (user scope), in the
|
|
23153
|
+
* Claude-Code shape: `{ "<Event>": [{ "matcher"?: "<regex>", "hooks": [{
|
|
23154
|
+
* "type": "command", "command": "...", "timeout"?: <seconds> }] }] }`. Both
|
|
23155
|
+
* files also hold settings rulesync does not own (`permissions`, `defaultMode`,
|
|
23156
|
+
* ...), so generation merges the `hooks` key into the existing file (see
|
|
23157
|
+
* `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it.
|
|
23158
|
+
*
|
|
23159
|
+
* @see https://commandcode.ai/docs/hooks
|
|
23160
|
+
* @see https://commandcode.ai/docs/settings
|
|
23161
|
+
*/
|
|
23162
|
+
var CommandcodeHooks = class CommandcodeHooks extends ToolHooks {
|
|
23163
|
+
constructor(params) {
|
|
23164
|
+
super({
|
|
23165
|
+
...params,
|
|
23166
|
+
fileContent: params.fileContent ?? "{}"
|
|
23167
|
+
});
|
|
23168
|
+
}
|
|
23169
|
+
isDeletable() {
|
|
23170
|
+
return false;
|
|
23171
|
+
}
|
|
23172
|
+
static getSettablePaths(_options = {}) {
|
|
23173
|
+
return {
|
|
23174
|
+
relativeDirPath: COMMANDCODE_DIR,
|
|
23175
|
+
relativeFilePath: COMMANDCODE_SETTINGS_FILE_NAME
|
|
23176
|
+
};
|
|
23177
|
+
}
|
|
23178
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
23179
|
+
const paths = CommandcodeHooks.getSettablePaths({ global });
|
|
23180
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"hooks\":{}}";
|
|
23181
|
+
return new CommandcodeHooks({
|
|
23182
|
+
outputRoot,
|
|
23183
|
+
relativeDirPath: paths.relativeDirPath,
|
|
23184
|
+
relativeFilePath: paths.relativeFilePath,
|
|
23185
|
+
fileContent,
|
|
23186
|
+
validate
|
|
23187
|
+
});
|
|
23188
|
+
}
|
|
23189
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
23190
|
+
const paths = CommandcodeHooks.getSettablePaths({ global });
|
|
23191
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
23192
|
+
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
23193
|
+
const config = rulesyncHooks.getJson();
|
|
23194
|
+
const hooks = canonicalToToolHooks({
|
|
23195
|
+
config,
|
|
23196
|
+
toolOverrideHooks: config.commandcode?.hooks,
|
|
23197
|
+
converterConfig: COMMANDCODE_CONVERTER_CONFIG,
|
|
23198
|
+
logger
|
|
23199
|
+
});
|
|
23200
|
+
const fileContent = applySharedConfigPatch({
|
|
23201
|
+
fileKey: sharedConfigFileKey(paths),
|
|
23202
|
+
feature: "hooks",
|
|
23203
|
+
existingContent,
|
|
23204
|
+
patch: { hooks },
|
|
23205
|
+
filePath,
|
|
23206
|
+
logger
|
|
23207
|
+
});
|
|
23208
|
+
return new CommandcodeHooks({
|
|
23209
|
+
outputRoot,
|
|
23210
|
+
relativeDirPath: paths.relativeDirPath,
|
|
23211
|
+
relativeFilePath: paths.relativeFilePath,
|
|
23212
|
+
fileContent,
|
|
23213
|
+
validate
|
|
23214
|
+
});
|
|
23215
|
+
}
|
|
23216
|
+
toRulesyncHooks({ logger } = {}) {
|
|
23217
|
+
const configPath = join(this.getRelativeDirPath(), this.getRelativeFilePath());
|
|
23218
|
+
let settings;
|
|
23219
|
+
try {
|
|
23220
|
+
settings = parseCommandcodeSettings$1(this.getFileContent(), configPath);
|
|
23221
|
+
} catch (error) {
|
|
23222
|
+
throw new Error(`Failed to parse Command Code hooks content in ${configPath}: ${formatError(error)}`, { cause: error });
|
|
23223
|
+
}
|
|
23224
|
+
const hooks = toolHooksToCanonical({
|
|
23225
|
+
logger,
|
|
23226
|
+
hooks: settings.hooks,
|
|
23227
|
+
converterConfig: COMMANDCODE_CONVERTER_CONFIG
|
|
23228
|
+
});
|
|
23229
|
+
return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
|
|
23230
|
+
hooks,
|
|
23231
|
+
overrideKey: "commandcode"
|
|
23232
|
+
}), null, 2) });
|
|
23233
|
+
}
|
|
23234
|
+
validate() {
|
|
23235
|
+
return {
|
|
23236
|
+
success: true,
|
|
23237
|
+
error: null
|
|
23238
|
+
};
|
|
23239
|
+
}
|
|
23240
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
23241
|
+
return new CommandcodeHooks({
|
|
23242
|
+
outputRoot,
|
|
23243
|
+
relativeDirPath,
|
|
23244
|
+
relativeFilePath,
|
|
23245
|
+
fileContent: JSON.stringify({ hooks: {} }, null, 2),
|
|
23246
|
+
validate: false
|
|
23247
|
+
});
|
|
23248
|
+
}
|
|
23249
|
+
};
|
|
23250
|
+
//#endregion
|
|
22958
23251
|
//#region src/features/hooks/continue-hooks.ts
|
|
22959
23252
|
const CONTINUE_SPEC = {
|
|
22960
23253
|
displayName: "Continue",
|
|
@@ -28074,6 +28367,18 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
28074
28367
|
supportedHookTypes: ["command"],
|
|
28075
28368
|
supportsMatcher: true
|
|
28076
28369
|
}],
|
|
28370
|
+
["commandcode", {
|
|
28371
|
+
class: CommandcodeHooks,
|
|
28372
|
+
meta: {
|
|
28373
|
+
supportsProject: true,
|
|
28374
|
+
supportsGlobal: true,
|
|
28375
|
+
supportsImport: true
|
|
28376
|
+
},
|
|
28377
|
+
supportedEvents: COMMANDCODE_HOOK_EVENTS,
|
|
28378
|
+
supportedHookTypes: ["command"],
|
|
28379
|
+
supportsMatcher: true,
|
|
28380
|
+
matcherEvents: ["preToolUse", "postToolUse"]
|
|
28381
|
+
}],
|
|
28077
28382
|
["copilot", {
|
|
28078
28383
|
class: CopilotHooks,
|
|
28079
28384
|
meta: {
|
|
@@ -31931,6 +32236,254 @@ var CodexcliMcp = class CodexcliMcp extends ToolMcp {
|
|
|
31931
32236
|
}
|
|
31932
32237
|
};
|
|
31933
32238
|
//#endregion
|
|
32239
|
+
//#region src/features/mcp/commandcode-mcp.ts
|
|
32240
|
+
/**
|
|
32241
|
+
* Parse a Command Code MCP file. The project file is the `.mcp.json` other
|
|
32242
|
+
* agents (Claude Code among them) share, so a hand-written file is read as
|
|
32243
|
+
* JSONC to tolerate comments and trailing commas; malformed content or a
|
|
32244
|
+
* non-object root (`null`, an array, a scalar) fails closed rather than being
|
|
32245
|
+
* spread into the regenerated file.
|
|
32246
|
+
*/
|
|
32247
|
+
function parseCommandcodeMcpConfig({ fileContent, relativePath }) {
|
|
32248
|
+
let parsed;
|
|
32249
|
+
try {
|
|
32250
|
+
parsed = parseJsonc(fileContent);
|
|
32251
|
+
} catch (error) {
|
|
32252
|
+
throw new Error(`Failed to parse Command Code MCP config at ${relativePath}: ${formatError(error)}`, { cause: error });
|
|
32253
|
+
}
|
|
32254
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Command Code MCP config at ${relativePath}: expected a JSON object at the root`);
|
|
32255
|
+
return parsed;
|
|
32256
|
+
}
|
|
32257
|
+
/**
|
|
32258
|
+
* The remote transport Command Code reads a server as. Its loader accepts
|
|
32259
|
+
* `http` (streamable HTTP) and `sse` for a `url` server; a bare `url` defaults
|
|
32260
|
+
* to `http`, and the canonical `streamable-http` spelling is folded into it.
|
|
32261
|
+
* A `ws(s)://` URL or any other stated transport has no Command Code
|
|
32262
|
+
* equivalent, so `undefined` tells the caller to skip the server.
|
|
32263
|
+
* @see https://commandcode.ai/docs/mcp
|
|
32264
|
+
*/
|
|
32265
|
+
function asCommandcodeRemoteTransport(stated, url) {
|
|
32266
|
+
if (stated === "sse") return "sse";
|
|
32267
|
+
if (stated === "http" || stated === "streamable-http") return "http";
|
|
32268
|
+
if (stated === void 0) return /^wss?:\/\//i.test(url) ? void 0 : "http";
|
|
32269
|
+
}
|
|
32270
|
+
/**
|
|
32271
|
+
* Convert the canonical server map to the shape Command Code's config schema
|
|
32272
|
+
* documents (used for the global `~/.commandcode/mcp.json` only — the project
|
|
32273
|
+
* `.mcp.json` is shared with Claude Code and written pass-through, see the
|
|
32274
|
+
* class doc): a remote server is `{ transport, url, headers?, env?, oauth? }`
|
|
32275
|
+
* and a stdio server is `{ transport: "stdio", command, args?, env? }`, each
|
|
32276
|
+
* with an optional `enabled` flag. The documented spelling is `transport`
|
|
32277
|
+
* (`type` is only accepted as an alias on read), so that is what is written,
|
|
32278
|
+
* and only the documented keys are emitted — `timeout`, rulesync-only fields
|
|
32279
|
+
* and anything else is left out. The canonical `disabled: true` becomes
|
|
32280
|
+
* Command Code's native `enabled: false` (the rulesync-source-only `enabled`
|
|
32281
|
+
* filter never reaches this function: `getMcpServers()` drops such a server).
|
|
32282
|
+
*
|
|
32283
|
+
* A server Command Code drops at load time — no transport at all, a remote
|
|
32284
|
+
* transport without a URL, a WebSocket URL, or a stdio entry without a
|
|
32285
|
+
* command — is skipped with a warning rather than written in a form the tool
|
|
32286
|
+
* would silently ignore.
|
|
32287
|
+
* @see https://commandcode.ai/docs/mcp
|
|
32288
|
+
*/
|
|
32289
|
+
function convertToCommandcodeFormat(mcpServers, logger) {
|
|
32290
|
+
const result = {};
|
|
32291
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
32292
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
32293
|
+
if (declaresNoTransport(serverConfig)) {
|
|
32294
|
+
warnAndSkipMcpServer({
|
|
32295
|
+
toolName: "Command Code",
|
|
32296
|
+
serverName,
|
|
32297
|
+
reason: "no transport",
|
|
32298
|
+
logger
|
|
32299
|
+
});
|
|
32300
|
+
continue;
|
|
32301
|
+
}
|
|
32302
|
+
const converted = isRemoteMcpServer(serverConfig) ? convertRemoteServer$1({
|
|
32303
|
+
serverName,
|
|
32304
|
+
serverConfig,
|
|
32305
|
+
logger
|
|
32306
|
+
}) : convertStdioServer$1({
|
|
32307
|
+
serverName,
|
|
32308
|
+
serverConfig,
|
|
32309
|
+
logger
|
|
32310
|
+
});
|
|
32311
|
+
if (converted === void 0) continue;
|
|
32312
|
+
if (serverConfig.disabled === true) converted.enabled = false;
|
|
32313
|
+
if (isRecord$1(serverConfig.env)) converted.env = omitPrototypePollutionKeys(serverConfig.env);
|
|
32314
|
+
result[serverName] = converted;
|
|
32315
|
+
}
|
|
32316
|
+
return result;
|
|
32317
|
+
}
|
|
32318
|
+
function convertRemoteServer$1({ serverName, serverConfig, logger }) {
|
|
32319
|
+
const url = resolveRemoteMcpUrl(serverConfig);
|
|
32320
|
+
if (!url) {
|
|
32321
|
+
warnAndSkipMcpServer({
|
|
32322
|
+
toolName: "Command Code",
|
|
32323
|
+
serverName,
|
|
32324
|
+
reason: "a remote transport without a url",
|
|
32325
|
+
logger
|
|
32326
|
+
});
|
|
32327
|
+
return;
|
|
32328
|
+
}
|
|
32329
|
+
const stated = serverConfig.type ?? serverConfig.transport;
|
|
32330
|
+
const transport = asCommandcodeRemoteTransport(typeof stated === "string" ? stated : void 0, url);
|
|
32331
|
+
if (transport === void 0) {
|
|
32332
|
+
warnAndSkipMcpServer({
|
|
32333
|
+
toolName: "Command Code",
|
|
32334
|
+
serverName,
|
|
32335
|
+
reason: stated === void 0 ? "a WebSocket url, which Command Code's remote transports (http and sse) cannot reach" : `the "${String(stated)}" transport, which Command Code does not offer for remote servers (only http and sse)`,
|
|
32336
|
+
logger
|
|
32337
|
+
});
|
|
32338
|
+
return;
|
|
32339
|
+
}
|
|
32340
|
+
const converted = {
|
|
32341
|
+
transport,
|
|
32342
|
+
url
|
|
32343
|
+
};
|
|
32344
|
+
if (isRecord$1(serverConfig.headers)) converted.headers = omitPrototypePollutionKeys(serverConfig.headers);
|
|
32345
|
+
if (isRecord$1(serverConfig.oauth)) converted.oauth = omitPrototypePollutionKeys(serverConfig.oauth);
|
|
32346
|
+
return converted;
|
|
32347
|
+
}
|
|
32348
|
+
function convertStdioServer$1({ serverName, serverConfig, logger }) {
|
|
32349
|
+
const [command, ...args] = resolveLocalMcpCommand(serverConfig);
|
|
32350
|
+
if (!command) {
|
|
32351
|
+
warnAndSkipMcpServer({
|
|
32352
|
+
toolName: "Command Code",
|
|
32353
|
+
serverName,
|
|
32354
|
+
reason: "a stdio transport without a command",
|
|
32355
|
+
logger
|
|
32356
|
+
});
|
|
32357
|
+
return;
|
|
32358
|
+
}
|
|
32359
|
+
const converted = {
|
|
32360
|
+
transport: "stdio",
|
|
32361
|
+
command
|
|
32362
|
+
};
|
|
32363
|
+
if (args.length > 0) converted.args = args;
|
|
32364
|
+
return converted;
|
|
32365
|
+
}
|
|
32366
|
+
/**
|
|
32367
|
+
* Convert Command Code's server map back to the canonical shape. Both
|
|
32368
|
+
* spellings Command Code accepts (`transport` and its `type` alias, with
|
|
32369
|
+
* `command`/`url`) are already canonical, so entries pass through with only
|
|
32370
|
+
* prototype-pollution keys dropped; the native `enabled: false` maps to the
|
|
32371
|
+
* canonical `disabled: true` (a bare `enabled` would otherwise be read back
|
|
32372
|
+
* as rulesync's own generation filter and silently drop the server from every
|
|
32373
|
+
* other target).
|
|
32374
|
+
*/
|
|
32375
|
+
function convertFromCommandcodeFormat(mcpServers) {
|
|
32376
|
+
if (!isMcpServers(mcpServers)) return {};
|
|
32377
|
+
const result = {};
|
|
32378
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
32379
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
32380
|
+
const { enabled, ...rest } = omitPrototypePollutionKeys(serverConfig);
|
|
32381
|
+
result[serverName] = enabled === false ? {
|
|
32382
|
+
...rest,
|
|
32383
|
+
disabled: true
|
|
32384
|
+
} : rest;
|
|
32385
|
+
}
|
|
32386
|
+
return result;
|
|
32387
|
+
}
|
|
32388
|
+
/**
|
|
32389
|
+
* Command Code MCP configuration.
|
|
32390
|
+
*
|
|
32391
|
+
* Command Code reads `.mcp.json` at the project root (project scope, meant to
|
|
32392
|
+
* be committed) and `~/.commandcode/mcp.json` (user scope), both in the
|
|
32393
|
+
* `{ "mcpServers": { ... } }` shape the docs show; the per-machine local
|
|
32394
|
+
* scope under `~/.commandcode/projects/` is left to the tool. Top-level
|
|
32395
|
+
* sibling keys of an existing file are kept. The global file is not deleted
|
|
32396
|
+
* by `--delete` (it lives outside the project), while the project file is
|
|
32397
|
+
* rulesync's own and is.
|
|
32398
|
+
*
|
|
32399
|
+
* The project `.mcp.json` is the very file the `claudecode` target writes, so
|
|
32400
|
+
* at project scope the servers are written in the same pass-through shape
|
|
32401
|
+
* `ClaudecodeMcp` uses: whichever of the two targets generates last leaves
|
|
32402
|
+
* byte-identical content, and Command Code reads that shape natively (`type`
|
|
32403
|
+
* is an alias of `transport`, a bare `url` infers `http`, and unknown keys
|
|
32404
|
+
* are ignored). Only the global file — Command Code's own — gets the
|
|
32405
|
+
* `transport` / `enabled: false` rewrite of `convertToCommandcodeFormat`.
|
|
32406
|
+
*
|
|
32407
|
+
* @see https://commandcode.ai/docs/mcp
|
|
32408
|
+
*/
|
|
32409
|
+
var CommandcodeMcp = class CommandcodeMcp extends ToolMcp {
|
|
32410
|
+
json;
|
|
32411
|
+
constructor(params) {
|
|
32412
|
+
super(params);
|
|
32413
|
+
this.json = this.fileContent === void 0 ? {} : parseCommandcodeMcpConfig({
|
|
32414
|
+
fileContent: this.fileContent,
|
|
32415
|
+
relativePath: join(this.relativeDirPath, this.relativeFilePath)
|
|
32416
|
+
});
|
|
32417
|
+
}
|
|
32418
|
+
getJson() {
|
|
32419
|
+
return this.json;
|
|
32420
|
+
}
|
|
32421
|
+
isDeletable() {
|
|
32422
|
+
return !this.global;
|
|
32423
|
+
}
|
|
32424
|
+
static getSettablePaths({ global = false } = {}) {
|
|
32425
|
+
return global ? {
|
|
32426
|
+
relativeDirPath: COMMANDCODE_DIR,
|
|
32427
|
+
relativeFilePath: COMMANDCODE_GLOBAL_MCP_FILE_NAME
|
|
32428
|
+
} : {
|
|
32429
|
+
relativeDirPath: ".",
|
|
32430
|
+
relativeFilePath: COMMANDCODE_PROJECT_MCP_FILE_NAME
|
|
32431
|
+
};
|
|
32432
|
+
}
|
|
32433
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
32434
|
+
const paths = this.getSettablePaths({ global });
|
|
32435
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"mcpServers\":{}}";
|
|
32436
|
+
return new CommandcodeMcp({
|
|
32437
|
+
outputRoot,
|
|
32438
|
+
relativeDirPath: paths.relativeDirPath,
|
|
32439
|
+
relativeFilePath: paths.relativeFilePath,
|
|
32440
|
+
fileContent,
|
|
32441
|
+
validate,
|
|
32442
|
+
global
|
|
32443
|
+
});
|
|
32444
|
+
}
|
|
32445
|
+
static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false, logger }) {
|
|
32446
|
+
const paths = this.getSettablePaths({ global });
|
|
32447
|
+
const json = parseCommandcodeMcpConfig({
|
|
32448
|
+
fileContent: await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"mcpServers\":{}}",
|
|
32449
|
+
relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
|
|
32450
|
+
});
|
|
32451
|
+
const mcpServers = global ? convertToCommandcodeFormat(rulesyncMcp.getMcpServers(), logger) : rulesyncMcp.getMcpServers();
|
|
32452
|
+
const commandcodeConfig = {
|
|
32453
|
+
...json,
|
|
32454
|
+
mcpServers
|
|
32455
|
+
};
|
|
32456
|
+
return new CommandcodeMcp({
|
|
32457
|
+
outputRoot,
|
|
32458
|
+
relativeDirPath: paths.relativeDirPath,
|
|
32459
|
+
relativeFilePath: paths.relativeFilePath,
|
|
32460
|
+
fileContent: JSON.stringify(commandcodeConfig, null, 2),
|
|
32461
|
+
validate,
|
|
32462
|
+
global
|
|
32463
|
+
});
|
|
32464
|
+
}
|
|
32465
|
+
toRulesyncMcp() {
|
|
32466
|
+
const mcpServers = convertFromCommandcodeFormat(this.json.mcpServers);
|
|
32467
|
+
return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
|
|
32468
|
+
}
|
|
32469
|
+
validate() {
|
|
32470
|
+
return {
|
|
32471
|
+
success: true,
|
|
32472
|
+
error: null
|
|
32473
|
+
};
|
|
32474
|
+
}
|
|
32475
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
32476
|
+
return new CommandcodeMcp({
|
|
32477
|
+
outputRoot,
|
|
32478
|
+
relativeDirPath,
|
|
32479
|
+
relativeFilePath,
|
|
32480
|
+
fileContent: "{}",
|
|
32481
|
+
validate: false,
|
|
32482
|
+
global
|
|
32483
|
+
});
|
|
32484
|
+
}
|
|
32485
|
+
};
|
|
32486
|
+
//#endregion
|
|
31934
32487
|
//#region src/features/mcp/continue-mcp.ts
|
|
31935
32488
|
/**
|
|
31936
32489
|
* Parse a Continue MCP file. Continue reads the files under `mcpServers/` as
|
|
@@ -36899,6 +37452,7 @@ var RovodevMcp = class RovodevMcp extends ToolMcp {
|
|
|
36899
37452
|
static async getAuxiliaryFiles({ outputRoot = process.cwd(), global = false, rulesyncMcp, logger }) {
|
|
36900
37453
|
const servers = rulesyncMcp.forTarget({
|
|
36901
37454
|
toolTarget: "rovodev",
|
|
37455
|
+
global,
|
|
36902
37456
|
logger
|
|
36903
37457
|
}).getMcpServers();
|
|
36904
37458
|
const managedNames = Object.keys(servers).filter((name) => toRovodevServer(name, servers[name]) !== null);
|
|
@@ -38196,6 +38750,15 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
38196
38750
|
supportsDisabledTools: true
|
|
38197
38751
|
}
|
|
38198
38752
|
}],
|
|
38753
|
+
["commandcode", {
|
|
38754
|
+
class: CommandcodeMcp,
|
|
38755
|
+
meta: {
|
|
38756
|
+
supportsProject: true,
|
|
38757
|
+
supportsGlobal: true,
|
|
38758
|
+
supportsEnabledTools: false,
|
|
38759
|
+
supportsDisabledTools: false
|
|
38760
|
+
}
|
|
38761
|
+
}],
|
|
38199
38762
|
["continue", {
|
|
38200
38763
|
class: ContinueMcp,
|
|
38201
38764
|
meta: {
|
|
@@ -38589,6 +39152,7 @@ var McpProcessor = class extends FeatureProcessor {
|
|
|
38589
39152
|
const toolMcps = await Promise.all([rulesyncMcp].map(async (mcp) => {
|
|
38590
39153
|
const targetedRulesyncMcp = mcp.forTarget({
|
|
38591
39154
|
toolTarget: this.toolTarget,
|
|
39155
|
+
global: this.global,
|
|
38592
39156
|
logger: this.logger
|
|
38593
39157
|
});
|
|
38594
39158
|
const fieldsToStrip = [];
|
|
@@ -42583,6 +43147,622 @@ function mapBashActionToDecision(action) {
|
|
|
42583
43147
|
return "forbidden";
|
|
42584
43148
|
}
|
|
42585
43149
|
//#endregion
|
|
43150
|
+
//#region src/features/permissions/commandcode-permissions.ts
|
|
43151
|
+
/** Top-level key of `.commandcode/settings.json` that rulesync owns. */
|
|
43152
|
+
const COMMANDCODE_PERMISSIONS_KEY = "permissions";
|
|
43153
|
+
const CATCH_ALL_PATTERN$6 = "*";
|
|
43154
|
+
const MCP_CANONICAL_PREFIX$5 = "mcp__";
|
|
43155
|
+
const COMMANDCODE_ALL_MCP_RULE = "mcp__*";
|
|
43156
|
+
const CATEGORY_TO_COMMANDCODE_TOOL = {
|
|
43157
|
+
bash: "Shell",
|
|
43158
|
+
read: "Read",
|
|
43159
|
+
edit: "Edit",
|
|
43160
|
+
write: "Write",
|
|
43161
|
+
grep: "Grep",
|
|
43162
|
+
glob: "Glob",
|
|
43163
|
+
webfetch: "WebFetch",
|
|
43164
|
+
websearch: "WebSearch"
|
|
43165
|
+
};
|
|
43166
|
+
const COMMANDCODE_TOOL_TO_CATEGORY = {
|
|
43167
|
+
...Object.fromEntries(Object.entries(CATEGORY_TO_COMMANDCODE_TOOL).map(([category, tool]) => [tool.toLowerCase(), category])),
|
|
43168
|
+
bash: "bash",
|
|
43169
|
+
powershell: "bash",
|
|
43170
|
+
shell_command: "bash",
|
|
43171
|
+
monitor_command: "bash",
|
|
43172
|
+
kill_shell: "bash",
|
|
43173
|
+
notebookedit: "edit",
|
|
43174
|
+
write_file: "write",
|
|
43175
|
+
web_fetch: "webfetch",
|
|
43176
|
+
web_search: "websearch"
|
|
43177
|
+
};
|
|
43178
|
+
const ACTION_RANK$1 = {
|
|
43179
|
+
allow: 0,
|
|
43180
|
+
ask: 1,
|
|
43181
|
+
deny: 2
|
|
43182
|
+
};
|
|
43183
|
+
/** Whether `action` outranks `existing` (deny > ask > allow). */
|
|
43184
|
+
function isStricterAction({ action, existing }) {
|
|
43185
|
+
return existing === void 0 || ACTION_RANK$1[action] > ACTION_RANK$1[existing];
|
|
43186
|
+
}
|
|
43187
|
+
/**
|
|
43188
|
+
* Why a padded `Shell( * )` cannot stand in for the bare `Shell` in `allow`:
|
|
43189
|
+
* the whitespace makes it a pattern rule, and a shell pattern grants only
|
|
43190
|
+
* commands that pass Command Code's pattern gate.
|
|
43191
|
+
*/
|
|
43192
|
+
const PADDED_SHELL_WILDCARD_REASON = "a pattern narrower than the bare 'Shell' (the command must parse into words and carry no environment assignment)";
|
|
43193
|
+
/**
|
|
43194
|
+
* Build a Command Code rule (`Shell(git *)`, `Read`, `mcp__github__get_issue`,
|
|
43195
|
+
* `mcp__*`, `*`) from a canonical category + pattern. Returns `null` for
|
|
43196
|
+
* categories Command Code cannot express so the caller can skip them.
|
|
43197
|
+
*
|
|
43198
|
+
* MCP tools are keyed by their canonical `mcp__server__tool` name, so a
|
|
43199
|
+
* scoped `mcp__<remainder>` category is written verbatim and the bare `mcp`
|
|
43200
|
+
* category becomes `mcp__*` (or `mcp__<pattern>` when the pattern names a
|
|
43201
|
+
* server or tool). A pattern on an MCP category is still written as the
|
|
43202
|
+
* `(specifier)` here; `writableCommandcodeRule` decides what to do with it,
|
|
43203
|
+
* because Command Code ignores it. The all-tools `*` category is only
|
|
43204
|
+
* written for its catch-all pattern; narrower `*` patterns are shell
|
|
43205
|
+
* restrictions and reach the `Shell` entries through `honorAllToolsOnBash`.
|
|
43206
|
+
*/
|
|
43207
|
+
function buildCommandcodeRule(category, pattern) {
|
|
43208
|
+
const catchAll = pattern === CATCH_ALL_PATTERN$6 || pattern === "";
|
|
43209
|
+
if (category.startsWith(MCP_CANONICAL_PREFIX$5)) return catchAll ? category : `${category}(${pattern})`;
|
|
43210
|
+
if (category === "mcp") return catchAll ? COMMANDCODE_ALL_MCP_RULE : `${MCP_CANONICAL_PREFIX$5}${pattern}`;
|
|
43211
|
+
if (category === "*") return catchAll ? CATCH_ALL_PATTERN$6 : null;
|
|
43212
|
+
const tool = Object.hasOwn(CATEGORY_TO_COMMANDCODE_TOOL, category) ? CATEGORY_TO_COMMANDCODE_TOOL[category] : void 0;
|
|
43213
|
+
if (tool === void 0) return null;
|
|
43214
|
+
return catchAll ? tool : `${tool}(${pattern})`;
|
|
43215
|
+
}
|
|
43216
|
+
/**
|
|
43217
|
+
* Split `Tool(specifier)` into its two halves the way Command Code's
|
|
43218
|
+
* `splitRule` does (`command-code` 1.54.0): the rule is split at the first
|
|
43219
|
+
* unescaped `(` and must end with `)`, the tool half is trimmed (so
|
|
43220
|
+
* `Shell (rm -rf *)` is a `Shell` rule), and a `\(` / `\)` in the specifier
|
|
43221
|
+
* is an escaped parenthesis. `Tool` alone has an empty specifier. A rule that
|
|
43222
|
+
* opens a parenthesis without closing one is not a rule to Command Code at
|
|
43223
|
+
* all, so it comes back with an empty tool half and stays unmodeled. Nothing
|
|
43224
|
+
* else in the specifier is touched: Command Code does not trim it (the shell
|
|
43225
|
+
* matcher normalizes whitespace on its own, see `parseCommandcodeRule`), so
|
|
43226
|
+
* `Read( )` is a rule for the pattern `" "`, which matches nothing, not for
|
|
43227
|
+
* the whole tool.
|
|
43228
|
+
*/
|
|
43229
|
+
function splitCommandcodeRule(rule) {
|
|
43230
|
+
const trimmed = rule.trim();
|
|
43231
|
+
const parenIndex = trimmed.search(/(?<!\\)\(/);
|
|
43232
|
+
if (parenIndex === -1) return {
|
|
43233
|
+
tool: trimmed,
|
|
43234
|
+
inner: ""
|
|
43235
|
+
};
|
|
43236
|
+
if (!trimmed.endsWith(")")) return {
|
|
43237
|
+
tool: "",
|
|
43238
|
+
inner: ""
|
|
43239
|
+
};
|
|
43240
|
+
return {
|
|
43241
|
+
tool: trimmed.slice(0, parenIndex).trim(),
|
|
43242
|
+
inner: trimmed.slice(parenIndex + 1, -1).replace(/\\([()])/g, "$1")
|
|
43243
|
+
};
|
|
43244
|
+
}
|
|
43245
|
+
/** Whether a specifier means "no specifier" to Command Code: only `` and `*` do. */
|
|
43246
|
+
function isCatchAllSpecifier(inner) {
|
|
43247
|
+
return inner === "" || inner === CATCH_ALL_PATTERN$6;
|
|
43248
|
+
}
|
|
43249
|
+
/**
|
|
43250
|
+
* Read an MCP-shaped rule the way Command Code does (`command-code` 1.54.0,
|
|
43251
|
+
* `parsePermissionRule` / `parseMcpToken`). The MCP shape is only recognized
|
|
43252
|
+
* by its exact `mcp__` prefix, and such a rule never carries a specifier:
|
|
43253
|
+
* `mcp__<server>__<tool>(owner:foo)` denies, asks or allows the whole tool.
|
|
43254
|
+
* Its names are matched case-sensitively against the registered server and
|
|
43255
|
+
* tool (Command Code never lowercases them), so the remainder keeps its case
|
|
43256
|
+
* as the canonical category. `mcp__<server>`, `mcp__<server>__` and
|
|
43257
|
+
* `mcp__<server>__*` are the whole server (category `mcp__<server>`), and a
|
|
43258
|
+
* `*` server (`mcp__*`, `mcp__*__<tool>`) is every MCP tool, honored in
|
|
43259
|
+
* `deny`/`ask` only. A differently-cased `MCP__...` spelling is read as a
|
|
43260
|
+
* plain tool-name rule instead, matched case-insensitively:
|
|
43261
|
+
* `MCP__<server>__<tool>` matches that tool by name (so it folds onto the
|
|
43262
|
+
* `mcp__` category, remainder as written), `MCP__<server>__<tool>(specifier)`
|
|
43263
|
+
* globs the specifier against the call's `command` / `file_path` / `path` /
|
|
43264
|
+
* `url` / `pattern` argument (in `deny`/`ask` a `param:glob` form is tried
|
|
43265
|
+
* first) — kept as a pattern in `deny`/`ask`, but not imported from `allow`,
|
|
43266
|
+
* where a grant scoped that way cannot be modeled without widening it; the
|
|
43267
|
+
* globs
|
|
43268
|
+
* `MCP__*` (every MCP tool) and `MCP__<server>__*` (the whole server) match
|
|
43269
|
+
* by name in `deny`/`ask` only, and `MCP__<server>` names no tool at all.
|
|
43270
|
+
* Whatever matches nothing anywhere (or a glob rulesync does not model)
|
|
43271
|
+
* returns `null` and is left alone.
|
|
43272
|
+
*/
|
|
43273
|
+
function parseMcpCommandcodeRule({ tool, inner }) {
|
|
43274
|
+
const remainder = tool.slice(5);
|
|
43275
|
+
const separator = remainder.indexOf("__");
|
|
43276
|
+
const server = separator === -1 ? remainder : remainder.slice(0, separator);
|
|
43277
|
+
const rest = separator === -1 ? "" : remainder.slice(separator + 2);
|
|
43278
|
+
if (server.length === 0) return null;
|
|
43279
|
+
const everyServer = {
|
|
43280
|
+
category: "mcp",
|
|
43281
|
+
pattern: CATCH_ALL_PATTERN$6,
|
|
43282
|
+
notInAllow: true
|
|
43283
|
+
};
|
|
43284
|
+
if (tool.startsWith(MCP_CANONICAL_PREFIX$5)) {
|
|
43285
|
+
if (server === CATCH_ALL_PATTERN$6) return everyServer;
|
|
43286
|
+
if (server.includes(CATCH_ALL_PATTERN$6)) return null;
|
|
43287
|
+
return {
|
|
43288
|
+
category: rest === "" || rest === CATCH_ALL_PATTERN$6 ? `${MCP_CANONICAL_PREFIX$5}${server}` : `${MCP_CANONICAL_PREFIX$5}${remainder}`,
|
|
43289
|
+
pattern: CATCH_ALL_PATTERN$6,
|
|
43290
|
+
notInAllow: false
|
|
43291
|
+
};
|
|
43292
|
+
}
|
|
43293
|
+
const category = `${MCP_CANONICAL_PREFIX$5}${remainder}`;
|
|
43294
|
+
const namesTool = separator !== -1 && rest.length > 0 && !remainder.includes(CATCH_ALL_PATTERN$6);
|
|
43295
|
+
if (!isCatchAllSpecifier(inner)) return namesTool ? {
|
|
43296
|
+
category,
|
|
43297
|
+
pattern: inner,
|
|
43298
|
+
notInAllow: true
|
|
43299
|
+
} : null;
|
|
43300
|
+
if (remainder === CATCH_ALL_PATTERN$6) return everyServer;
|
|
43301
|
+
if (rest === CATCH_ALL_PATTERN$6 && !server.includes(CATCH_ALL_PATTERN$6)) return {
|
|
43302
|
+
category: `${MCP_CANONICAL_PREFIX$5}${server}`,
|
|
43303
|
+
pattern: CATCH_ALL_PATTERN$6,
|
|
43304
|
+
notInAllow: true
|
|
43305
|
+
};
|
|
43306
|
+
return namesTool ? {
|
|
43307
|
+
category,
|
|
43308
|
+
pattern: CATCH_ALL_PATTERN$6,
|
|
43309
|
+
notInAllow: false
|
|
43310
|
+
} : null;
|
|
43311
|
+
}
|
|
43312
|
+
/**
|
|
43313
|
+
* A rule on one of the friendly tools, with its specifier read as Command
|
|
43314
|
+
* Code matches it. The shell matcher trims and collapses whitespace on both
|
|
43315
|
+
* the pattern and the command, so `Shell( git * )` is `Shell(git *)` and a
|
|
43316
|
+
* pattern that is blank once normalized (`Shell( )`), or a bare `:*` whose
|
|
43317
|
+
* prefix is empty (`Shell(:*)`), matches nothing. A padded `Shell( * )` is
|
|
43318
|
+
* not the bare `Shell`: only a specifier of exactly
|
|
43319
|
+
* `` or `*` is dropped by the rule parser, so `( * )` stays a pattern rule —
|
|
43320
|
+
* in `deny`/`ask` it matches every command (the whole tool), while in `allow`
|
|
43321
|
+
* it is narrower than the bare `Shell` (it needs a command that parses into
|
|
43322
|
+
* words and has no environment-assignment prefix), so it is the whole tool
|
|
43323
|
+
* marked `notInAllow`. The path, web and tool-name matchers use the
|
|
43324
|
+
* specifier as written, so one that is blank or `*` only once trimmed
|
|
43325
|
+
* (`Read( * )`) matches nothing. What matches nothing comes back as `null`:
|
|
43326
|
+
* the rule is unmodeled and left alone.
|
|
43327
|
+
*/
|
|
43328
|
+
function friendlyToolRule({ category, inner }) {
|
|
43329
|
+
if (category === "bash") {
|
|
43330
|
+
if (isCatchAllSpecifier(inner)) return {
|
|
43331
|
+
category,
|
|
43332
|
+
pattern: CATCH_ALL_PATTERN$6,
|
|
43333
|
+
notInAllow: false
|
|
43334
|
+
};
|
|
43335
|
+
const normalized = inner.trim().replace(/\s+/g, " ");
|
|
43336
|
+
if (normalized === "" || normalized === ":*") return null;
|
|
43337
|
+
return isCatchAllSpecifier(normalized) ? {
|
|
43338
|
+
category,
|
|
43339
|
+
pattern: CATCH_ALL_PATTERN$6,
|
|
43340
|
+
notInAllow: true
|
|
43341
|
+
} : {
|
|
43342
|
+
category,
|
|
43343
|
+
pattern: normalized,
|
|
43344
|
+
notInAllow: false
|
|
43345
|
+
};
|
|
43346
|
+
}
|
|
43347
|
+
if (isCatchAllSpecifier(inner)) return {
|
|
43348
|
+
category,
|
|
43349
|
+
pattern: CATCH_ALL_PATTERN$6,
|
|
43350
|
+
notInAllow: false
|
|
43351
|
+
};
|
|
43352
|
+
return isCatchAllSpecifier(inner.trim()) ? null : {
|
|
43353
|
+
category,
|
|
43354
|
+
pattern: inner,
|
|
43355
|
+
notInAllow: false
|
|
43356
|
+
};
|
|
43357
|
+
}
|
|
43358
|
+
/**
|
|
43359
|
+
* Parse a Command Code rule back into canonical terms. Tool names fold case;
|
|
43360
|
+
* `Tool`, `Tool()` and `Tool(*)` all mean the whole tool; MCP-shaped rules
|
|
43361
|
+
* go through `parseMcpCommandcodeRule`. Returns `null` for a rule rulesync
|
|
43362
|
+
* cannot model (an exact internal tool name such as `edit_file`, a
|
|
43363
|
+
* name-wildcard like `edit_*`, a specifier on a rule that takes none, or a
|
|
43364
|
+
* rule Command Code enforces nothing for, such as `Agent`).
|
|
43365
|
+
*/
|
|
43366
|
+
function parseCommandcodeRule(rule) {
|
|
43367
|
+
const { tool, inner } = splitCommandcodeRule(rule);
|
|
43368
|
+
if (tool === CATCH_ALL_PATTERN$6) return isCatchAllSpecifier(inner) ? {
|
|
43369
|
+
category: "*",
|
|
43370
|
+
pattern: CATCH_ALL_PATTERN$6,
|
|
43371
|
+
notInAllow: true
|
|
43372
|
+
} : null;
|
|
43373
|
+
const lowered = tool.toLowerCase();
|
|
43374
|
+
if (lowered.startsWith(MCP_CANONICAL_PREFIX$5)) return parseMcpCommandcodeRule({
|
|
43375
|
+
tool,
|
|
43376
|
+
inner
|
|
43377
|
+
});
|
|
43378
|
+
const category = Object.hasOwn(COMMANDCODE_TOOL_TO_CATEGORY, lowered) ? COMMANDCODE_TOOL_TO_CATEGORY[lowered] : void 0;
|
|
43379
|
+
if (category === void 0) return null;
|
|
43380
|
+
return friendlyToolRule({
|
|
43381
|
+
category,
|
|
43382
|
+
inner
|
|
43383
|
+
});
|
|
43384
|
+
}
|
|
43385
|
+
/**
|
|
43386
|
+
* Whether an imported rule is a differently-cased `MCP__<server>__<tool>`
|
|
43387
|
+
* name that folded onto an exact-prefix `mcp__` category (which Command Code
|
|
43388
|
+
* matches case-sensitively, unlike the name rule it came from).
|
|
43389
|
+
*/
|
|
43390
|
+
function isFoldedMcpToolNameRule({ rule, category }) {
|
|
43391
|
+
const { tool } = splitCommandcodeRule(rule);
|
|
43392
|
+
return category.startsWith(MCP_CANONICAL_PREFIX$5) && !tool.startsWith(MCP_CANONICAL_PREFIX$5);
|
|
43393
|
+
}
|
|
43394
|
+
/** The `Tool` half of `Tool(specifier)`, when the rule is an exact-prefix MCP rule with a specifier. */
|
|
43395
|
+
function scopedMcpRuleTool(rule) {
|
|
43396
|
+
const { tool, inner } = splitCommandcodeRule(rule);
|
|
43397
|
+
return !isCatchAllSpecifier(inner) && tool.startsWith(MCP_CANONICAL_PREFIX$5) ? tool : null;
|
|
43398
|
+
}
|
|
43399
|
+
/**
|
|
43400
|
+
* The string entries of a Command Code permission list. Command Code reads
|
|
43401
|
+
* each entry on its own and skips one that is not a string, so a stray
|
|
43402
|
+
* `null` beside `Shell(rm -rf *)` must not throw the deny away with it; the
|
|
43403
|
+
* skipped entries are counted in a warning because they cannot be kept.
|
|
43404
|
+
*/
|
|
43405
|
+
function stringRules({ list, action, outcome, warn }) {
|
|
43406
|
+
if (!Array.isArray(list)) return [];
|
|
43407
|
+
const rules = list.filter((entry) => typeof entry === "string");
|
|
43408
|
+
const skipped = list.length - rules.length;
|
|
43409
|
+
if (skipped > 0) warn(`Command Code permission list "${action}" holds ${skipped} ${skipped === 1 ? "entry" : "entries"} that ${skipped === 1 ? "is" : "are"} not a string, which Command Code skips; ${skipped === 1 ? "that entry was" : "those entries were"} ${outcome}.`);
|
|
43410
|
+
return rules;
|
|
43411
|
+
}
|
|
43412
|
+
/**
|
|
43413
|
+
* The canonical categories this run rebuilds — every category the canonical
|
|
43414
|
+
* config names (an empty `bash: {}` still reclaims the previous `Shell(...)`
|
|
43415
|
+
* entries, as in the Claude Code adapter) plus every rule it emits, keyed the
|
|
43416
|
+
* way an existing entry parses back (so `mcp: { github: "deny" }`, written as
|
|
43417
|
+
* `mcp__github`, claims the `mcp__github` entries of the previous run; case
|
|
43418
|
+
* variants of the friendly tool names fold together, while MCP names are
|
|
43419
|
+
* matched exactly, as Command Code does). An existing entry whose tool folds onto one of them is
|
|
43420
|
+
* rulesync's to replace, whatever list it sits in — otherwise flipping a rule
|
|
43421
|
+
* from deny to allow would leave the old deny behind and win. Every other
|
|
43422
|
+
* entry — an internal tool name rulesync cannot model, or a modeled tool the
|
|
43423
|
+
* canonical config does not mention — is the user's (Command Code writes
|
|
43424
|
+
* interactive approvals into the same lists) and is preserved verbatim.
|
|
43425
|
+
* A `deny`/`ask` entry that is reclaimed without an equally strict rule
|
|
43426
|
+
* taking its place (say a hand-written `mcp__*` deny next to a canonical
|
|
43427
|
+
* `mcp: { github: "deny" }`) is reported, so regenerating never silently
|
|
43428
|
+
* loosens what the user wrote. Mirrors `managedClaudeToolNames` in the
|
|
43429
|
+
* Claude Code adapter.
|
|
43430
|
+
*/
|
|
43431
|
+
function preservedRules({ existingPermissions, key, managedCategories, written, logger }) {
|
|
43432
|
+
return stringRules({
|
|
43433
|
+
list: existingPermissions[key],
|
|
43434
|
+
action: key,
|
|
43435
|
+
outcome: "dropped from the regenerated list",
|
|
43436
|
+
warn: (message) => logger?.warn(message)
|
|
43437
|
+
}).filter((rule) => {
|
|
43438
|
+
const parsed = parseCommandcodeRule(rule);
|
|
43439
|
+
if (parsed === null || !managedCategories.has(parsed.category)) return true;
|
|
43440
|
+
const replacement = written.get(`${parsed.category}(${parsed.pattern})`) ?? written.get(`${parsed.category}(${CATCH_ALL_PATTERN$6})`);
|
|
43441
|
+
if (key !== "allow" && isStricterAction({
|
|
43442
|
+
action: key,
|
|
43443
|
+
existing: replacement
|
|
43444
|
+
})) logger?.warn(`Command Code permission rule '${rule}' in "${key}" belongs to the '${parsed.category}' category, which the rulesync config now manages, and was replaced by its rules.`);
|
|
43445
|
+
return false;
|
|
43446
|
+
});
|
|
43447
|
+
}
|
|
43448
|
+
/**
|
|
43449
|
+
* The rule to write for a canonical rule, or `null` when Command Code would
|
|
43450
|
+
* ignore it in the `action` list. A server-less wildcard (`*`, `mcp__*`) in
|
|
43451
|
+
* `allow` is warned about and left unwritten rather than written dead. A
|
|
43452
|
+
* specifier on an MCP rule is ignored by Command Code in every list, so a
|
|
43453
|
+
* scoped `deny`/`ask` is written as the bare tool (which is what Command
|
|
43454
|
+
* Code would enforce anyway, only now visibly) with a warning, and a scoped
|
|
43455
|
+
* `allow` — which would grant the whole tool — is refused. Judged on the
|
|
43456
|
+
* rule as written, so the bare `mcp` category cannot smuggle a specifier in
|
|
43457
|
+
* through a `<server>__<tool>(specifier)` pattern.
|
|
43458
|
+
*/
|
|
43459
|
+
function writableCommandcodeRule({ rule, emitted, action, logger }) {
|
|
43460
|
+
if (action === "allow" && emitted.notInAllow) {
|
|
43461
|
+
logger?.warn(emitted.category === "bash" ? `Command Code reads '${rule}' in "allow" as ${PADDED_SHELL_WILDCARD_REASON}, so the '${emitted.category}' allow rule was not written; use the '*' pattern for the whole tool.` : `Command Code does not honor '${rule}' in "allow" as written (an allow rule must name what it grants), so the '${emitted.category}' allow rule was not written.`);
|
|
43462
|
+
return null;
|
|
43463
|
+
}
|
|
43464
|
+
const tool = scopedMcpRuleTool(rule);
|
|
43465
|
+
if (tool === null) return rule;
|
|
43466
|
+
if (action === "allow") {
|
|
43467
|
+
logger?.warn(`Command Code ignores the specifier of an MCP rule and would allow the whole '${tool}' tool, so the '${rule}' allow rule was not written.`);
|
|
43468
|
+
return null;
|
|
43469
|
+
}
|
|
43470
|
+
logger?.warn(`Command Code ignores the specifier of an MCP rule, so '${rule}' was written as '${tool}' and applies "${action}" to the whole tool.`);
|
|
43471
|
+
return tool;
|
|
43472
|
+
}
|
|
43473
|
+
/**
|
|
43474
|
+
* Record `action` for `rule`, keeping the stricter one (deny > ask > allow)
|
|
43475
|
+
* when two canonical rules collapse onto the same Command Code entry.
|
|
43476
|
+
*/
|
|
43477
|
+
function rankCommandcodeRule({ ranked, rule, action, logger }) {
|
|
43478
|
+
const existing = ranked.get(rule);
|
|
43479
|
+
if (existing !== void 0 && existing !== action) logger?.warn(`Command Code permission rule '${rule}' received conflicting actions ('${existing}' and '${action}'); keeping the stricter one (deny > ask > allow).`);
|
|
43480
|
+
if (isStricterAction({
|
|
43481
|
+
action,
|
|
43482
|
+
existing
|
|
43483
|
+
})) ranked.set(rule, action);
|
|
43484
|
+
}
|
|
43485
|
+
/**
|
|
43486
|
+
* Bucket the canonical rules into Command Code's `allow`/`ask`/`deny` lists.
|
|
43487
|
+
* Collisions resolve to the strictest action (deny > ask > allow); categories
|
|
43488
|
+
* Command Code cannot express are skipped with a warning when they carry a
|
|
43489
|
+
* `deny`; rules Command Code would ignore in the target list are dropped or
|
|
43490
|
+
* rewritten by `writableCommandcodeRule`.
|
|
43491
|
+
*/
|
|
43492
|
+
function buildCommandcodeRuleLists({ config, existingPermissions, logger }) {
|
|
43493
|
+
const permission = honorAllToolsOnBash(config.permission);
|
|
43494
|
+
const ranked = /* @__PURE__ */ new Map();
|
|
43495
|
+
const managedCategories = /* @__PURE__ */ new Set();
|
|
43496
|
+
for (const [category, rules] of Object.entries(permission)) {
|
|
43497
|
+
const categoryRule = buildCommandcodeRule(category, CATCH_ALL_PATTERN$6);
|
|
43498
|
+
const managedCategory = categoryRule === null ? null : parseCommandcodeRule(categoryRule);
|
|
43499
|
+
if (managedCategory !== null) managedCategories.add(managedCategory.category);
|
|
43500
|
+
for (const [pattern, action] of Object.entries(rules)) {
|
|
43501
|
+
const rule = buildCommandcodeRule(category, pattern);
|
|
43502
|
+
const emitted = rule === null ? null : parseCommandcodeRule(rule);
|
|
43503
|
+
if (rule === null || emitted === null) {
|
|
43504
|
+
const honoredOnShell = category === "*" && permission.bash !== void 0;
|
|
43505
|
+
if (action === "deny" && !honoredOnShell) logger?.warn(`Command Code has no permission rule for the '${category}' category with pattern '${pattern}'; its 'deny' rule could not be represented and was skipped.`);
|
|
43506
|
+
continue;
|
|
43507
|
+
}
|
|
43508
|
+
managedCategories.add(emitted.category);
|
|
43509
|
+
const written = writableCommandcodeRule({
|
|
43510
|
+
rule,
|
|
43511
|
+
emitted,
|
|
43512
|
+
action,
|
|
43513
|
+
logger
|
|
43514
|
+
});
|
|
43515
|
+
if (written !== null) rankCommandcodeRule({
|
|
43516
|
+
ranked,
|
|
43517
|
+
rule: written,
|
|
43518
|
+
action,
|
|
43519
|
+
logger
|
|
43520
|
+
});
|
|
43521
|
+
}
|
|
43522
|
+
}
|
|
43523
|
+
const written = /* @__PURE__ */ new Map();
|
|
43524
|
+
for (const [rule, action] of ranked) {
|
|
43525
|
+
const parsed = parseCommandcodeRule(rule);
|
|
43526
|
+
if (parsed !== null) written.set(`${parsed.category}(${parsed.pattern})`, action);
|
|
43527
|
+
}
|
|
43528
|
+
const preserved = {
|
|
43529
|
+
existingPermissions,
|
|
43530
|
+
managedCategories,
|
|
43531
|
+
written,
|
|
43532
|
+
logger
|
|
43533
|
+
};
|
|
43534
|
+
const allow = preservedRules({
|
|
43535
|
+
...preserved,
|
|
43536
|
+
key: "allow"
|
|
43537
|
+
});
|
|
43538
|
+
const ask = preservedRules({
|
|
43539
|
+
...preserved,
|
|
43540
|
+
key: "ask"
|
|
43541
|
+
});
|
|
43542
|
+
const deny = preservedRules({
|
|
43543
|
+
...preserved,
|
|
43544
|
+
key: "deny"
|
|
43545
|
+
});
|
|
43546
|
+
for (const [rule, action] of ranked) if (action === "allow") allow.push(rule);
|
|
43547
|
+
else if (action === "ask") ask.push(rule);
|
|
43548
|
+
else deny.push(rule);
|
|
43549
|
+
return {
|
|
43550
|
+
allow: uniq(allow.toSorted()),
|
|
43551
|
+
ask: uniq(ask.toSorted()),
|
|
43552
|
+
deny: uniq(deny.toSorted())
|
|
43553
|
+
};
|
|
43554
|
+
}
|
|
43555
|
+
/**
|
|
43556
|
+
* Why a rule marked `notInAllow` is not imported from `allow`: Command Code
|
|
43557
|
+
* ignores a server-less wildcard or a tool-name glob there, while it does
|
|
43558
|
+
* enforce a scoped `MCP__<server>__<tool>(specifier)` — as a glob over the
|
|
43559
|
+
* call's arguments — and a padded `Shell( * )` — as a pattern narrower than
|
|
43560
|
+
* the bare `Shell` — which rulesync cannot model in `allow` without widening
|
|
43561
|
+
* the grant.
|
|
43562
|
+
*/
|
|
43563
|
+
function skippedAllowImportMessage({ rule, category }) {
|
|
43564
|
+
const { tool, inner } = splitCommandcodeRule(rule);
|
|
43565
|
+
if (category === "bash") return `Command Code reads '${rule}' in "allow" as ${PADDED_SHELL_WILDCARD_REASON}, which rulesync cannot import without widening the grant to every command, so it was not imported.`;
|
|
43566
|
+
if (tool.startsWith(MCP_CANONICAL_PREFIX$5) || isCatchAllSpecifier(inner)) return `Command Code ignores '${rule}' in "allow" (an allow rule must name what it grants), so it was not imported.`;
|
|
43567
|
+
return `Command Code globs the specifier of '${rule}' in "allow" against the call's arguments, which rulesync cannot import without widening the grant to the whole tool, so it was not imported.`;
|
|
43568
|
+
}
|
|
43569
|
+
/**
|
|
43570
|
+
* Parse Command Code's `permissions` lists back into a canonical permission
|
|
43571
|
+
* map with `deny > ask > allow` precedence, so a tool described more than
|
|
43572
|
+
* once resolves to the strictest action. Each rule is imported as what
|
|
43573
|
+
* Command Code enforces for it, never as what it looks like: a rule Command
|
|
43574
|
+
* Code ignores in `allow` (`*`, `mcp__*`, an `MCP__...` glob) is skipped
|
|
43575
|
+
* there with a warning, since importing it would turn a dead line into a
|
|
43576
|
+
* live grant for every other target, and so is a scoped
|
|
43577
|
+
* `MCP__<server>__<tool>(specifier)` allow, which Command Code enforces as a
|
|
43578
|
+
* glob over the call's arguments but rulesync cannot import without widening
|
|
43579
|
+
* it to the whole tool; the specifier of an exact-prefix `mcp__...` rule,
|
|
43580
|
+
* which Command Code drops, is folded to the whole tool in every list — with
|
|
43581
|
+
* a warning in `allow`, where the fold widens the grant the user wrote. A
|
|
43582
|
+
* tool-half glob in that exact-prefix spelling (`mcp__<server>__get_*`), which
|
|
43583
|
+
* Command Code matches against tool names in every list, is imported as the
|
|
43584
|
+
* category spelled that way, the same one the generator writes back; other
|
|
43585
|
+
* targets read it as a literal tool name.
|
|
43586
|
+
*/
|
|
43587
|
+
function parseCommandcodeRuleLists(permissions) {
|
|
43588
|
+
const permission = {};
|
|
43589
|
+
const lists = [
|
|
43590
|
+
["allow", permissions.allow],
|
|
43591
|
+
["ask", permissions.ask],
|
|
43592
|
+
["deny", permissions.deny]
|
|
43593
|
+
];
|
|
43594
|
+
for (const [action, rawList] of lists) {
|
|
43595
|
+
const list = stringRules({
|
|
43596
|
+
list: rawList,
|
|
43597
|
+
action,
|
|
43598
|
+
outcome: "not imported",
|
|
43599
|
+
warn: (message) => fallbackLogger.warn(message)
|
|
43600
|
+
});
|
|
43601
|
+
for (const rule of list) {
|
|
43602
|
+
const parsed = parseCommandcodeRule(rule);
|
|
43603
|
+
if (parsed === null || isPrototypePollutionKey(parsed.category) || isPrototypePollutionKey(parsed.pattern)) {
|
|
43604
|
+
if (action !== "allow") fallbackLogger.warn(`Command Code permission rule '${rule}' in "${action}" is not one rulesync can model, so it could not be imported; it stays in the Command Code settings, where a regenerate keeps it unless the canonical config manages its category.`);
|
|
43605
|
+
continue;
|
|
43606
|
+
}
|
|
43607
|
+
const { category, pattern } = parsed;
|
|
43608
|
+
if (action === "allow" && parsed.notInAllow) {
|
|
43609
|
+
fallbackLogger.warn(skippedAllowImportMessage({
|
|
43610
|
+
rule,
|
|
43611
|
+
category
|
|
43612
|
+
}));
|
|
43613
|
+
continue;
|
|
43614
|
+
}
|
|
43615
|
+
if (action === "allow" && scopedMcpRuleTool(rule) !== null) fallbackLogger.warn(`Command Code ignores the specifier of an MCP rule, so '${rule}' in "allow" was imported as the whole '${category}' tool, which is what Command Code grants for it.`);
|
|
43616
|
+
if (isFoldedMcpToolNameRule({
|
|
43617
|
+
rule,
|
|
43618
|
+
category
|
|
43619
|
+
})) 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.`);
|
|
43620
|
+
const rules = permission[category] ??= {};
|
|
43621
|
+
if (isStricterAction({
|
|
43622
|
+
action,
|
|
43623
|
+
existing: rules[pattern]
|
|
43624
|
+
})) rules[pattern] = action;
|
|
43625
|
+
}
|
|
43626
|
+
}
|
|
43627
|
+
return permission;
|
|
43628
|
+
}
|
|
43629
|
+
/**
|
|
43630
|
+
* Permissions generator for Command Code.
|
|
43631
|
+
*
|
|
43632
|
+
* Rules live under the `permissions` key of `.commandcode/settings.json`
|
|
43633
|
+
* (project) and `~/.commandcode/settings.json` (user) as three lists of
|
|
43634
|
+
* Claude-style entries: `deny` (always wins), `ask`, `allow`. The same key
|
|
43635
|
+
* also carries `defaultMode`, `additionalDirectories` and `disableBypass`,
|
|
43636
|
+
* and the file holds `hooks` and other settings, so writes go through the
|
|
43637
|
+
* shared-config gateway: rulesync replaces the three lists, keeps every
|
|
43638
|
+
* sibling key, and never deletes the file.
|
|
43639
|
+
*
|
|
43640
|
+
* Generate: `permission.<category>.<pattern>` becomes `Tool(pattern)` with
|
|
43641
|
+
* the friendly names (`Shell`, `Read`, `Edit`, `Write`, `WebFetch`,
|
|
43642
|
+
* `WebSearch`), `mcp__<server>__<tool>` names pass through, and the all-tools
|
|
43643
|
+
* `*` category becomes the bare `*` rule Command Code accepts in `deny`/`ask`.
|
|
43644
|
+
* Only the entries of the categories the canonical config names are rebuilt;
|
|
43645
|
+
* every other existing entry is preserved verbatim.
|
|
43646
|
+
* Import: the lists are parsed back as what Command Code enforces for each
|
|
43647
|
+
* entry — friendly tool names case-insensitively (with the internal aliases
|
|
43648
|
+
* such as `Bash`, `PowerShell` and `write_file` folding onto their
|
|
43649
|
+
* category), MCP names exactly; rules for internal tool names rulesync does
|
|
43650
|
+
* not model are skipped.
|
|
43651
|
+
*
|
|
43652
|
+
* @see https://commandcode.ai/docs/permissions
|
|
43653
|
+
* @see https://commandcode.ai/docs/settings
|
|
43654
|
+
*/
|
|
43655
|
+
var CommandcodePermissions = class CommandcodePermissions extends ToolPermissions {
|
|
43656
|
+
constructor(params) {
|
|
43657
|
+
super({
|
|
43658
|
+
...params,
|
|
43659
|
+
fileContent: params.fileContent ?? "{}"
|
|
43660
|
+
});
|
|
43661
|
+
}
|
|
43662
|
+
/**
|
|
43663
|
+
* `settings.json` holds hooks and other user settings, so it is never
|
|
43664
|
+
* deleted; clearing permissions happens via an in-place merge.
|
|
43665
|
+
*/
|
|
43666
|
+
isDeletable() {
|
|
43667
|
+
return false;
|
|
43668
|
+
}
|
|
43669
|
+
static getSettablePaths(_options = {}) {
|
|
43670
|
+
return {
|
|
43671
|
+
relativeDirPath: COMMANDCODE_DIR,
|
|
43672
|
+
relativeFilePath: COMMANDCODE_SETTINGS_FILE_NAME
|
|
43673
|
+
};
|
|
43674
|
+
}
|
|
43675
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
43676
|
+
const paths = CommandcodePermissions.getSettablePaths({ global });
|
|
43677
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{}";
|
|
43678
|
+
return new CommandcodePermissions({
|
|
43679
|
+
outputRoot,
|
|
43680
|
+
relativeDirPath: paths.relativeDirPath,
|
|
43681
|
+
relativeFilePath: paths.relativeFilePath,
|
|
43682
|
+
fileContent,
|
|
43683
|
+
validate,
|
|
43684
|
+
global
|
|
43685
|
+
});
|
|
43686
|
+
}
|
|
43687
|
+
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, global = false, logger }) {
|
|
43688
|
+
const paths = CommandcodePermissions.getSettablePaths({ global });
|
|
43689
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
43690
|
+
const existingContent = await readFileContentOrNull(filePath) ?? "{}";
|
|
43691
|
+
const existing = parseCommandcodeSettings({
|
|
43692
|
+
fileContent: existingContent,
|
|
43693
|
+
relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
|
|
43694
|
+
});
|
|
43695
|
+
const existingPermissions = isRecord$1(existing[COMMANDCODE_PERMISSIONS_KEY]) ? existing[COMMANDCODE_PERMISSIONS_KEY] : {};
|
|
43696
|
+
const lists = buildCommandcodeRuleLists({
|
|
43697
|
+
config: rulesyncPermissions.getJson(),
|
|
43698
|
+
existingPermissions,
|
|
43699
|
+
logger
|
|
43700
|
+
});
|
|
43701
|
+
const permissions = { ...existingPermissions };
|
|
43702
|
+
for (const key of [
|
|
43703
|
+
"allow",
|
|
43704
|
+
"ask",
|
|
43705
|
+
"deny"
|
|
43706
|
+
]) if (lists[key].length > 0) permissions[key] = lists[key];
|
|
43707
|
+
else delete permissions[key];
|
|
43708
|
+
return new CommandcodePermissions({
|
|
43709
|
+
outputRoot,
|
|
43710
|
+
relativeDirPath: paths.relativeDirPath,
|
|
43711
|
+
relativeFilePath: paths.relativeFilePath,
|
|
43712
|
+
fileContent: applySharedConfigPatch({
|
|
43713
|
+
fileKey: sharedConfigFileKey(paths),
|
|
43714
|
+
feature: "permissions",
|
|
43715
|
+
existingContent,
|
|
43716
|
+
patch: { [COMMANDCODE_PERMISSIONS_KEY]: permissions },
|
|
43717
|
+
filePath
|
|
43718
|
+
}),
|
|
43719
|
+
validate: true,
|
|
43720
|
+
global
|
|
43721
|
+
});
|
|
43722
|
+
}
|
|
43723
|
+
toRulesyncPermissions() {
|
|
43724
|
+
const settings = parseCommandcodeSettings({
|
|
43725
|
+
fileContent: this.getFileContent() || "{}",
|
|
43726
|
+
relativePath: join(this.getRelativeDirPath(), this.getRelativeFilePath())
|
|
43727
|
+
});
|
|
43728
|
+
const permissions = isRecord$1(settings[COMMANDCODE_PERMISSIONS_KEY]) ? settings[COMMANDCODE_PERMISSIONS_KEY] : {};
|
|
43729
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({ permission: parseCommandcodeRuleLists(permissions) }, null, 2) });
|
|
43730
|
+
}
|
|
43731
|
+
validate() {
|
|
43732
|
+
return {
|
|
43733
|
+
success: true,
|
|
43734
|
+
error: null
|
|
43735
|
+
};
|
|
43736
|
+
}
|
|
43737
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
43738
|
+
return new CommandcodePermissions({
|
|
43739
|
+
outputRoot,
|
|
43740
|
+
relativeDirPath,
|
|
43741
|
+
relativeFilePath,
|
|
43742
|
+
fileContent: "{}",
|
|
43743
|
+
validate: false,
|
|
43744
|
+
global
|
|
43745
|
+
});
|
|
43746
|
+
}
|
|
43747
|
+
};
|
|
43748
|
+
/**
|
|
43749
|
+
* Fail closed on a syntax error or non-object root, matching the write path's
|
|
43750
|
+
* shared-config declaration, so a broken file is surfaced rather than
|
|
43751
|
+
* partially imported or overwritten.
|
|
43752
|
+
*/
|
|
43753
|
+
function parseCommandcodeSettings({ fileContent, relativePath }) {
|
|
43754
|
+
try {
|
|
43755
|
+
return parseSharedConfig({
|
|
43756
|
+
format: "json",
|
|
43757
|
+
fileContent,
|
|
43758
|
+
filePath: relativePath,
|
|
43759
|
+
invalidRootPolicy: "error"
|
|
43760
|
+
});
|
|
43761
|
+
} catch (error) {
|
|
43762
|
+
throw new Error(`Failed to parse Command Code settings in ${relativePath}: ${formatError(error)}`, { cause: error });
|
|
43763
|
+
}
|
|
43764
|
+
}
|
|
43765
|
+
//#endregion
|
|
42586
43766
|
//#region src/features/permissions/continue-permissions.ts
|
|
42587
43767
|
const CONTINUE_GLOBAL_ONLY_MESSAGE = "Continue permissions are global-only; use --global to sync ~/.continue/permissions.yaml";
|
|
42588
43768
|
const CATCH_ALL_PATTERN$5 = "*";
|
|
@@ -53344,6 +54524,14 @@ const toolPermissionsFactories = /* @__PURE__ */ new Map([
|
|
|
53344
54524
|
supportsImport: true
|
|
53345
54525
|
}
|
|
53346
54526
|
}],
|
|
54527
|
+
["commandcode", {
|
|
54528
|
+
class: CommandcodePermissions,
|
|
54529
|
+
meta: {
|
|
54530
|
+
supportsProject: true,
|
|
54531
|
+
supportsGlobal: true,
|
|
54532
|
+
supportsImport: true
|
|
54533
|
+
}
|
|
54534
|
+
}],
|
|
53347
54535
|
["continue", {
|
|
53348
54536
|
class: ContinuePermissions,
|
|
53349
54537
|
meta: {
|
|
@@ -56110,6 +57298,154 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
|
|
|
56110
57298
|
}
|
|
56111
57299
|
};
|
|
56112
57300
|
//#endregion
|
|
57301
|
+
//#region src/features/skills/commandcode-skill.ts
|
|
57302
|
+
const CommandcodeSkillFrontmatterSchema = z.looseObject({
|
|
57303
|
+
name: z.string(),
|
|
57304
|
+
description: z.string()
|
|
57305
|
+
});
|
|
57306
|
+
/**
|
|
57307
|
+
* Represents a Command Code skill directory.
|
|
57308
|
+
*
|
|
57309
|
+
* Command Code discovers Anthropic-style `<name>/SKILL.md` directories under
|
|
57310
|
+
* `<project>/.commandcode/skills/` (project scope) and
|
|
57311
|
+
* `~/.commandcode/skills/` (user scope). The frontmatter requires `name`
|
|
57312
|
+
* (matching the directory name) and `description`; supporting files next to
|
|
57313
|
+
* `SKILL.md` are carried along.
|
|
57314
|
+
*
|
|
57315
|
+
* @see https://commandcode.ai/docs/skills
|
|
57316
|
+
*/
|
|
57317
|
+
var CommandcodeSkill = class CommandcodeSkill extends ToolSkill {
|
|
57318
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = COMMANDCODE_SKILLS_DIR_PATH, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
57319
|
+
super({
|
|
57320
|
+
outputRoot,
|
|
57321
|
+
relativeDirPath,
|
|
57322
|
+
dirName,
|
|
57323
|
+
mainFile: {
|
|
57324
|
+
name: SKILL_FILE_NAME,
|
|
57325
|
+
body,
|
|
57326
|
+
frontmatter: { ...frontmatter }
|
|
57327
|
+
},
|
|
57328
|
+
otherFiles,
|
|
57329
|
+
global
|
|
57330
|
+
});
|
|
57331
|
+
if (validate) {
|
|
57332
|
+
const result = this.validate();
|
|
57333
|
+
if (!result.success) throw result.error;
|
|
57334
|
+
}
|
|
57335
|
+
}
|
|
57336
|
+
static getSettablePaths(_options = {}) {
|
|
57337
|
+
return { relativeDirPath: COMMANDCODE_SKILLS_DIR_PATH };
|
|
57338
|
+
}
|
|
57339
|
+
getFrontmatter() {
|
|
57340
|
+
return CommandcodeSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
57341
|
+
}
|
|
57342
|
+
getBody() {
|
|
57343
|
+
return this.mainFile?.body ?? "";
|
|
57344
|
+
}
|
|
57345
|
+
validate() {
|
|
57346
|
+
if (!this.mainFile) return {
|
|
57347
|
+
success: false,
|
|
57348
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
57349
|
+
};
|
|
57350
|
+
const result = CommandcodeSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
57351
|
+
if (!result.success) return {
|
|
57352
|
+
success: false,
|
|
57353
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
57354
|
+
};
|
|
57355
|
+
if (result.data.name !== this.getDirName()) return {
|
|
57356
|
+
success: false,
|
|
57357
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: frontmatter name (${result.data.name}) must match directory name (${this.getDirName()})`)
|
|
57358
|
+
};
|
|
57359
|
+
return {
|
|
57360
|
+
success: true,
|
|
57361
|
+
error: null
|
|
57362
|
+
};
|
|
57363
|
+
}
|
|
57364
|
+
toRulesyncSkill() {
|
|
57365
|
+
const { name, description, ...commandcodeSection } = this.getFrontmatter();
|
|
57366
|
+
const rulesyncFrontmatter = {
|
|
57367
|
+
name,
|
|
57368
|
+
description,
|
|
57369
|
+
targets: ["*"],
|
|
57370
|
+
...Object.keys(commandcodeSection).length > 0 && { commandcode: commandcodeSection }
|
|
57371
|
+
};
|
|
57372
|
+
return new RulesyncSkill({
|
|
57373
|
+
outputRoot: this.outputRoot,
|
|
57374
|
+
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
57375
|
+
dirName: this.getDirName(),
|
|
57376
|
+
frontmatter: rulesyncFrontmatter,
|
|
57377
|
+
body: this.getBody(),
|
|
57378
|
+
otherFiles: this.getOtherFiles(),
|
|
57379
|
+
validate: true,
|
|
57380
|
+
global: this.global
|
|
57381
|
+
});
|
|
57382
|
+
}
|
|
57383
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
57384
|
+
const settablePaths = CommandcodeSkill.getSettablePaths({ global });
|
|
57385
|
+
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
57386
|
+
const { name: _sectionName, description: _sectionDescription, ...commandcodeSection } = rulesyncFrontmatter.commandcode ?? {};
|
|
57387
|
+
const commandcodeFrontmatter = {
|
|
57388
|
+
...commandcodeSection,
|
|
57389
|
+
name: rulesyncFrontmatter.name,
|
|
57390
|
+
description: rulesyncFrontmatter.description
|
|
57391
|
+
};
|
|
57392
|
+
return new CommandcodeSkill({
|
|
57393
|
+
outputRoot,
|
|
57394
|
+
relativeDirPath: settablePaths.relativeDirPath,
|
|
57395
|
+
dirName: commandcodeFrontmatter.name,
|
|
57396
|
+
frontmatter: commandcodeFrontmatter,
|
|
57397
|
+
body: rulesyncSkill.getBody(),
|
|
57398
|
+
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
57399
|
+
validate,
|
|
57400
|
+
global
|
|
57401
|
+
});
|
|
57402
|
+
}
|
|
57403
|
+
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
57404
|
+
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
57405
|
+
return targets.includes("*") || targets.includes("commandcode");
|
|
57406
|
+
}
|
|
57407
|
+
static async fromDir(params) {
|
|
57408
|
+
const loaded = await this.loadSkillDirContent({
|
|
57409
|
+
...params,
|
|
57410
|
+
getSettablePaths: CommandcodeSkill.getSettablePaths
|
|
57411
|
+
});
|
|
57412
|
+
const result = CommandcodeSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
57413
|
+
if (!result.success) {
|
|
57414
|
+
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
57415
|
+
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
57416
|
+
}
|
|
57417
|
+
if (result.data.name !== loaded.dirName) {
|
|
57418
|
+
const skillFilePath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
|
|
57419
|
+
throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
|
|
57420
|
+
}
|
|
57421
|
+
return new CommandcodeSkill({
|
|
57422
|
+
outputRoot: loaded.outputRoot,
|
|
57423
|
+
relativeDirPath: loaded.relativeDirPath,
|
|
57424
|
+
dirName: loaded.dirName,
|
|
57425
|
+
frontmatter: result.data,
|
|
57426
|
+
body: loaded.body,
|
|
57427
|
+
otherFiles: loaded.otherFiles,
|
|
57428
|
+
validate: true,
|
|
57429
|
+
global: loaded.global
|
|
57430
|
+
});
|
|
57431
|
+
}
|
|
57432
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
57433
|
+
return new CommandcodeSkill({
|
|
57434
|
+
outputRoot,
|
|
57435
|
+
relativeDirPath,
|
|
57436
|
+
dirName,
|
|
57437
|
+
frontmatter: {
|
|
57438
|
+
name: "",
|
|
57439
|
+
description: ""
|
|
57440
|
+
},
|
|
57441
|
+
body: "",
|
|
57442
|
+
otherFiles: [],
|
|
57443
|
+
validate: false,
|
|
57444
|
+
global
|
|
57445
|
+
});
|
|
57446
|
+
}
|
|
57447
|
+
};
|
|
57448
|
+
//#endregion
|
|
56113
57449
|
//#region src/features/skills/continue-skill.ts
|
|
56114
57450
|
const ContinueSkillFrontmatterSchema = z.looseObject({
|
|
56115
57451
|
name: z.string(),
|
|
@@ -61299,6 +62635,14 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
61299
62635
|
supportsGlobal: true
|
|
61300
62636
|
}
|
|
61301
62637
|
}],
|
|
62638
|
+
["commandcode", {
|
|
62639
|
+
class: CommandcodeSkill,
|
|
62640
|
+
meta: {
|
|
62641
|
+
supportsProject: true,
|
|
62642
|
+
supportsSimulated: false,
|
|
62643
|
+
supportsGlobal: true
|
|
62644
|
+
}
|
|
62645
|
+
}],
|
|
61302
62646
|
["continue", {
|
|
61303
62647
|
class: ContinueSkill,
|
|
61304
62648
|
meta: {
|
|
@@ -63402,6 +64746,143 @@ var CodexCliSubagent = class CodexCliSubagent extends ToolSubagent {
|
|
|
63402
64746
|
}
|
|
63403
64747
|
};
|
|
63404
64748
|
//#endregion
|
|
64749
|
+
//#region src/features/subagents/commandcode-subagent.ts
|
|
64750
|
+
const CommandcodeSubagentFrontmatterSchema = z.looseObject({
|
|
64751
|
+
name: z.string(),
|
|
64752
|
+
description: z.optional(z.string()),
|
|
64753
|
+
tools: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
64754
|
+
disallowedTools: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
64755
|
+
model: z.optional(z.string()),
|
|
64756
|
+
reasoningEffort: z.optional(z.string()),
|
|
64757
|
+
maxTurns: z.optional(z.number()),
|
|
64758
|
+
permissionMode: z.optional(z.string()),
|
|
64759
|
+
background: z.optional(z.boolean()),
|
|
64760
|
+
showOutput: z.optional(z.boolean())
|
|
64761
|
+
});
|
|
64762
|
+
/**
|
|
64763
|
+
* Command Code custom agent: a Markdown file with YAML frontmatter under
|
|
64764
|
+
* `.commandcode/agents/` (project) or `~/.commandcode/agents/` (user); the
|
|
64765
|
+
* body is the agent's system prompt.
|
|
64766
|
+
*
|
|
64767
|
+
* @see https://commandcode.ai/docs/custom-agents
|
|
64768
|
+
*/
|
|
64769
|
+
var CommandcodeSubagent = class CommandcodeSubagent extends ToolSubagent {
|
|
64770
|
+
frontmatter;
|
|
64771
|
+
body;
|
|
64772
|
+
constructor({ frontmatter, body, fileContent, ...rest }) {
|
|
64773
|
+
if (rest.validate !== false) {
|
|
64774
|
+
const result = CommandcodeSubagentFrontmatterSchema.safeParse(frontmatter);
|
|
64775
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
|
|
64776
|
+
}
|
|
64777
|
+
super({
|
|
64778
|
+
...rest,
|
|
64779
|
+
fileContent: fileContent ?? stringifyFrontmatter(body, frontmatter)
|
|
64780
|
+
});
|
|
64781
|
+
this.frontmatter = frontmatter;
|
|
64782
|
+
this.body = body;
|
|
64783
|
+
}
|
|
64784
|
+
static getSettablePaths(_options = {}) {
|
|
64785
|
+
return { relativeDirPath: COMMANDCODE_AGENTS_DIR_PATH };
|
|
64786
|
+
}
|
|
64787
|
+
getFrontmatter() {
|
|
64788
|
+
return this.frontmatter;
|
|
64789
|
+
}
|
|
64790
|
+
getBody() {
|
|
64791
|
+
return this.body;
|
|
64792
|
+
}
|
|
64793
|
+
toRulesyncSubagent() {
|
|
64794
|
+
const { name, description, ...rest } = this.frontmatter;
|
|
64795
|
+
return new RulesyncSubagent({
|
|
64796
|
+
outputRoot: ".",
|
|
64797
|
+
frontmatter: {
|
|
64798
|
+
targets: ["*"],
|
|
64799
|
+
name,
|
|
64800
|
+
description,
|
|
64801
|
+
commandcode: { ...rest }
|
|
64802
|
+
},
|
|
64803
|
+
body: this.body,
|
|
64804
|
+
relativeDirPath: RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH,
|
|
64805
|
+
relativeFilePath: this.getRelativeFilePath(),
|
|
64806
|
+
validate: true
|
|
64807
|
+
});
|
|
64808
|
+
}
|
|
64809
|
+
static fromRulesyncSubagent({ outputRoot = process.cwd(), rulesyncSubagent, validate = true, global = false }) {
|
|
64810
|
+
const rulesyncFrontmatter = rulesyncSubagent.getFrontmatter();
|
|
64811
|
+
const commandcodeSection = rulesyncFrontmatter.commandcode ?? {};
|
|
64812
|
+
const commandcodeSubagentFrontmatter = {
|
|
64813
|
+
name: rulesyncFrontmatter.name,
|
|
64814
|
+
description: rulesyncFrontmatter.description,
|
|
64815
|
+
...commandcodeSection
|
|
64816
|
+
};
|
|
64817
|
+
const body = rulesyncSubagent.getBody();
|
|
64818
|
+
const fileContent = stringifyFrontmatter(body, commandcodeSubagentFrontmatter, { avoidBlockScalars: true });
|
|
64819
|
+
const paths = this.getSettablePaths({ global });
|
|
64820
|
+
return new CommandcodeSubagent({
|
|
64821
|
+
outputRoot,
|
|
64822
|
+
frontmatter: commandcodeSubagentFrontmatter,
|
|
64823
|
+
body,
|
|
64824
|
+
relativeDirPath: paths.relativeDirPath,
|
|
64825
|
+
relativeFilePath: rulesyncSubagent.getRelativeFilePath(),
|
|
64826
|
+
fileContent,
|
|
64827
|
+
validate,
|
|
64828
|
+
global
|
|
64829
|
+
});
|
|
64830
|
+
}
|
|
64831
|
+
validate() {
|
|
64832
|
+
if (!this.frontmatter) return {
|
|
64833
|
+
success: true,
|
|
64834
|
+
error: null
|
|
64835
|
+
};
|
|
64836
|
+
const result = CommandcodeSubagentFrontmatterSchema.safeParse(this.frontmatter);
|
|
64837
|
+
if (result.success) return {
|
|
64838
|
+
success: true,
|
|
64839
|
+
error: null
|
|
64840
|
+
};
|
|
64841
|
+
else return {
|
|
64842
|
+
success: false,
|
|
64843
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${join(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
|
|
64844
|
+
};
|
|
64845
|
+
}
|
|
64846
|
+
static isTargetedByRulesyncSubagent(rulesyncSubagent) {
|
|
64847
|
+
return this.isTargetedByRulesyncSubagentDefault({
|
|
64848
|
+
rulesyncSubagent,
|
|
64849
|
+
toolTarget: "commandcode"
|
|
64850
|
+
});
|
|
64851
|
+
}
|
|
64852
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
64853
|
+
const paths = this.getSettablePaths({ global });
|
|
64854
|
+
const filePath = join(outputRoot, paths.relativeDirPath, relativeFilePath);
|
|
64855
|
+
const fileContent = await readFileContent(filePath);
|
|
64856
|
+
const { frontmatter, body: content } = parseFrontmatter(fileContent, filePath);
|
|
64857
|
+
const result = CommandcodeSubagentFrontmatterSchema.safeParse(frontmatter);
|
|
64858
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
|
|
64859
|
+
return new CommandcodeSubagent({
|
|
64860
|
+
outputRoot,
|
|
64861
|
+
relativeDirPath: paths.relativeDirPath,
|
|
64862
|
+
relativeFilePath,
|
|
64863
|
+
frontmatter: result.data,
|
|
64864
|
+
body: content.trim(),
|
|
64865
|
+
fileContent,
|
|
64866
|
+
validate,
|
|
64867
|
+
global
|
|
64868
|
+
});
|
|
64869
|
+
}
|
|
64870
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
64871
|
+
return new CommandcodeSubagent({
|
|
64872
|
+
outputRoot,
|
|
64873
|
+
relativeDirPath,
|
|
64874
|
+
relativeFilePath,
|
|
64875
|
+
frontmatter: {
|
|
64876
|
+
name: "",
|
|
64877
|
+
description: ""
|
|
64878
|
+
},
|
|
64879
|
+
body: "",
|
|
64880
|
+
fileContent: "",
|
|
64881
|
+
validate: false
|
|
64882
|
+
});
|
|
64883
|
+
}
|
|
64884
|
+
};
|
|
64885
|
+
//#endregion
|
|
63405
64886
|
//#region src/features/subagents/copilot-subagent.ts
|
|
63406
64887
|
const CopilotSubagentFrontmatterSchema = z.looseObject({
|
|
63407
64888
|
name: z.string(),
|
|
@@ -67060,6 +68541,15 @@ const toolSubagentFactories = /* @__PURE__ */ new Map([
|
|
|
67060
68541
|
filePattern: "*.toml"
|
|
67061
68542
|
}
|
|
67062
68543
|
}],
|
|
68544
|
+
["commandcode", {
|
|
68545
|
+
class: CommandcodeSubagent,
|
|
68546
|
+
meta: {
|
|
68547
|
+
supportsProject: true,
|
|
68548
|
+
supportsSimulated: false,
|
|
68549
|
+
supportsGlobal: true,
|
|
68550
|
+
filePattern: "*.md"
|
|
68551
|
+
}
|
|
68552
|
+
}],
|
|
67063
68553
|
["copilot", {
|
|
67064
68554
|
class: CopilotSubagent,
|
|
67065
68555
|
meta: {
|
|
@@ -69892,6 +71382,73 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
69892
71382
|
}
|
|
69893
71383
|
};
|
|
69894
71384
|
//#endregion
|
|
71385
|
+
//#region src/features/rules/commandcode-rule.ts
|
|
71386
|
+
var CommandcodeRule = class CommandcodeRule extends ToolRule {
|
|
71387
|
+
constructor({ fileContent, root, ...rest }) {
|
|
71388
|
+
super({
|
|
71389
|
+
...rest,
|
|
71390
|
+
fileContent,
|
|
71391
|
+
root: root ?? false
|
|
71392
|
+
});
|
|
71393
|
+
}
|
|
71394
|
+
static getSettablePaths({ global = false } = {}) {
|
|
71395
|
+
return { root: {
|
|
71396
|
+
relativeDirPath: global ? COMMANDCODE_DIR : ".",
|
|
71397
|
+
relativeFilePath: COMMANDCODE_RULE_FILE_NAME
|
|
71398
|
+
} };
|
|
71399
|
+
}
|
|
71400
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath: _relativeFilePath, validate = true, global = false }) {
|
|
71401
|
+
const { root } = this.getSettablePaths({ global });
|
|
71402
|
+
const relativePath = join(root.relativeDirPath, root.relativeFilePath);
|
|
71403
|
+
const fileContent = await readFileContent(join(outputRoot, relativePath));
|
|
71404
|
+
return new CommandcodeRule({
|
|
71405
|
+
outputRoot,
|
|
71406
|
+
relativeDirPath: root.relativeDirPath,
|
|
71407
|
+
relativeFilePath: root.relativeFilePath,
|
|
71408
|
+
fileContent,
|
|
71409
|
+
validate,
|
|
71410
|
+
root: true
|
|
71411
|
+
});
|
|
71412
|
+
}
|
|
71413
|
+
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
71414
|
+
const { root } = this.getSettablePaths({ global });
|
|
71415
|
+
const isRoot = rulesyncRule.getFrontmatter().root ?? false;
|
|
71416
|
+
return new CommandcodeRule({
|
|
71417
|
+
outputRoot,
|
|
71418
|
+
relativeDirPath: root.relativeDirPath,
|
|
71419
|
+
relativeFilePath: root.relativeFilePath,
|
|
71420
|
+
fileContent: rulesyncRule.getBody(),
|
|
71421
|
+
validate,
|
|
71422
|
+
root: isRoot
|
|
71423
|
+
});
|
|
71424
|
+
}
|
|
71425
|
+
toRulesyncRule() {
|
|
71426
|
+
return this.toRulesyncRuleDefault();
|
|
71427
|
+
}
|
|
71428
|
+
validate() {
|
|
71429
|
+
return {
|
|
71430
|
+
success: true,
|
|
71431
|
+
error: null
|
|
71432
|
+
};
|
|
71433
|
+
}
|
|
71434
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
71435
|
+
return new CommandcodeRule({
|
|
71436
|
+
outputRoot,
|
|
71437
|
+
relativeDirPath,
|
|
71438
|
+
relativeFilePath,
|
|
71439
|
+
fileContent: "",
|
|
71440
|
+
validate: false,
|
|
71441
|
+
root: relativeFilePath === "AGENTS.md" && (relativeDirPath === "." || relativeDirPath === ".commandcode")
|
|
71442
|
+
});
|
|
71443
|
+
}
|
|
71444
|
+
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
71445
|
+
return this.isTargetedByRulesyncRuleDefault({
|
|
71446
|
+
rulesyncRule,
|
|
71447
|
+
toolTarget: "commandcode"
|
|
71448
|
+
});
|
|
71449
|
+
}
|
|
71450
|
+
};
|
|
71451
|
+
//#endregion
|
|
69895
71452
|
//#region src/features/rules/continue-rule.ts
|
|
69896
71453
|
/**
|
|
69897
71454
|
* Frontmatter schema for Continue rule files (`.continue/rules/*.md`).
|
|
@@ -74212,6 +75769,15 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
74212
75769
|
collisionPolicy: "fold"
|
|
74213
75770
|
}
|
|
74214
75771
|
}],
|
|
75772
|
+
["commandcode", {
|
|
75773
|
+
class: CommandcodeRule,
|
|
75774
|
+
meta: {
|
|
75775
|
+
extension: "md",
|
|
75776
|
+
supportsGlobal: true,
|
|
75777
|
+
ruleDiscoveryMode: "auto",
|
|
75778
|
+
collisionPolicy: "fold"
|
|
75779
|
+
}
|
|
75780
|
+
}],
|
|
74215
75781
|
["continue", {
|
|
74216
75782
|
class: ContinueRule,
|
|
74217
75783
|
meta: {
|
|
@@ -77708,4 +79274,4 @@ async function importChecksCore(params) {
|
|
|
77708
79274
|
//#endregion
|
|
77709
79275
|
export { RulesyncCheck as $, writeFileContent as $t, CLAUDECODE_SKILLS_DIR_PATH as A, RULESYNC_SKILLS_RELATIVE_DIR_PATH as An, directoryExists as At, RulesyncSkill as B, truncateText as Bn, listSubdirectoryNames as Bt, ChecksProcessor as C, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Cn, ErrorCodes as Ct, CLAUDECODE_LOCAL_RULE_FILE_NAME as D, RULESYNC_PERMISSIONS_SCHEMA_URL as Dn, assertWritablePathInsideRoot as Dt, CLAUDECODE_DIR as E, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as En, assertTreeContainsNoSymlinks as Et, AUGMENTCODE_DIR as F, parseCommaSeparatedList as Fn, isFileNotFoundError as Ft, RulesyncMcp as G, stripControlCharactersKeepingLineFeeds as Gn, removeDirectoryStrict as Gt, RulesyncRule as H, hasEnclosingMarkOutsideKeycap as Hn, readFileContent as Ht, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as I, ALL_FEATURES as In, isFileSystemError as It, getRulesyncSourceCandidates as J, removeTempDirectory as Jt, RulesyncIgnore as K, stripHiddenCharacters as Kn, removeFile as Kt, getLocalSkillDirNames as L, ALL_FEATURES_WITH_WILDCARD as Ln, isSymlink as Lt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as M, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Mn, fileExists as Mt, caseFoldIdentity as N, RULESYNC_USER_CONFIG_DIR_NAME as Nn, getFileSize as Nt, CLAUDECODE_MEMORIES_DIR_NAME as O, RULESYNC_RELATIVE_DIR_PATH as On, checkPathTraversal as Ot, groupSpellingsByCaseFoldedIdentity as P, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as Pn, getHomeDirectory as Pt, RulesyncCommandFrontmatterSchema as Q, writeFileBuffer as Qt, RulesyncSubagent as R, DEPRECATED_FEATURE_REPLACEMENTS as Rn, listDirectoryEntryNames as Rt, QWENCODE_LOCAL_RULE_FILE_NAME as S, RULESYNC_MCP_SCHEMA_URL as Sn, CLIError as St, CODEXCLI_DIR as T, RULESYNC_PERMISSIONS_LEGACY_FILE_NAME as Tn, assertDirectoryIfExists as Tt, RulesyncRuleFrontmatterSchema as U, quoteForLog as Un, readFileContentOrNull as Ut, RulesyncSkillFrontmatterSchema as V, hasDeceptiveHiddenCharacters as Vn, pathEscapesRoot as Vt, RulesyncPermissions as W, stripControlCharacters as Wn, removeDirectory as Wt, parseJsonc as X, runWithDirectoryRollback as Xt, resolveRulesyncSourceWritePath as Y, resolvePath as Yt, RulesyncCommand as Z, toPosixPath as Zt, IgnoreProcessor as _, RULESYNC_IGNORE_RELATIVE_FILE_PATH as _n, fallbackLogger as _t, getProcessorRegistryEntry as a, MAX_FILE_SIZE as an, ConfigResolver as at, CommandsProcessor as b, RULESYNC_MCP_LEGACY_FILE_NAME as bn, resetRunWarningState as bt, RulesProcessor as c, RULESYNC_CHECKS_RELATIVE_DIR_PATH as cn, CONFLICTING_TARGET_PAIRS as ct, CODEBUDDY_DIR as d, RULESYNC_CONFIG_SCHEMA_URL as dn, SourceEntrySchema as dt, ALL_TOOL_TARGETS as en, RulesyncCheckFrontmatterSchema as et, CODEBUDDY_LOCAL_RULE_FILE_NAME as f, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as fn, assertTargetsFeaturesExclusive as ft, McpProcessor as g, RULESYNC_HOOKS_RELATIVE_FILE_PATH as gn, WarningCollectingLogger as gt, shortenToWidth as h, RULESYNC_HOOKS_LEGACY_FILE_NAME as hn, JsonLogger as ht, inspectInputRoots as i, CURATED_RULES_FEATURE_SUBDIR as in, SKILL_FILE_NAME as it, FACTORYDROID_DIR as j, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as jn, ensureDir as jt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as k, RULESYNC_RULES_RELATIVE_DIR_PATH as kn, createTempDirectory as kt, SubagentsProcessor as l, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as ln, ConfigFileSchema as lt, displayWidthOf as m, RULESYNC_HOOKS_FILE_NAME as mn, ConsoleLogger as mt, formatSourceLoadFailure as n, PACKAGING_TOOL_TARGETS as nn, loadYaml as nt, convertFromTool as o, RULESYNC_AIIGNORE_FILE_NAME as on, mergeInputRootConfigs as ot, ELLIPSIS_WIDTH as p, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as pn, findControlCharacter as pt, RulesyncHooks as q, removeFileStrict as qt, generate as r, ToolTargetSchema as rn, SHARED_USER_MANAGED_CONFIG_PATHS as rt, isPackagingToolTarget as s, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as sn, resolveEffectiveInputRoots as st, importFromTool as t, ALL_TOOL_TARGETS_WITH_WILDCARD as tn, stringifyFrontmatter as tt, SkillsProcessor as u, RULESYNC_CONFIG_RELATIVE_FILE_PATH as un, GITIGNORE_DESTINATION_KEY as ut, HooksProcessor as v, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as vn, warnOnConflictingFlags as vt, CODEXCLI_BASH_RULES_FILE_NAME as w, RULESYNC_PERMISSIONS_FILE_NAME as wn, applyFileMode as wt, QWENCODE_DIR as x, RULESYNC_MCP_RELATIVE_FILE_PATH as xn, withWarnOnceScope as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_MCP_FILE_NAME as yn, withFallbackLoggerTarget as yt, RulesyncSubagentFrontmatterSchema as z, formatError as zn, listFilePathsRecursively as zt };
|
|
77710
79276
|
|
|
77711
|
-
//# sourceMappingURL=import-
|
|
79277
|
+
//# sourceMappingURL=import-De13gNpF.js.map
|