rulesync 16.31.0 → 16.32.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-Cwe8RKJs.cjs → import-0wSuIchf.cjs} +1417 -23
- package/dist/{import-D2GhBXJV.js → import-D5n_V39C.js} +1401 -7
- package/dist/import-D5n_V39C.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-D2GhBXJV.js.map +0 -1
|
@@ -478,6 +478,7 @@ const rulesProcessorToolTargetTuple = [
|
|
|
478
478
|
"cline",
|
|
479
479
|
"codebuddy",
|
|
480
480
|
"codexcli",
|
|
481
|
+
"continue",
|
|
481
482
|
"copilot",
|
|
482
483
|
"copilotcli",
|
|
483
484
|
"cortexcode",
|
|
@@ -521,6 +522,7 @@ const ignoreProcessorToolTargetTuple = [
|
|
|
521
522
|
"claudecode",
|
|
522
523
|
"claudecode-legacy",
|
|
523
524
|
"cline",
|
|
525
|
+
"continue",
|
|
524
526
|
"crush",
|
|
525
527
|
"cursor",
|
|
526
528
|
"hermesagent",
|
|
@@ -552,6 +554,7 @@ const mcpProcessorToolTargetTuple = [
|
|
|
552
554
|
"claudecode-legacy",
|
|
553
555
|
"cline",
|
|
554
556
|
"codexcli",
|
|
557
|
+
"continue",
|
|
555
558
|
"copilot",
|
|
556
559
|
"copilotcli",
|
|
557
560
|
"cortexcode",
|
|
@@ -593,6 +596,7 @@ const commandsProcessorToolTargetTuple = [
|
|
|
593
596
|
"claudecode-legacy",
|
|
594
597
|
"cline",
|
|
595
598
|
"codexcli",
|
|
599
|
+
"continue",
|
|
596
600
|
"copilot",
|
|
597
601
|
"cursor",
|
|
598
602
|
"factorydroid",
|
|
@@ -670,6 +674,7 @@ const skillsProcessorToolTargetTuple = [
|
|
|
670
674
|
"claudecode-legacy",
|
|
671
675
|
"cline",
|
|
672
676
|
"codexcli",
|
|
677
|
+
"continue",
|
|
673
678
|
"copilot",
|
|
674
679
|
"copilotcli",
|
|
675
680
|
"cortexcode",
|
|
@@ -715,6 +720,7 @@ const hooksProcessorToolTargetTuple = [
|
|
|
715
720
|
"claudecode",
|
|
716
721
|
"claudecode-plugin",
|
|
717
722
|
"codexcli",
|
|
723
|
+
"continue",
|
|
718
724
|
"copilot",
|
|
719
725
|
"copilotcli",
|
|
720
726
|
"cortexcode",
|
|
@@ -747,6 +753,7 @@ const permissionsProcessorToolTargetTuple = [
|
|
|
747
753
|
"claudecode",
|
|
748
754
|
"cline",
|
|
749
755
|
"codexcli",
|
|
756
|
+
"continue",
|
|
750
757
|
"copilot",
|
|
751
758
|
"copilotcli",
|
|
752
759
|
"cursor",
|
|
@@ -3259,6 +3266,7 @@ const SHARED_USER_MANAGED_CONFIG_PATHS = [
|
|
|
3259
3266
|
".claude/settings.json",
|
|
3260
3267
|
".claude/settings.local.json",
|
|
3261
3268
|
".codex/config.toml",
|
|
3269
|
+
".continue/settings.json",
|
|
3262
3270
|
".copilot/settings.json",
|
|
3263
3271
|
".github/copilot/settings.json",
|
|
3264
3272
|
".cortex/settings.json",
|
|
@@ -4883,6 +4891,59 @@ const CANONICAL_TO_CORTEXCODE_EVENT_NAMES = {
|
|
|
4883
4891
|
};
|
|
4884
4892
|
const CORTEXCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_CORTEXCODE_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
4885
4893
|
/**
|
|
4894
|
+
* Hook events supported by the Continue CLI (`cn`).
|
|
4895
|
+
*
|
|
4896
|
+
* Continue reads a Claude-Code-compatible `hooks` key from
|
|
4897
|
+
* `~/.continue/settings.json` (user) and `.continue/settings.json` (project):
|
|
4898
|
+
* PascalCase event names, a regex `matcher` over the event's subject (tool
|
|
4899
|
+
* name, session source, ...), and `command` / `http` / `prompt` / `agent`
|
|
4900
|
+
* handlers with a `timeout` in seconds. `$CONTINUE_PROJECT_DIR` resolves to
|
|
4901
|
+
* the working directory inside commands. The event list is the CLI's
|
|
4902
|
+
* `HOOK_EVENT_NAMES`.
|
|
4903
|
+
*
|
|
4904
|
+
* @see https://github.com/continuedev/continue/blob/main/extensions/cli/src/hooks/types.ts
|
|
4905
|
+
* @see https://github.com/continuedev/continue/blob/main/extensions/cli/src/hooks/hookConfig.ts
|
|
4906
|
+
*/
|
|
4907
|
+
const CONTINUE_HOOK_EVENTS = [
|
|
4908
|
+
"preToolUse",
|
|
4909
|
+
"postToolUse",
|
|
4910
|
+
"postToolUseFailure",
|
|
4911
|
+
"permissionRequest",
|
|
4912
|
+
"beforeSubmitPrompt",
|
|
4913
|
+
"sessionStart",
|
|
4914
|
+
"sessionEnd",
|
|
4915
|
+
"stop",
|
|
4916
|
+
"notification",
|
|
4917
|
+
"subagentStart",
|
|
4918
|
+
"subagentStop",
|
|
4919
|
+
"preCompact",
|
|
4920
|
+
"configChange",
|
|
4921
|
+
"teammateIdle",
|
|
4922
|
+
"taskCompleted",
|
|
4923
|
+
"worktreeCreate",
|
|
4924
|
+
"worktreeRemove"
|
|
4925
|
+
];
|
|
4926
|
+
const CANONICAL_TO_CONTINUE_EVENT_NAMES = {
|
|
4927
|
+
preToolUse: "PreToolUse",
|
|
4928
|
+
postToolUse: "PostToolUse",
|
|
4929
|
+
postToolUseFailure: "PostToolUseFailure",
|
|
4930
|
+
permissionRequest: "PermissionRequest",
|
|
4931
|
+
beforeSubmitPrompt: "UserPromptSubmit",
|
|
4932
|
+
sessionStart: "SessionStart",
|
|
4933
|
+
sessionEnd: "SessionEnd",
|
|
4934
|
+
stop: "Stop",
|
|
4935
|
+
notification: "Notification",
|
|
4936
|
+
subagentStart: "SubagentStart",
|
|
4937
|
+
subagentStop: "SubagentStop",
|
|
4938
|
+
preCompact: "PreCompact",
|
|
4939
|
+
configChange: "ConfigChange",
|
|
4940
|
+
teammateIdle: "TeammateIdle",
|
|
4941
|
+
taskCompleted: "TaskCompleted",
|
|
4942
|
+
worktreeCreate: "WorktreeCreate",
|
|
4943
|
+
worktreeRemove: "WorktreeRemove"
|
|
4944
|
+
};
|
|
4945
|
+
const CONTINUE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_CONTINUE_EVENT_NAMES).map(([k, v]) => [v, k]));
|
|
4946
|
+
/**
|
|
4886
4947
|
* Hook events supported by Hermes Agent's native Shell Hooks system.
|
|
4887
4948
|
*
|
|
4888
4949
|
* Hermes validates hook events against a fixed `VALID_HOOKS` set — 37 entries as
|
|
@@ -5037,6 +5098,7 @@ const HooksConfigSchema = z.looseObject({
|
|
|
5037
5098
|
zcode: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5038
5099
|
bob: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5039
5100
|
cortexcode: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5101
|
+
continue: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5040
5102
|
tabnine: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
|
|
5041
5103
|
qwencode: z.optional(z.looseObject({
|
|
5042
5104
|
hooks: z.optional(hooksRecordSchema),
|
|
@@ -6043,6 +6105,7 @@ const RulesyncMcpFileSchema = z.looseObject({
|
|
|
6043
6105
|
claudecode: z.optional(toolScopedMcpSchema),
|
|
6044
6106
|
cline: z.optional(toolScopedMcpSchema),
|
|
6045
6107
|
codexcli: z.optional(toolScopedMcpSchema),
|
|
6108
|
+
continue: z.optional(toolScopedMcpSchema),
|
|
6046
6109
|
copilot: z.optional(toolScopedMcpSchema),
|
|
6047
6110
|
copilotcli: z.optional(toolScopedMcpSchema),
|
|
6048
6111
|
cursor: z.optional(toolScopedMcpSchema),
|
|
@@ -7676,6 +7739,7 @@ const PermissionsConfigSchema = z.looseObject({
|
|
|
7676
7739
|
zed: z.optional(ZedPermissionsOverrideSchema),
|
|
7677
7740
|
devin: z.optional(DevinPermissionsOverrideSchema),
|
|
7678
7741
|
"antigravity-ide": z.optional(CanonicalPermissionsOverrideSchema),
|
|
7742
|
+
continue: z.optional(CanonicalPermissionsOverrideSchema),
|
|
7679
7743
|
copilot: z.optional(CanonicalPermissionsOverrideSchema),
|
|
7680
7744
|
copilotcli: z.optional(CanonicalPermissionsOverrideSchema),
|
|
7681
7745
|
goose: z.optional(CanonicalPermissionsOverrideSchema),
|
|
@@ -8104,6 +8168,13 @@ const RulesyncRuleFrontmatterSchema = z.object({
|
|
|
8104
8168
|
enabled: z.optional(z.boolean()),
|
|
8105
8169
|
description: z.optional(z.string())
|
|
8106
8170
|
})),
|
|
8171
|
+
continue: z.optional(z.looseObject({
|
|
8172
|
+
alwaysApply: z.optional(z.boolean()),
|
|
8173
|
+
description: z.optional(z.string()),
|
|
8174
|
+
globs: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
8175
|
+
name: z.optional(z.string()),
|
|
8176
|
+
regex: z.optional(z.union([z.string(), z.array(z.string())]))
|
|
8177
|
+
})),
|
|
8107
8178
|
cursor: z.optional(z.looseObject({
|
|
8108
8179
|
alwaysApply: z.optional(z.boolean()),
|
|
8109
8180
|
description: z.optional(z.string()),
|
|
@@ -9392,6 +9463,7 @@ const RulesyncSkillFrontmatterSchema = z.looseObject({
|
|
|
9392
9463
|
"allowed-tools": z.optional(z.union([z.string(), z.array(z.string())]))
|
|
9393
9464
|
})),
|
|
9394
9465
|
cortexcode: z.optional(z.looseObject({})),
|
|
9466
|
+
continue: z.optional(z.looseObject({})),
|
|
9395
9467
|
tabnine: z.optional(z.looseObject({})),
|
|
9396
9468
|
takt: z.optional(z.looseObject({
|
|
9397
9469
|
name: z.optional(z.string()),
|
|
@@ -12971,6 +13043,14 @@ const SHARED_CONFIG_OWNERSHIP = {
|
|
|
12971
13043
|
ownedKeys: ["hooks"]
|
|
12972
13044
|
} }
|
|
12973
13045
|
},
|
|
13046
|
+
".continue/settings.json": {
|
|
13047
|
+
format: "json",
|
|
13048
|
+
invalidRootPolicy: "error",
|
|
13049
|
+
features: { hooks: {
|
|
13050
|
+
kind: "replace-owned-keys",
|
|
13051
|
+
ownedKeys: ["hooks"]
|
|
13052
|
+
} }
|
|
13053
|
+
},
|
|
12974
13054
|
".cortex/settings.json": {
|
|
12975
13055
|
format: "json",
|
|
12976
13056
|
invalidRootPolicy: "error",
|
|
@@ -15172,6 +15252,152 @@ var CodexcliCommand = class CodexcliCommand extends ToolCommand {
|
|
|
15172
15252
|
}
|
|
15173
15253
|
};
|
|
15174
15254
|
//#endregion
|
|
15255
|
+
//#region src/constants/continue-paths.ts
|
|
15256
|
+
const CONTINUE_DIR = ".continue";
|
|
15257
|
+
const CONTINUE_ROOT_RULE_FILE_NAME = "AGENTS.md";
|
|
15258
|
+
const CONTINUE_PROMPTS_DIR_PATH = join(CONTINUE_DIR, "prompts");
|
|
15259
|
+
const CONTINUE_MCP_DIR_PATH = join(CONTINUE_DIR, "mcpServers");
|
|
15260
|
+
const CONTINUE_MCP_FILE_NAME = "mcp.json";
|
|
15261
|
+
const CONTINUE_SKILLS_DIR_PATH = join(CONTINUE_DIR, "skills");
|
|
15262
|
+
const CONTINUE_SETTINGS_FILE_NAME = "settings.json";
|
|
15263
|
+
const CONTINUE_PERMISSIONS_FILE_NAME = "permissions.yaml";
|
|
15264
|
+
const CONTINUE_IGNORE_FILE_NAME = ".continueignore";
|
|
15265
|
+
//#endregion
|
|
15266
|
+
//#region src/features/commands/continue-command.ts
|
|
15267
|
+
/**
|
|
15268
|
+
* Continue prompt files are Markdown files under `<project>/.continue/prompts/`
|
|
15269
|
+
* (project scope) and `~/.continue/prompts/` (user scope). A prompt with
|
|
15270
|
+
* `invokable: true` becomes a `/name` slash command in the chat input and the
|
|
15271
|
+
* CLI; without it the file is treated as a plain rule, so rulesync always sets
|
|
15272
|
+
* the flag. `name` is the command name (the file stem by default) and
|
|
15273
|
+
* `description` the hint shown next to it.
|
|
15274
|
+
*
|
|
15275
|
+
* @see https://docs.continue.dev/customize/deep-dives/prompts
|
|
15276
|
+
*/
|
|
15277
|
+
const ContinueCommandFrontmatterSchema = z.looseObject({
|
|
15278
|
+
name: z.optional(z.string()),
|
|
15279
|
+
description: z.optional(z.string()),
|
|
15280
|
+
invokable: z.optional(z.boolean())
|
|
15281
|
+
});
|
|
15282
|
+
/** The command name Continue derives from a prompt file: its stem. */
|
|
15283
|
+
function commandNameFromFilePath(relativeFilePath) {
|
|
15284
|
+
return basename(relativeFilePath).replace(/\.md$/, "");
|
|
15285
|
+
}
|
|
15286
|
+
var ContinueCommand = class ContinueCommand extends ToolCommand {
|
|
15287
|
+
frontmatter;
|
|
15288
|
+
body;
|
|
15289
|
+
constructor({ frontmatter, body, ...rest }) {
|
|
15290
|
+
if (rest.validate) {
|
|
15291
|
+
const result = ContinueCommandFrontmatterSchema.safeParse(frontmatter);
|
|
15292
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
|
|
15293
|
+
}
|
|
15294
|
+
super({
|
|
15295
|
+
...rest,
|
|
15296
|
+
fileContent: stringifyFrontmatter(body, frontmatter)
|
|
15297
|
+
});
|
|
15298
|
+
this.frontmatter = frontmatter;
|
|
15299
|
+
this.body = body;
|
|
15300
|
+
}
|
|
15301
|
+
static getSettablePaths(_options = {}) {
|
|
15302
|
+
return { relativeDirPath: CONTINUE_PROMPTS_DIR_PATH };
|
|
15303
|
+
}
|
|
15304
|
+
getBody() {
|
|
15305
|
+
return this.body;
|
|
15306
|
+
}
|
|
15307
|
+
getFrontmatter() {
|
|
15308
|
+
return this.frontmatter;
|
|
15309
|
+
}
|
|
15310
|
+
toRulesyncCommand() {
|
|
15311
|
+
const { description, name, invokable: _invokable, ...restFields } = this.frontmatter;
|
|
15312
|
+
const continueFields = {
|
|
15313
|
+
...name !== void 0 && name !== commandNameFromFilePath(this.relativeFilePath) && { name },
|
|
15314
|
+
...restFields
|
|
15315
|
+
};
|
|
15316
|
+
const rulesyncFrontmatter = {
|
|
15317
|
+
targets: ["*"],
|
|
15318
|
+
description,
|
|
15319
|
+
...Object.keys(continueFields).length > 0 && { continue: continueFields }
|
|
15320
|
+
};
|
|
15321
|
+
const fileContent = stringifyFrontmatter(this.body, rulesyncFrontmatter);
|
|
15322
|
+
return new RulesyncCommand({
|
|
15323
|
+
outputRoot: ".",
|
|
15324
|
+
frontmatter: rulesyncFrontmatter,
|
|
15325
|
+
body: this.body,
|
|
15326
|
+
relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
|
|
15327
|
+
relativeFilePath: this.relativeFilePath,
|
|
15328
|
+
fileContent,
|
|
15329
|
+
validate: true
|
|
15330
|
+
});
|
|
15331
|
+
}
|
|
15332
|
+
static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false }) {
|
|
15333
|
+
const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
|
|
15334
|
+
const relativeFilePath = rulesyncCommand.getRelativeFilePath();
|
|
15335
|
+
const continueFields = rulesyncFrontmatter.continue ?? {};
|
|
15336
|
+
const continueFrontmatter = {
|
|
15337
|
+
name: commandNameFromFilePath(relativeFilePath),
|
|
15338
|
+
description: rulesyncFrontmatter.description,
|
|
15339
|
+
...continueFields,
|
|
15340
|
+
invokable: true
|
|
15341
|
+
};
|
|
15342
|
+
const body = rulesyncCommand.getBody();
|
|
15343
|
+
const paths = this.getSettablePaths({ global });
|
|
15344
|
+
return new ContinueCommand({
|
|
15345
|
+
outputRoot,
|
|
15346
|
+
frontmatter: continueFrontmatter,
|
|
15347
|
+
body,
|
|
15348
|
+
relativeDirPath: paths.relativeDirPath,
|
|
15349
|
+
relativeFilePath,
|
|
15350
|
+
validate
|
|
15351
|
+
});
|
|
15352
|
+
}
|
|
15353
|
+
validate() {
|
|
15354
|
+
if (!this.frontmatter) return {
|
|
15355
|
+
success: true,
|
|
15356
|
+
error: null
|
|
15357
|
+
};
|
|
15358
|
+
const result = ContinueCommandFrontmatterSchema.safeParse(this.frontmatter);
|
|
15359
|
+
if (result.success) return {
|
|
15360
|
+
success: true,
|
|
15361
|
+
error: null
|
|
15362
|
+
};
|
|
15363
|
+
return {
|
|
15364
|
+
success: false,
|
|
15365
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${join(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
|
|
15366
|
+
};
|
|
15367
|
+
}
|
|
15368
|
+
static isTargetedByRulesyncCommand(rulesyncCommand) {
|
|
15369
|
+
return this.isTargetedByRulesyncCommandDefault({
|
|
15370
|
+
rulesyncCommand,
|
|
15371
|
+
toolTarget: "continue"
|
|
15372
|
+
});
|
|
15373
|
+
}
|
|
15374
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
|
|
15375
|
+
const paths = this.getSettablePaths({ global });
|
|
15376
|
+
const filePath = join(outputRoot, paths.relativeDirPath, relativeFilePath);
|
|
15377
|
+
const { frontmatter, body: content } = parseFrontmatter(await readFileContent(filePath), filePath);
|
|
15378
|
+
const result = ContinueCommandFrontmatterSchema.safeParse(frontmatter);
|
|
15379
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
|
|
15380
|
+
return new ContinueCommand({
|
|
15381
|
+
outputRoot,
|
|
15382
|
+
relativeDirPath: paths.relativeDirPath,
|
|
15383
|
+
relativeFilePath,
|
|
15384
|
+
frontmatter: result.data,
|
|
15385
|
+
body: content.trim(),
|
|
15386
|
+
validate
|
|
15387
|
+
});
|
|
15388
|
+
}
|
|
15389
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
15390
|
+
return new ContinueCommand({
|
|
15391
|
+
outputRoot,
|
|
15392
|
+
relativeDirPath,
|
|
15393
|
+
relativeFilePath,
|
|
15394
|
+
frontmatter: { description: "" },
|
|
15395
|
+
body: "",
|
|
15396
|
+
validate: false
|
|
15397
|
+
});
|
|
15398
|
+
}
|
|
15399
|
+
};
|
|
15400
|
+
//#endregion
|
|
15175
15401
|
//#region src/constants/copilot-paths.ts
|
|
15176
15402
|
const COPILOT_DIR = ".copilot";
|
|
15177
15403
|
const GITHUB_DIR = ".github";
|
|
@@ -19272,6 +19498,16 @@ const toolCommandFactories = /* @__PURE__ */ new Map([
|
|
|
19272
19498
|
supportsSubdirectory: false
|
|
19273
19499
|
}
|
|
19274
19500
|
}],
|
|
19501
|
+
["continue", {
|
|
19502
|
+
class: ContinueCommand,
|
|
19503
|
+
meta: {
|
|
19504
|
+
extension: "md",
|
|
19505
|
+
supportsProject: true,
|
|
19506
|
+
supportsGlobal: true,
|
|
19507
|
+
isSimulated: false,
|
|
19508
|
+
supportsSubdirectory: false
|
|
19509
|
+
}
|
|
19510
|
+
}],
|
|
19275
19511
|
["copilot", {
|
|
19276
19512
|
class: CopilotCommand,
|
|
19277
19513
|
meta: {
|
|
@@ -22621,6 +22857,159 @@ var CodexcliHooks = class CodexcliHooks extends ToolHooks {
|
|
|
22621
22857
|
}
|
|
22622
22858
|
};
|
|
22623
22859
|
//#endregion
|
|
22860
|
+
//#region src/features/hooks/continue-hooks.ts
|
|
22861
|
+
const CONTINUE_CONVERTER_CONFIG = {
|
|
22862
|
+
supportedEvents: CONTINUE_HOOK_EVENTS,
|
|
22863
|
+
canonicalToToolEventNames: CANONICAL_TO_CONTINUE_EVENT_NAMES,
|
|
22864
|
+
toolToCanonicalEventNames: CONTINUE_TO_CANONICAL_EVENT_NAMES,
|
|
22865
|
+
projectDirVar: "$CONTINUE_PROJECT_DIR",
|
|
22866
|
+
prefixDotRelativeCommandsOnly: true,
|
|
22867
|
+
noMatcherEvents: /* @__PURE__ */ new Set([
|
|
22868
|
+
"beforeSubmitPrompt",
|
|
22869
|
+
"stop",
|
|
22870
|
+
"teammateIdle",
|
|
22871
|
+
"taskCompleted",
|
|
22872
|
+
"worktreeCreate",
|
|
22873
|
+
"worktreeRemove"
|
|
22874
|
+
]),
|
|
22875
|
+
supportedHookTypes: /* @__PURE__ */ new Set([
|
|
22876
|
+
"command",
|
|
22877
|
+
"prompt",
|
|
22878
|
+
"http",
|
|
22879
|
+
"agent"
|
|
22880
|
+
]),
|
|
22881
|
+
emitsPromptModel: true,
|
|
22882
|
+
stringPassthroughFields: [{
|
|
22883
|
+
canonical: "statusMessage",
|
|
22884
|
+
tool: "statusMessage"
|
|
22885
|
+
}],
|
|
22886
|
+
booleanPassthroughFields: [{
|
|
22887
|
+
canonical: "once",
|
|
22888
|
+
tool: "once"
|
|
22889
|
+
}, {
|
|
22890
|
+
canonical: "async",
|
|
22891
|
+
tool: "async",
|
|
22892
|
+
commandOnly: true
|
|
22893
|
+
}]
|
|
22894
|
+
};
|
|
22895
|
+
/**
|
|
22896
|
+
* Single spelling of the settings/hooks codec/policy: fail closed on an
|
|
22897
|
+
* unparseable root rather than replacing the user's Continue settings with
|
|
22898
|
+
* generated output.
|
|
22899
|
+
*/
|
|
22900
|
+
function parseContinueSettings(fileContent, filePath) {
|
|
22901
|
+
return parseSharedConfig({
|
|
22902
|
+
format: "json",
|
|
22903
|
+
fileContent,
|
|
22904
|
+
filePath,
|
|
22905
|
+
invalidRootPolicy: "error"
|
|
22906
|
+
});
|
|
22907
|
+
}
|
|
22908
|
+
/**
|
|
22909
|
+
* Continue CLI hooks.
|
|
22910
|
+
*
|
|
22911
|
+
* Hooks live under the top-level `hooks` key of `<project>/.continue/settings.json`
|
|
22912
|
+
* (project scope) and `~/.continue/settings.json` (user scope), in the
|
|
22913
|
+
* Claude-Code shape: `{ "<Event>": [{ "matcher"?: "<regex>", "hooks": [{
|
|
22914
|
+
* "type": "command" | "http" | "prompt" | "agent", ..., "timeout"?: <seconds>
|
|
22915
|
+
* }] }] }`. Both files also hold settings rulesync does not own, so generation
|
|
22916
|
+
* merges the `hooks` key into the existing file (see `SHARED_CONFIG_OWNERSHIP`)
|
|
22917
|
+
* instead of overwriting it. The CLI additionally reads
|
|
22918
|
+
* `.continue/settings.local.json` and the Claude Code settings files; rulesync
|
|
22919
|
+
* writes only the committable project file and the user file.
|
|
22920
|
+
*
|
|
22921
|
+
* @see https://github.com/continuedev/continue/blob/main/extensions/cli/src/hooks/hookConfig.ts
|
|
22922
|
+
* @see https://github.com/continuedev/continue/blob/main/extensions/cli/src/hooks/types.ts
|
|
22923
|
+
*/
|
|
22924
|
+
var ContinueHooks = class ContinueHooks extends ToolHooks {
|
|
22925
|
+
constructor(params) {
|
|
22926
|
+
super({
|
|
22927
|
+
...params,
|
|
22928
|
+
fileContent: params.fileContent ?? "{}"
|
|
22929
|
+
});
|
|
22930
|
+
}
|
|
22931
|
+
isDeletable() {
|
|
22932
|
+
return false;
|
|
22933
|
+
}
|
|
22934
|
+
static getSettablePaths(_options = {}) {
|
|
22935
|
+
return {
|
|
22936
|
+
relativeDirPath: CONTINUE_DIR,
|
|
22937
|
+
relativeFilePath: CONTINUE_SETTINGS_FILE_NAME
|
|
22938
|
+
};
|
|
22939
|
+
}
|
|
22940
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
22941
|
+
const paths = ContinueHooks.getSettablePaths({ global });
|
|
22942
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"hooks\":{}}";
|
|
22943
|
+
return new ContinueHooks({
|
|
22944
|
+
outputRoot,
|
|
22945
|
+
relativeDirPath: paths.relativeDirPath,
|
|
22946
|
+
relativeFilePath: paths.relativeFilePath,
|
|
22947
|
+
fileContent,
|
|
22948
|
+
validate
|
|
22949
|
+
});
|
|
22950
|
+
}
|
|
22951
|
+
static async fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false, logger }) {
|
|
22952
|
+
const paths = ContinueHooks.getSettablePaths({ global });
|
|
22953
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
22954
|
+
const existingContent = await readFileContentOrNull(filePath) ?? JSON.stringify({}, null, 2);
|
|
22955
|
+
const config = rulesyncHooks.getJson();
|
|
22956
|
+
const hooks = canonicalToToolHooks({
|
|
22957
|
+
config,
|
|
22958
|
+
toolOverrideHooks: config.continue?.hooks,
|
|
22959
|
+
converterConfig: CONTINUE_CONVERTER_CONFIG,
|
|
22960
|
+
logger
|
|
22961
|
+
});
|
|
22962
|
+
const fileContent = applySharedConfigPatch({
|
|
22963
|
+
fileKey: sharedConfigFileKey(paths),
|
|
22964
|
+
feature: "hooks",
|
|
22965
|
+
existingContent,
|
|
22966
|
+
patch: { hooks },
|
|
22967
|
+
filePath,
|
|
22968
|
+
logger
|
|
22969
|
+
});
|
|
22970
|
+
return new ContinueHooks({
|
|
22971
|
+
outputRoot,
|
|
22972
|
+
relativeDirPath: paths.relativeDirPath,
|
|
22973
|
+
relativeFilePath: paths.relativeFilePath,
|
|
22974
|
+
fileContent,
|
|
22975
|
+
validate
|
|
22976
|
+
});
|
|
22977
|
+
}
|
|
22978
|
+
toRulesyncHooks({ logger } = {}) {
|
|
22979
|
+
const configPath = join(this.getRelativeDirPath(), this.getRelativeFilePath());
|
|
22980
|
+
let settings;
|
|
22981
|
+
try {
|
|
22982
|
+
settings = parseContinueSettings(this.getFileContent(), configPath);
|
|
22983
|
+
} catch (error) {
|
|
22984
|
+
throw new Error(`Failed to parse Continue hooks content in ${configPath}: ${formatError(error)}`, { cause: error });
|
|
22985
|
+
}
|
|
22986
|
+
const hooks = toolHooksToCanonical({
|
|
22987
|
+
logger,
|
|
22988
|
+
hooks: settings.hooks,
|
|
22989
|
+
converterConfig: CONTINUE_CONVERTER_CONFIG
|
|
22990
|
+
});
|
|
22991
|
+
return this.toRulesyncHooksDefault({ fileContent: JSON.stringify(buildImportedHooksConfig({
|
|
22992
|
+
hooks,
|
|
22993
|
+
overrideKey: "continue"
|
|
22994
|
+
}), null, 2) });
|
|
22995
|
+
}
|
|
22996
|
+
validate() {
|
|
22997
|
+
return {
|
|
22998
|
+
success: true,
|
|
22999
|
+
error: null
|
|
23000
|
+
};
|
|
23001
|
+
}
|
|
23002
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
23003
|
+
return new ContinueHooks({
|
|
23004
|
+
outputRoot,
|
|
23005
|
+
relativeDirPath,
|
|
23006
|
+
relativeFilePath,
|
|
23007
|
+
fileContent: JSON.stringify({ hooks: {} }, null, 2),
|
|
23008
|
+
validate: false
|
|
23009
|
+
});
|
|
23010
|
+
}
|
|
23011
|
+
};
|
|
23012
|
+
//#endregion
|
|
22624
23013
|
//#region src/features/hooks/copilot-hooks.ts
|
|
22625
23014
|
/**
|
|
22626
23015
|
* Copilot hook entry as stored in .github/hooks/copilot-hooks.json.
|
|
@@ -27603,6 +27992,35 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
|
|
|
27603
27992
|
supportsMatcher: true,
|
|
27604
27993
|
matcherEvents: ["preToolUse", "postToolUse"]
|
|
27605
27994
|
}],
|
|
27995
|
+
["continue", {
|
|
27996
|
+
class: ContinueHooks,
|
|
27997
|
+
meta: {
|
|
27998
|
+
supportsProject: true,
|
|
27999
|
+
supportsGlobal: true,
|
|
28000
|
+
supportsImport: true
|
|
28001
|
+
},
|
|
28002
|
+
supportedEvents: CONTINUE_HOOK_EVENTS,
|
|
28003
|
+
supportedHookTypes: [
|
|
28004
|
+
"command",
|
|
28005
|
+
"http",
|
|
28006
|
+
"prompt",
|
|
28007
|
+
"agent"
|
|
28008
|
+
],
|
|
28009
|
+
supportsMatcher: true,
|
|
28010
|
+
matcherEvents: [
|
|
28011
|
+
"preToolUse",
|
|
28012
|
+
"postToolUse",
|
|
28013
|
+
"postToolUseFailure",
|
|
28014
|
+
"permissionRequest",
|
|
28015
|
+
"sessionStart",
|
|
28016
|
+
"sessionEnd",
|
|
28017
|
+
"notification",
|
|
28018
|
+
"subagentStart",
|
|
28019
|
+
"subagentStop",
|
|
28020
|
+
"preCompact",
|
|
28021
|
+
"configChange"
|
|
28022
|
+
]
|
|
28023
|
+
}],
|
|
27606
28024
|
["cortexcode", {
|
|
27607
28025
|
class: CortexcodeHooks,
|
|
27608
28026
|
meta: {
|
|
@@ -28323,6 +28741,60 @@ var ClineIgnore = class ClineIgnore extends ToolIgnore {
|
|
|
28323
28741
|
}
|
|
28324
28742
|
};
|
|
28325
28743
|
//#endregion
|
|
28744
|
+
//#region src/features/ignore/continue-ignore.ts
|
|
28745
|
+
/**
|
|
28746
|
+
* Continue ignore file implementation.
|
|
28747
|
+
*
|
|
28748
|
+
* - Project scope: the workspace-root `.continueignore`, which follows the
|
|
28749
|
+
* exact same rules as `.gitignore` and is layered on top of it.
|
|
28750
|
+
* - Global scope: `~/.continue/.continueignore`, respected for all workspaces.
|
|
28751
|
+
*
|
|
28752
|
+
* @see https://docs.continue.dev/reference/deprecated-codebase
|
|
28753
|
+
*/
|
|
28754
|
+
var ContinueIgnore = class ContinueIgnore extends ToolIgnore {
|
|
28755
|
+
static getSettablePaths({ global = false } = {}) {
|
|
28756
|
+
return {
|
|
28757
|
+
relativeDirPath: global ? CONTINUE_DIR : ".",
|
|
28758
|
+
relativeFilePath: CONTINUE_IGNORE_FILE_NAME
|
|
28759
|
+
};
|
|
28760
|
+
}
|
|
28761
|
+
toRulesyncIgnore() {
|
|
28762
|
+
return this.toRulesyncIgnoreDefault();
|
|
28763
|
+
}
|
|
28764
|
+
static fromRulesyncIgnore({ outputRoot = process.cwd(), rulesyncIgnore, global = false }) {
|
|
28765
|
+
const paths = this.getSettablePaths({ global });
|
|
28766
|
+
return new ContinueIgnore({
|
|
28767
|
+
outputRoot,
|
|
28768
|
+
relativeDirPath: paths.relativeDirPath,
|
|
28769
|
+
relativeFilePath: paths.relativeFilePath,
|
|
28770
|
+
fileContent: rulesyncIgnore.getFileContent(),
|
|
28771
|
+
global
|
|
28772
|
+
});
|
|
28773
|
+
}
|
|
28774
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
28775
|
+
const { relativeDirPath, relativeFilePath } = this.getSettablePaths({ global });
|
|
28776
|
+
const fileContent = await readFileContent(join(outputRoot, relativeDirPath, relativeFilePath));
|
|
28777
|
+
return new ContinueIgnore({
|
|
28778
|
+
outputRoot,
|
|
28779
|
+
relativeDirPath,
|
|
28780
|
+
relativeFilePath,
|
|
28781
|
+
fileContent,
|
|
28782
|
+
validate,
|
|
28783
|
+
global
|
|
28784
|
+
});
|
|
28785
|
+
}
|
|
28786
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
28787
|
+
return new ContinueIgnore({
|
|
28788
|
+
outputRoot,
|
|
28789
|
+
relativeDirPath,
|
|
28790
|
+
relativeFilePath,
|
|
28791
|
+
fileContent: "",
|
|
28792
|
+
validate: false,
|
|
28793
|
+
global
|
|
28794
|
+
});
|
|
28795
|
+
}
|
|
28796
|
+
};
|
|
28797
|
+
//#endregion
|
|
28326
28798
|
//#region src/constants/crush-paths.ts
|
|
28327
28799
|
const CRUSH_RULE_FILE_NAME = "CRUSH.md";
|
|
28328
28800
|
const CRUSH_GLOBAL_DIR = join(".config", "crush");
|
|
@@ -29437,6 +29909,7 @@ const toolIgnoreFactories = /* @__PURE__ */ new Map([
|
|
|
29437
29909
|
["claudecode", { class: ClaudecodeIgnore }],
|
|
29438
29910
|
["claudecode-legacy", { class: ClaudecodeIgnore }],
|
|
29439
29911
|
["cline", { class: ClineIgnore }],
|
|
29912
|
+
["continue", { class: ContinueIgnore }],
|
|
29440
29913
|
["crush", { class: CrushIgnore }],
|
|
29441
29914
|
["cursor", { class: CursorIgnore }],
|
|
29442
29915
|
["hermesagent", { class: HermesagentIgnore }],
|
|
@@ -29457,6 +29930,7 @@ const toolIgnoreFactories = /* @__PURE__ */ new Map([
|
|
|
29457
29930
|
]);
|
|
29458
29931
|
const ignoreProcessorToolTargets = [...toolIgnoreFactories.keys()];
|
|
29459
29932
|
const ignoreProcessorGlobalToolTargets = [
|
|
29933
|
+
"continue",
|
|
29460
29934
|
"devin",
|
|
29461
29935
|
"kiro",
|
|
29462
29936
|
"kiro-cli",
|
|
@@ -31160,6 +31634,230 @@ var CodexcliMcp = class CodexcliMcp extends ToolMcp {
|
|
|
31160
31634
|
}
|
|
31161
31635
|
};
|
|
31162
31636
|
//#endregion
|
|
31637
|
+
//#region src/features/mcp/continue-mcp.ts
|
|
31638
|
+
/**
|
|
31639
|
+
* Parse a Continue MCP file. Continue reads the files under `mcpServers/` as
|
|
31640
|
+
* JSONC, so comments and trailing commas in a hand-written file are accepted;
|
|
31641
|
+
* malformed content or a non-object root (`null`, an array, a scalar) fails
|
|
31642
|
+
* closed rather than being spread into the regenerated file.
|
|
31643
|
+
*/
|
|
31644
|
+
function parseContinueMcpConfig({ fileContent, relativePath }) {
|
|
31645
|
+
let parsed;
|
|
31646
|
+
try {
|
|
31647
|
+
parsed = parseJsonc(fileContent);
|
|
31648
|
+
} catch (error) {
|
|
31649
|
+
throw new Error(`Failed to parse Continue MCP config at ${relativePath}: ${formatError(error)}`, { cause: error });
|
|
31650
|
+
}
|
|
31651
|
+
if (!isPlainObject$1(parsed)) throw new Error(`Failed to parse Continue MCP config at ${relativePath}: expected a JSON object at the root`);
|
|
31652
|
+
return parsed;
|
|
31653
|
+
}
|
|
31654
|
+
/**
|
|
31655
|
+
* The remote transport Continue reads a server as. Continue's JSON schema
|
|
31656
|
+
* accepts `type: "sse"` or `type: "http"` (streamable HTTP) for a `url`
|
|
31657
|
+
* server; a bare `url` defaults to `http`, and the canonical
|
|
31658
|
+
* `streamable-http` spelling is folded into it. A `ws(s)://` URL or any other
|
|
31659
|
+
* stated transport has no Continue equivalent, so `undefined` tells the
|
|
31660
|
+
* caller to skip the server.
|
|
31661
|
+
* @see https://docs.continue.dev/customize/deep-dives/mcp
|
|
31662
|
+
*/
|
|
31663
|
+
function asContinueRemoteType(stated, url) {
|
|
31664
|
+
if (stated === "sse") return "sse";
|
|
31665
|
+
if (stated === "http" || stated === "streamable-http") return "http";
|
|
31666
|
+
if (stated === void 0) return /^wss?:\/\//i.test(url) ? void 0 : "http";
|
|
31667
|
+
}
|
|
31668
|
+
/**
|
|
31669
|
+
* Convert the canonical server map to the shape Continue's JSON MCP schema
|
|
31670
|
+
* accepts: a remote server is `{ type, url, headers? }` and a stdio server is
|
|
31671
|
+
* `{ type: "stdio", command, args?, env? }`. Continue validates the whole
|
|
31672
|
+
* file with a strict union — one server that matches neither shape (an
|
|
31673
|
+
* unknown transport, a non-string `env` value) makes Continue drop every
|
|
31674
|
+
* server in the file — so only the documented keys are emitted and anything
|
|
31675
|
+
* else (`timeout`, `oauth`, rulesync-only fields) is left out. `envFile` is
|
|
31676
|
+
* accepted by Continue's schema but ignored with a warning at load time, so
|
|
31677
|
+
* it is dropped here with a warning instead of being written as if it worked.
|
|
31678
|
+
*
|
|
31679
|
+
* A server Continue cannot start or reach — no transport at all, a remote
|
|
31680
|
+
* transport without a URL, a WebSocket URL, or a stdio entry without a
|
|
31681
|
+
* command — is skipped with a warning rather than written in a form that
|
|
31682
|
+
* would invalidate the file.
|
|
31683
|
+
* @see https://docs.continue.dev/customize/deep-dives/mcp
|
|
31684
|
+
*/
|
|
31685
|
+
function convertToContinueFormat(mcpServers, logger) {
|
|
31686
|
+
const result = {};
|
|
31687
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
31688
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
31689
|
+
if (declaresNoTransport(serverConfig)) {
|
|
31690
|
+
warnAndSkipMcpServer({
|
|
31691
|
+
toolName: "Continue",
|
|
31692
|
+
serverName,
|
|
31693
|
+
reason: "no transport",
|
|
31694
|
+
logger
|
|
31695
|
+
});
|
|
31696
|
+
continue;
|
|
31697
|
+
}
|
|
31698
|
+
const converted = isRemoteMcpServer(serverConfig) ? convertRemoteServer({
|
|
31699
|
+
serverName,
|
|
31700
|
+
serverConfig,
|
|
31701
|
+
logger
|
|
31702
|
+
}) : convertStdioServer({
|
|
31703
|
+
serverName,
|
|
31704
|
+
serverConfig,
|
|
31705
|
+
logger
|
|
31706
|
+
});
|
|
31707
|
+
if (converted !== void 0) result[serverName] = converted;
|
|
31708
|
+
}
|
|
31709
|
+
return result;
|
|
31710
|
+
}
|
|
31711
|
+
function convertRemoteServer({ serverName, serverConfig, logger }) {
|
|
31712
|
+
const url = resolveRemoteMcpUrl(serverConfig);
|
|
31713
|
+
if (!url) {
|
|
31714
|
+
warnAndSkipMcpServer({
|
|
31715
|
+
toolName: "Continue",
|
|
31716
|
+
serverName,
|
|
31717
|
+
reason: "a remote transport without a url",
|
|
31718
|
+
logger
|
|
31719
|
+
});
|
|
31720
|
+
return;
|
|
31721
|
+
}
|
|
31722
|
+
const stated = serverConfig.type ?? serverConfig.transport;
|
|
31723
|
+
const remoteType = asContinueRemoteType(typeof stated === "string" ? stated : void 0, url);
|
|
31724
|
+
if (remoteType === void 0) {
|
|
31725
|
+
warnAndSkipMcpServer({
|
|
31726
|
+
toolName: "Continue",
|
|
31727
|
+
serverName,
|
|
31728
|
+
reason: stated === void 0 ? "a WebSocket url, which Continue's remote transports (http and sse) cannot reach" : `the "${String(stated)}" transport, which Continue does not offer for remote servers (only http and sse)`,
|
|
31729
|
+
logger
|
|
31730
|
+
});
|
|
31731
|
+
return;
|
|
31732
|
+
}
|
|
31733
|
+
const converted = {
|
|
31734
|
+
type: remoteType,
|
|
31735
|
+
url
|
|
31736
|
+
};
|
|
31737
|
+
if (isRecord$1(serverConfig.headers)) converted.headers = omitPrototypePollutionKeys(serverConfig.headers);
|
|
31738
|
+
return converted;
|
|
31739
|
+
}
|
|
31740
|
+
function convertStdioServer({ serverName, serverConfig, logger }) {
|
|
31741
|
+
const [command, ...args] = (typeof serverConfig.command === "string" ? serverConfig.command !== "" : Array.isArray(serverConfig.command) && serverConfig.command.length > 0) ? resolveLocalMcpCommand(serverConfig) : [];
|
|
31742
|
+
if (!command) {
|
|
31743
|
+
warnAndSkipMcpServer({
|
|
31744
|
+
toolName: "Continue",
|
|
31745
|
+
serverName,
|
|
31746
|
+
reason: "a stdio transport without a command",
|
|
31747
|
+
logger
|
|
31748
|
+
});
|
|
31749
|
+
return;
|
|
31750
|
+
}
|
|
31751
|
+
const converted = {
|
|
31752
|
+
type: "stdio",
|
|
31753
|
+
command
|
|
31754
|
+
};
|
|
31755
|
+
if (args.length > 0) converted.args = args;
|
|
31756
|
+
if (isRecord$1(serverConfig.env)) converted.env = omitPrototypePollutionKeys(serverConfig.env);
|
|
31757
|
+
if (typeof serverConfig.envFile === "string") logger?.warn(`Continue ignores "envFile" for MCP servers, so the envFile of server "${serverName}" was not written; put the variables in "env" instead.`);
|
|
31758
|
+
return converted;
|
|
31759
|
+
}
|
|
31760
|
+
/**
|
|
31761
|
+
* Convert Continue's server map back to the canonical shape. The documented
|
|
31762
|
+
* spelling (`type` + `command`/`url`) is already canonical, so entries pass
|
|
31763
|
+
* through with only prototype-pollution keys dropped.
|
|
31764
|
+
*/
|
|
31765
|
+
function convertFromContinueFormat(mcpServers) {
|
|
31766
|
+
if (!isMcpServers(mcpServers)) return {};
|
|
31767
|
+
const result = {};
|
|
31768
|
+
for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
|
|
31769
|
+
if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
|
|
31770
|
+
result[serverName] = omitPrototypePollutionKeys(serverConfig);
|
|
31771
|
+
}
|
|
31772
|
+
return result;
|
|
31773
|
+
}
|
|
31774
|
+
/**
|
|
31775
|
+
* Continue MCP configuration.
|
|
31776
|
+
*
|
|
31777
|
+
* Continue loads every `*.json` file under `.continue/mcpServers/` (project)
|
|
31778
|
+
* and `~/.continue/mcpServers/` (global); rulesync owns one of them,
|
|
31779
|
+
* `mcp.json`, in the `{ "mcpServers": { ... } }` shape the docs show. Other
|
|
31780
|
+
* files in the directory, and top-level sibling keys of `mcp.json`, are left
|
|
31781
|
+
* alone. The global file is not deleted by `--delete` (it lives outside the
|
|
31782
|
+
* project), while the project file is rulesync's own and is.
|
|
31783
|
+
*
|
|
31784
|
+
* @see https://docs.continue.dev/customize/deep-dives/mcp
|
|
31785
|
+
*/
|
|
31786
|
+
var ContinueMcp = class ContinueMcp extends ToolMcp {
|
|
31787
|
+
json;
|
|
31788
|
+
constructor(params) {
|
|
31789
|
+
super(params);
|
|
31790
|
+
this.json = this.fileContent === void 0 ? {} : parseContinueMcpConfig({
|
|
31791
|
+
fileContent: this.fileContent,
|
|
31792
|
+
relativePath: join(this.relativeDirPath, this.relativeFilePath)
|
|
31793
|
+
});
|
|
31794
|
+
}
|
|
31795
|
+
getJson() {
|
|
31796
|
+
return this.json;
|
|
31797
|
+
}
|
|
31798
|
+
isDeletable() {
|
|
31799
|
+
return !this.global;
|
|
31800
|
+
}
|
|
31801
|
+
static getSettablePaths(_options = {}) {
|
|
31802
|
+
return {
|
|
31803
|
+
relativeDirPath: CONTINUE_MCP_DIR_PATH,
|
|
31804
|
+
relativeFilePath: CONTINUE_MCP_FILE_NAME
|
|
31805
|
+
};
|
|
31806
|
+
}
|
|
31807
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
31808
|
+
const paths = this.getSettablePaths({ global });
|
|
31809
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"mcpServers\":{}}";
|
|
31810
|
+
return new ContinueMcp({
|
|
31811
|
+
outputRoot,
|
|
31812
|
+
relativeDirPath: paths.relativeDirPath,
|
|
31813
|
+
relativeFilePath: paths.relativeFilePath,
|
|
31814
|
+
fileContent,
|
|
31815
|
+
validate,
|
|
31816
|
+
global
|
|
31817
|
+
});
|
|
31818
|
+
}
|
|
31819
|
+
static async fromRulesyncMcp({ outputRoot = process.cwd(), rulesyncMcp, validate = true, global = false, logger }) {
|
|
31820
|
+
const paths = this.getSettablePaths({ global });
|
|
31821
|
+
const json = parseContinueMcpConfig({
|
|
31822
|
+
fileContent: await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "{\"mcpServers\":{}}",
|
|
31823
|
+
relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
|
|
31824
|
+
});
|
|
31825
|
+
const mcpServers = convertToContinueFormat(rulesyncMcp.getMcpServers(), logger);
|
|
31826
|
+
const continueConfig = {
|
|
31827
|
+
...json,
|
|
31828
|
+
mcpServers
|
|
31829
|
+
};
|
|
31830
|
+
return new ContinueMcp({
|
|
31831
|
+
outputRoot,
|
|
31832
|
+
relativeDirPath: paths.relativeDirPath,
|
|
31833
|
+
relativeFilePath: paths.relativeFilePath,
|
|
31834
|
+
fileContent: JSON.stringify(continueConfig, null, 2),
|
|
31835
|
+
validate,
|
|
31836
|
+
global
|
|
31837
|
+
});
|
|
31838
|
+
}
|
|
31839
|
+
toRulesyncMcp() {
|
|
31840
|
+
const mcpServers = convertFromContinueFormat(this.json.mcpServers);
|
|
31841
|
+
return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers }, null, 2) });
|
|
31842
|
+
}
|
|
31843
|
+
validate() {
|
|
31844
|
+
return {
|
|
31845
|
+
success: true,
|
|
31846
|
+
error: null
|
|
31847
|
+
};
|
|
31848
|
+
}
|
|
31849
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
31850
|
+
return new ContinueMcp({
|
|
31851
|
+
outputRoot,
|
|
31852
|
+
relativeDirPath,
|
|
31853
|
+
relativeFilePath,
|
|
31854
|
+
fileContent: "{}",
|
|
31855
|
+
validate: false,
|
|
31856
|
+
global
|
|
31857
|
+
});
|
|
31858
|
+
}
|
|
31859
|
+
};
|
|
31860
|
+
//#endregion
|
|
31163
31861
|
//#region src/features/mcp/copilot-mcp.ts
|
|
31164
31862
|
function convertFromCopilotFormat(copilotConfig) {
|
|
31165
31863
|
return copilotConfig.servers ?? {};
|
|
@@ -36892,6 +37590,15 @@ const toolMcpFactories = /* @__PURE__ */ new Map([
|
|
|
36892
37590
|
supportsDisabledTools: true
|
|
36893
37591
|
}
|
|
36894
37592
|
}],
|
|
37593
|
+
["continue", {
|
|
37594
|
+
class: ContinueMcp,
|
|
37595
|
+
meta: {
|
|
37596
|
+
supportsProject: true,
|
|
37597
|
+
supportsGlobal: true,
|
|
37598
|
+
supportsEnabledTools: false,
|
|
37599
|
+
supportsDisabledTools: false
|
|
37600
|
+
}
|
|
37601
|
+
}],
|
|
36895
37602
|
["copilot", {
|
|
36896
37603
|
class: CopilotMcp,
|
|
36897
37604
|
meta: {
|
|
@@ -38350,7 +39057,7 @@ const ANTIGRAVITY_CLI_TO_CANONICAL_TOOL_NAMES = {
|
|
|
38350
39057
|
function toAntigravityCliToolName(canonical) {
|
|
38351
39058
|
return CANONICAL_TO_ANTIGRAVITY_CLI_TOOL_NAMES[canonical] ?? canonical;
|
|
38352
39059
|
}
|
|
38353
|
-
function toCanonicalToolName$
|
|
39060
|
+
function toCanonicalToolName$7(cliName) {
|
|
38354
39061
|
return ANTIGRAVITY_CLI_TO_CANONICAL_TOOL_NAMES[cliName] ?? cliName;
|
|
38355
39062
|
}
|
|
38356
39063
|
/**
|
|
@@ -38566,7 +39273,7 @@ function convertAntigravityCliToRulesyncPermissions(params) {
|
|
|
38566
39273
|
const processEntries = (entries, action) => {
|
|
38567
39274
|
for (const entry of entries) {
|
|
38568
39275
|
const { toolName, pattern } = parsePermissionEntry$1(entry);
|
|
38569
|
-
const canonical = toCanonicalToolName$
|
|
39276
|
+
const canonical = toCanonicalToolName$7(toolName);
|
|
38570
39277
|
if (!permission[canonical]) permission[canonical] = {};
|
|
38571
39278
|
permission[canonical][pattern] = action;
|
|
38572
39279
|
}
|
|
@@ -38826,7 +39533,7 @@ const AUGMENT_TO_CANONICAL_TOOL_NAMES = Object.fromEntries(Object.entries(CANONI
|
|
|
38826
39533
|
function toAugmentToolName(canonical) {
|
|
38827
39534
|
return CANONICAL_TO_AUGMENT_TOOL_NAMES[canonical] ?? canonical;
|
|
38828
39535
|
}
|
|
38829
|
-
function toCanonicalToolName$
|
|
39536
|
+
function toCanonicalToolName$6(augmentName) {
|
|
38830
39537
|
return AUGMENT_TO_CANONICAL_TOOL_NAMES[augmentName] ?? augmentName;
|
|
38831
39538
|
}
|
|
38832
39539
|
function actionToAugmentType(action) {
|
|
@@ -39273,7 +39980,7 @@ function convertAugmentToRulesyncPermissions({ entries, logger }) {
|
|
|
39273
39980
|
if (isSpecialEntry(entry)) continue;
|
|
39274
39981
|
const type = entry.permission.type;
|
|
39275
39982
|
if (!isBasicAugmentType(type)) continue;
|
|
39276
|
-
const canonical = toCanonicalToolName$
|
|
39983
|
+
const canonical = toCanonicalToolName$6(entry.toolName);
|
|
39277
39984
|
if (forbiddenMapKeys.has(canonical)) {
|
|
39278
39985
|
logger?.warn(`AugmentCode permissions: skipping entry for tool '${entry.toolName}' because it maps to the reserved object key '${canonical}', which cannot be used as a permission key.`);
|
|
39279
39986
|
continue;
|
|
@@ -39508,7 +40215,7 @@ const CLAUDE_TO_CANONICAL_TOOL_NAMES = Object.fromEntries(Object.entries(CANONIC
|
|
|
39508
40215
|
function toClaudeToolName(canonical) {
|
|
39509
40216
|
return CANONICAL_TO_CLAUDE_TOOL_NAMES[canonical] ?? canonical;
|
|
39510
40217
|
}
|
|
39511
|
-
function toCanonicalToolName$
|
|
40218
|
+
function toCanonicalToolName$5(claudeName) {
|
|
39512
40219
|
return CLAUDE_TO_CANONICAL_TOOL_NAMES[claudeName] ?? claudeName;
|
|
39513
40220
|
}
|
|
39514
40221
|
/**
|
|
@@ -40403,7 +41110,7 @@ function convertClaudeToRulesyncPermissions(params) {
|
|
|
40403
41110
|
const processEntries = (entries, action) => {
|
|
40404
41111
|
for (const entry of entries) {
|
|
40405
41112
|
const { toolName, pattern } = parseClaudePermissionEntry(entry);
|
|
40406
|
-
const canonical = toCanonicalToolName$
|
|
41113
|
+
const canonical = toCanonicalToolName$5(toolName);
|
|
40407
41114
|
if (!permission[canonical]) permission[canonical] = {};
|
|
40408
41115
|
permission[canonical][pattern] = action;
|
|
40409
41116
|
}
|
|
@@ -41226,6 +41933,264 @@ function mapBashActionToDecision(action) {
|
|
|
41226
41933
|
return "forbidden";
|
|
41227
41934
|
}
|
|
41228
41935
|
//#endregion
|
|
41936
|
+
//#region src/features/permissions/continue-permissions.ts
|
|
41937
|
+
const CONTINUE_GLOBAL_ONLY_MESSAGE = "Continue permissions are global-only; use --global to sync ~/.continue/permissions.yaml";
|
|
41938
|
+
const CATCH_ALL_PATTERN$4 = "*";
|
|
41939
|
+
const CANONICAL_TO_CONTINUE_TOOL_NAMES = {
|
|
41940
|
+
bash: "Bash",
|
|
41941
|
+
read: "Read",
|
|
41942
|
+
edit: "Edit",
|
|
41943
|
+
write: "Write",
|
|
41944
|
+
webfetch: "Fetch"
|
|
41945
|
+
};
|
|
41946
|
+
const CONTINUE_TO_CANONICAL_TOOL_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_CONTINUE_TOOL_NAMES).map(([k, v]) => [v, k]));
|
|
41947
|
+
const ACTION_TO_CONTINUE_LIST = {
|
|
41948
|
+
allow: "allow",
|
|
41949
|
+
ask: "ask",
|
|
41950
|
+
deny: "exclude"
|
|
41951
|
+
};
|
|
41952
|
+
const CONTINUE_LIST_TO_ACTION = {
|
|
41953
|
+
allow: "allow",
|
|
41954
|
+
ask: "ask",
|
|
41955
|
+
exclude: "deny"
|
|
41956
|
+
};
|
|
41957
|
+
const CONTINUE_PERMISSION_LIST_KEYS = [
|
|
41958
|
+
"exclude",
|
|
41959
|
+
"ask",
|
|
41960
|
+
"allow"
|
|
41961
|
+
];
|
|
41962
|
+
const CONTINUE_PATTERN_RE = /^([^(]+)(?:\(([^)]*)\))?$/;
|
|
41963
|
+
function toContinueToolName(canonical) {
|
|
41964
|
+
return Object.hasOwn(CANONICAL_TO_CONTINUE_TOOL_NAMES, canonical) ? CANONICAL_TO_CONTINUE_TOOL_NAMES[canonical] ?? canonical : canonical;
|
|
41965
|
+
}
|
|
41966
|
+
function toCanonicalToolName$4(continueName) {
|
|
41967
|
+
return Object.hasOwn(CONTINUE_TO_CANONICAL_TOOL_NAMES, continueName) ? CONTINUE_TO_CANONICAL_TOOL_NAMES[continueName] ?? continueName : continueName;
|
|
41968
|
+
}
|
|
41969
|
+
/**
|
|
41970
|
+
* Build a permissions.yaml entry: the bare tool name for the catch-all, else
|
|
41971
|
+
* `Tool(pattern)`.
|
|
41972
|
+
*/
|
|
41973
|
+
function buildContinuePermissionEntry(toolName, pattern) {
|
|
41974
|
+
return pattern === CATCH_ALL_PATTERN$4 ? toolName : `${toolName}(${pattern})`;
|
|
41975
|
+
}
|
|
41976
|
+
/**
|
|
41977
|
+
* Split a permissions.yaml entry into its tool name and argument pattern, or
|
|
41978
|
+
* `undefined` for an entry outside the loader's grammar.
|
|
41979
|
+
*/
|
|
41980
|
+
function parseContinuePermissionEntry(entry) {
|
|
41981
|
+
const match = CONTINUE_PATTERN_RE.exec(entry);
|
|
41982
|
+
if (!match) return;
|
|
41983
|
+
const toolName = match[1]?.trim() ?? "";
|
|
41984
|
+
const pattern = match[2]?.trim() ?? "";
|
|
41985
|
+
if (toolName === "") return;
|
|
41986
|
+
return {
|
|
41987
|
+
toolName,
|
|
41988
|
+
pattern: pattern === "" ? CATCH_ALL_PATTERN$4 : pattern
|
|
41989
|
+
};
|
|
41990
|
+
}
|
|
41991
|
+
/**
|
|
41992
|
+
* Permissions adapter for the Continue CLI (`cn`).
|
|
41993
|
+
*
|
|
41994
|
+
* Continue reads tool policies from `~/.continue/permissions.yaml`, three
|
|
41995
|
+
* string lists (`allow`, `ask`, `exclude`) of `Tool` / `Tool(pattern)` entries
|
|
41996
|
+
* evaluated exclude-first, then ask, then allow, first match wins. `exclude`
|
|
41997
|
+
* disables the tool. A `Tool(pattern)` entry glob-matches (`*` / `?`) the
|
|
41998
|
+
* tool's primary argument — the shell command for `Bash`, the path for
|
|
41999
|
+
* `Read` / `Edit` / `Write`, the URL for `Fetch`. The file is **global only**:
|
|
42000
|
+
* no project-scoped permissions file is read.
|
|
42001
|
+
*
|
|
42002
|
+
* Mapping (rulesync canonical -> Continue):
|
|
42003
|
+
* - Action: `allow` -> `allow`, `ask` -> `ask`, `deny` -> `exclude`.
|
|
42004
|
+
* - Tool name: `bash` -> `Bash`, `read` -> `Read`, `edit` -> `Edit`,
|
|
42005
|
+
* `write` -> `Write`, `webfetch` -> `Fetch`; any other category passes
|
|
42006
|
+
* through verbatim, and the all-tools `*` category becomes the bare `*`
|
|
42007
|
+
* entry that matches every tool.
|
|
42008
|
+
* - Entry: the catch-all `*` pattern is the bare tool name; every other
|
|
42009
|
+
* pattern is written as `Tool(pattern)`. A pattern holding a parenthesis
|
|
42010
|
+
* cannot be written (the loader would refuse the whole file) and is
|
|
42011
|
+
* reported and skipped.
|
|
42012
|
+
*
|
|
42013
|
+
* The CLI appends the user's own "always allow" decisions to the same file,
|
|
42014
|
+
* so entries naming a tool the canonical config does not manage are preserved
|
|
42015
|
+
* verbatim, the managed tools' entries are rebuilt, and the file is never
|
|
42016
|
+
* deleted.
|
|
42017
|
+
*
|
|
42018
|
+
* @see https://docs.continue.dev/cli/tool-permissions
|
|
42019
|
+
* @see https://github.com/continuedev/continue/blob/main/extensions/cli/src/permissions/permissionsYamlLoader.ts
|
|
42020
|
+
* @see https://github.com/continuedev/continue/blob/main/extensions/cli/src/permissions/permissionChecker.ts
|
|
42021
|
+
*/
|
|
42022
|
+
var ContinuePermissions = class ContinuePermissions extends ToolPermissions {
|
|
42023
|
+
constructor(params) {
|
|
42024
|
+
super({
|
|
42025
|
+
...params,
|
|
42026
|
+
fileContent: params.fileContent ?? ""
|
|
42027
|
+
});
|
|
42028
|
+
}
|
|
42029
|
+
isDeletable() {
|
|
42030
|
+
return false;
|
|
42031
|
+
}
|
|
42032
|
+
/**
|
|
42033
|
+
* `permissions.yaml` is Continue's file: rulesync merges into it when it
|
|
42034
|
+
* exists but does not create one that holds three empty lists — an absent
|
|
42035
|
+
* file and empty lists both mean "no policy, ask for everything".
|
|
42036
|
+
*/
|
|
42037
|
+
shouldSkipCreationWhenPayloadEmpty() {
|
|
42038
|
+
return true;
|
|
42039
|
+
}
|
|
42040
|
+
static getSettablePaths(_options) {
|
|
42041
|
+
return {
|
|
42042
|
+
relativeDirPath: CONTINUE_DIR,
|
|
42043
|
+
relativeFilePath: CONTINUE_PERMISSIONS_FILE_NAME
|
|
42044
|
+
};
|
|
42045
|
+
}
|
|
42046
|
+
static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
|
|
42047
|
+
if (!global) throw new Error(CONTINUE_GLOBAL_ONLY_MESSAGE);
|
|
42048
|
+
const paths = ContinuePermissions.getSettablePaths({ global });
|
|
42049
|
+
const fileContent = await readFileContentOrNull(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath)) ?? "";
|
|
42050
|
+
return new ContinuePermissions({
|
|
42051
|
+
outputRoot,
|
|
42052
|
+
relativeDirPath: paths.relativeDirPath,
|
|
42053
|
+
relativeFilePath: paths.relativeFilePath,
|
|
42054
|
+
fileContent,
|
|
42055
|
+
validate,
|
|
42056
|
+
global: true
|
|
42057
|
+
});
|
|
42058
|
+
}
|
|
42059
|
+
static async fromRulesyncPermissions({ outputRoot = process.cwd(), rulesyncPermissions, logger, global = false }) {
|
|
42060
|
+
if (!global) throw new Error(CONTINUE_GLOBAL_ONLY_MESSAGE);
|
|
42061
|
+
const paths = ContinuePermissions.getSettablePaths({ global });
|
|
42062
|
+
const filePath = join(outputRoot, paths.relativeDirPath, paths.relativeFilePath);
|
|
42063
|
+
const existingContent = await readFileContentOrNull(filePath) ?? "";
|
|
42064
|
+
let parsed;
|
|
42065
|
+
try {
|
|
42066
|
+
parsed = existingContent.trim() === "" ? {} : loadYaml(existingContent);
|
|
42067
|
+
} catch (error) {
|
|
42068
|
+
throw new Error(`Failed to parse existing Continue permissions.yaml at ${filePath}: ${formatError(error)}`, { cause: error });
|
|
42069
|
+
}
|
|
42070
|
+
const existing = parseContinuePermissionLists(isRecord$1(parsed) ? parsed : {});
|
|
42071
|
+
const config = rulesyncPermissions.getJson();
|
|
42072
|
+
const generated = convertRulesyncToContinuePermissions({
|
|
42073
|
+
config,
|
|
42074
|
+
logger
|
|
42075
|
+
});
|
|
42076
|
+
const managedToolNames = managedContinueToolNames(config);
|
|
42077
|
+
const lists = {};
|
|
42078
|
+
for (const key of CONTINUE_PERMISSION_LIST_KEYS) {
|
|
42079
|
+
const preserved = existing[key].filter((entry) => {
|
|
42080
|
+
const parsedEntry = parseContinuePermissionEntry(entry);
|
|
42081
|
+
return parsedEntry === void 0 || !managedToolNames.has(parsedEntry.toolName);
|
|
42082
|
+
});
|
|
42083
|
+
const merged = [.../* @__PURE__ */ new Set([...preserved, ...generated[key]])];
|
|
42084
|
+
if (merged.length > 0) lists[key] = merged;
|
|
42085
|
+
}
|
|
42086
|
+
return new ContinuePermissions({
|
|
42087
|
+
outputRoot,
|
|
42088
|
+
relativeDirPath: paths.relativeDirPath,
|
|
42089
|
+
relativeFilePath: paths.relativeFilePath,
|
|
42090
|
+
fileContent: Object.keys(lists).length === 0 ? "" : dump(lists),
|
|
42091
|
+
validate: true,
|
|
42092
|
+
global: true
|
|
42093
|
+
});
|
|
42094
|
+
}
|
|
42095
|
+
toRulesyncPermissions() {
|
|
42096
|
+
let parsed;
|
|
42097
|
+
try {
|
|
42098
|
+
const content = this.getFileContent();
|
|
42099
|
+
parsed = content.trim() === "" ? {} : loadYaml(content);
|
|
42100
|
+
} catch (error) {
|
|
42101
|
+
throw new Error(`Failed to parse Continue permissions content in ${join(this.getRelativeDirPath(), this.getRelativeFilePath())}: ${formatError(error)}`, { cause: error });
|
|
42102
|
+
}
|
|
42103
|
+
const rulesyncConfig = convertContinuePermissionsToRulesync(parseContinuePermissionLists(isRecord$1(parsed) ? parsed : {}));
|
|
42104
|
+
return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify(rulesyncConfig, null, 2) });
|
|
42105
|
+
}
|
|
42106
|
+
validate() {
|
|
42107
|
+
return {
|
|
42108
|
+
success: true,
|
|
42109
|
+
error: null
|
|
42110
|
+
};
|
|
42111
|
+
}
|
|
42112
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
|
|
42113
|
+
return new ContinuePermissions({
|
|
42114
|
+
outputRoot,
|
|
42115
|
+
relativeDirPath,
|
|
42116
|
+
relativeFilePath,
|
|
42117
|
+
fileContent: "",
|
|
42118
|
+
validate: false,
|
|
42119
|
+
global: true
|
|
42120
|
+
});
|
|
42121
|
+
}
|
|
42122
|
+
};
|
|
42123
|
+
/**
|
|
42124
|
+
* The Continue tool names the canonical config manages, i.e. the names its
|
|
42125
|
+
* categories map to. Entries naming any other tool are the user's (or the
|
|
42126
|
+
* CLI's) and survive a generate.
|
|
42127
|
+
*/
|
|
42128
|
+
function managedContinueToolNames(config) {
|
|
42129
|
+
return new Set(Object.keys(config.permission).map((category) => toContinueToolName(category)));
|
|
42130
|
+
}
|
|
42131
|
+
/**
|
|
42132
|
+
* Read the three lists out of a parsed permissions.yaml, tolerating a missing
|
|
42133
|
+
* or malformed list (the CLI itself discards the file in that case).
|
|
42134
|
+
*/
|
|
42135
|
+
function parseContinuePermissionLists(parsed) {
|
|
42136
|
+
return {
|
|
42137
|
+
exclude: isStringArray$2(parsed.exclude) ? parsed.exclude : [],
|
|
42138
|
+
ask: isStringArray$2(parsed.ask) ? parsed.ask : [],
|
|
42139
|
+
allow: isStringArray$2(parsed.allow) ? parsed.allow : []
|
|
42140
|
+
};
|
|
42141
|
+
}
|
|
42142
|
+
/**
|
|
42143
|
+
* Convert a rulesync permissions config into the three permissions.yaml lists.
|
|
42144
|
+
*/
|
|
42145
|
+
function convertRulesyncToContinuePermissions({ config, logger }) {
|
|
42146
|
+
const lists = {
|
|
42147
|
+
exclude: [],
|
|
42148
|
+
ask: [],
|
|
42149
|
+
allow: []
|
|
42150
|
+
};
|
|
42151
|
+
const actionByEntry = /* @__PURE__ */ new Map();
|
|
42152
|
+
for (const [category, rules] of Object.entries(honorAllToolsOnBash(config.permission))) {
|
|
42153
|
+
if (isPrototypePollutionKey(category)) continue;
|
|
42154
|
+
const toolName = category === "*" ? "*" : toContinueToolName(category);
|
|
42155
|
+
for (const [pattern, action] of Object.entries(rules)) {
|
|
42156
|
+
if (isPrototypePollutionKey(pattern)) continue;
|
|
42157
|
+
if (pattern.includes("(") || pattern.includes(")")) {
|
|
42158
|
+
logger?.warn(`Continue permissions.yaml cannot hold a parenthesis inside a pattern, so the "${action}" rule for "${category}" (pattern ${quoteValueForWarning(pattern)}) was skipped.`);
|
|
42159
|
+
continue;
|
|
42160
|
+
}
|
|
42161
|
+
if (toolName === "*" && pattern !== CATCH_ALL_PATTERN$4) {
|
|
42162
|
+
logger?.warn(`Continue permissions.yaml cannot scope a pattern to every tool, so the "${action}" rule for "*" (pattern ${quoteValueForWarning(pattern)}) was skipped.`);
|
|
42163
|
+
continue;
|
|
42164
|
+
}
|
|
42165
|
+
const entry = buildContinuePermissionEntry(toolName, pattern);
|
|
42166
|
+
const previous = actionByEntry.get(entry);
|
|
42167
|
+
if (previous !== void 0 && previous !== action) logger?.warn(`Continue permissions: rules from different categories both resolve to ${quoteValueForWarning(entry)} with conflicting actions (${previous} and ${action}). Both are written; Continue applies exclude first, then ask, then allow.`);
|
|
42168
|
+
actionByEntry.set(entry, action);
|
|
42169
|
+
lists[ACTION_TO_CONTINUE_LIST[action]].push(entry);
|
|
42170
|
+
}
|
|
42171
|
+
}
|
|
42172
|
+
for (const key of CONTINUE_PERMISSION_LIST_KEYS) lists[key] = [...new Set(lists[key])];
|
|
42173
|
+
return lists;
|
|
42174
|
+
}
|
|
42175
|
+
/**
|
|
42176
|
+
* Convert the three permissions.yaml lists back into a rulesync config.
|
|
42177
|
+
*/
|
|
42178
|
+
function convertContinuePermissionsToRulesync(lists) {
|
|
42179
|
+
const permission = {};
|
|
42180
|
+
for (const key of CONTINUE_PERMISSION_LIST_KEYS) {
|
|
42181
|
+
const action = CONTINUE_LIST_TO_ACTION[key];
|
|
42182
|
+
for (const entry of lists[key]) {
|
|
42183
|
+
const parsedEntry = parseContinuePermissionEntry(entry);
|
|
42184
|
+
if (parsedEntry === void 0) continue;
|
|
42185
|
+
if (isPrototypePollutionKey(parsedEntry.toolName) || isPrototypePollutionKey(parsedEntry.pattern)) continue;
|
|
42186
|
+
const category = toCanonicalToolName$4(parsedEntry.toolName);
|
|
42187
|
+
permission[category] ??= {};
|
|
42188
|
+
permission[category][parsedEntry.pattern] ??= action;
|
|
42189
|
+
}
|
|
42190
|
+
}
|
|
42191
|
+
return { permission };
|
|
42192
|
+
}
|
|
42193
|
+
//#endregion
|
|
41229
42194
|
//#region src/features/permissions/copilot-permissions.ts
|
|
41230
42195
|
/**
|
|
41231
42196
|
* The flat, dotted VS Code setting keys this adapter manages, one per canonical
|
|
@@ -51274,6 +52239,14 @@ const toolPermissionsFactories = /* @__PURE__ */ new Map([
|
|
|
51274
52239
|
supportsImport: true
|
|
51275
52240
|
}
|
|
51276
52241
|
}],
|
|
52242
|
+
["continue", {
|
|
52243
|
+
class: ContinuePermissions,
|
|
52244
|
+
meta: {
|
|
52245
|
+
supportsProject: false,
|
|
52246
|
+
supportsGlobal: true,
|
|
52247
|
+
supportsImport: true
|
|
52248
|
+
}
|
|
52249
|
+
}],
|
|
51277
52250
|
["copilot", {
|
|
51278
52251
|
class: CopilotPermissions,
|
|
51279
52252
|
meta: {
|
|
@@ -54024,6 +54997,154 @@ var CodexCliSkill = class CodexCliSkill extends ToolSkill {
|
|
|
54024
54997
|
}
|
|
54025
54998
|
};
|
|
54026
54999
|
//#endregion
|
|
55000
|
+
//#region src/features/skills/continue-skill.ts
|
|
55001
|
+
const ContinueSkillFrontmatterSchema = z.looseObject({
|
|
55002
|
+
name: z.string(),
|
|
55003
|
+
description: z.string()
|
|
55004
|
+
});
|
|
55005
|
+
/**
|
|
55006
|
+
* Represents a Continue skill directory.
|
|
55007
|
+
*
|
|
55008
|
+
* Continue discovers Anthropic-style `<name>/SKILL.md` directories under
|
|
55009
|
+
* `<project>/.continue/skills/` (project scope) and `~/.continue/skills/`
|
|
55010
|
+
* (user scope); it also reads `.claude/skills/`, which the claudecode target
|
|
55011
|
+
* covers. The frontmatter requires `name` (matching the directory name) and
|
|
55012
|
+
* `description`; supporting files next to `SKILL.md` are carried along.
|
|
55013
|
+
*
|
|
55014
|
+
* @see https://github.com/continuedev/continue/blob/main/extensions/cli/src/util/loadMarkdownSkills.ts
|
|
55015
|
+
*/
|
|
55016
|
+
var ContinueSkill = class ContinueSkill extends ToolSkill {
|
|
55017
|
+
constructor({ outputRoot = process.cwd(), relativeDirPath = CONTINUE_SKILLS_DIR_PATH, dirName, frontmatter, body, otherFiles = [], validate = true, global = false }) {
|
|
55018
|
+
super({
|
|
55019
|
+
outputRoot,
|
|
55020
|
+
relativeDirPath,
|
|
55021
|
+
dirName,
|
|
55022
|
+
mainFile: {
|
|
55023
|
+
name: SKILL_FILE_NAME,
|
|
55024
|
+
body,
|
|
55025
|
+
frontmatter: { ...frontmatter }
|
|
55026
|
+
},
|
|
55027
|
+
otherFiles,
|
|
55028
|
+
global
|
|
55029
|
+
});
|
|
55030
|
+
if (validate) {
|
|
55031
|
+
const result = this.validate();
|
|
55032
|
+
if (!result.success) throw result.error;
|
|
55033
|
+
}
|
|
55034
|
+
}
|
|
55035
|
+
static getSettablePaths(_options = {}) {
|
|
55036
|
+
return { relativeDirPath: CONTINUE_SKILLS_DIR_PATH };
|
|
55037
|
+
}
|
|
55038
|
+
getFrontmatter() {
|
|
55039
|
+
return ContinueSkillFrontmatterSchema.parse(this.requireMainFileFrontmatter());
|
|
55040
|
+
}
|
|
55041
|
+
getBody() {
|
|
55042
|
+
return this.mainFile?.body ?? "";
|
|
55043
|
+
}
|
|
55044
|
+
validate() {
|
|
55045
|
+
if (!this.mainFile) return {
|
|
55046
|
+
success: false,
|
|
55047
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: ${SKILL_FILE_NAME} file does not exist`)
|
|
55048
|
+
};
|
|
55049
|
+
const result = ContinueSkillFrontmatterSchema.safeParse(this.mainFile.frontmatter);
|
|
55050
|
+
if (!result.success) return {
|
|
55051
|
+
success: false,
|
|
55052
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${this.getDirPath()}: ${formatError(result.error)}`)
|
|
55053
|
+
};
|
|
55054
|
+
if (result.data.name !== this.getDirName()) return {
|
|
55055
|
+
success: false,
|
|
55056
|
+
error: /* @__PURE__ */ new Error(`${this.getDirPath()}: frontmatter name (${result.data.name}) must match directory name (${this.getDirName()})`)
|
|
55057
|
+
};
|
|
55058
|
+
return {
|
|
55059
|
+
success: true,
|
|
55060
|
+
error: null
|
|
55061
|
+
};
|
|
55062
|
+
}
|
|
55063
|
+
toRulesyncSkill() {
|
|
55064
|
+
const { name, description, ...continueSection } = this.getFrontmatter();
|
|
55065
|
+
const rulesyncFrontmatter = {
|
|
55066
|
+
name,
|
|
55067
|
+
description,
|
|
55068
|
+
targets: ["*"],
|
|
55069
|
+
...Object.keys(continueSection).length > 0 && { continue: continueSection }
|
|
55070
|
+
};
|
|
55071
|
+
return new RulesyncSkill({
|
|
55072
|
+
outputRoot: this.outputRoot,
|
|
55073
|
+
relativeDirPath: RULESYNC_SKILLS_RELATIVE_DIR_PATH,
|
|
55074
|
+
dirName: this.getDirName(),
|
|
55075
|
+
frontmatter: rulesyncFrontmatter,
|
|
55076
|
+
body: this.getBody(),
|
|
55077
|
+
otherFiles: this.getOtherFiles(),
|
|
55078
|
+
validate: true,
|
|
55079
|
+
global: this.global
|
|
55080
|
+
});
|
|
55081
|
+
}
|
|
55082
|
+
static fromRulesyncSkill({ outputRoot = process.cwd(), rulesyncSkill, validate = true, global = false }) {
|
|
55083
|
+
const settablePaths = ContinueSkill.getSettablePaths({ global });
|
|
55084
|
+
const rulesyncFrontmatter = rulesyncSkill.getFrontmatter();
|
|
55085
|
+
const { name: _sectionName, description: _sectionDescription, ...continueSection } = rulesyncFrontmatter.continue ?? {};
|
|
55086
|
+
const continueFrontmatter = {
|
|
55087
|
+
...continueSection,
|
|
55088
|
+
name: rulesyncFrontmatter.name,
|
|
55089
|
+
description: rulesyncFrontmatter.description
|
|
55090
|
+
};
|
|
55091
|
+
return new ContinueSkill({
|
|
55092
|
+
outputRoot,
|
|
55093
|
+
relativeDirPath: settablePaths.relativeDirPath,
|
|
55094
|
+
dirName: continueFrontmatter.name,
|
|
55095
|
+
frontmatter: continueFrontmatter,
|
|
55096
|
+
body: rulesyncSkill.getBody(),
|
|
55097
|
+
otherFiles: rulesyncSkill.getOtherFiles(),
|
|
55098
|
+
validate,
|
|
55099
|
+
global
|
|
55100
|
+
});
|
|
55101
|
+
}
|
|
55102
|
+
static isTargetedByRulesyncSkill(rulesyncSkill) {
|
|
55103
|
+
const targets = rulesyncSkill.getFrontmatter().targets;
|
|
55104
|
+
return targets.includes("*") || targets.includes("continue");
|
|
55105
|
+
}
|
|
55106
|
+
static async fromDir(params) {
|
|
55107
|
+
const loaded = await this.loadSkillDirContent({
|
|
55108
|
+
...params,
|
|
55109
|
+
getSettablePaths: ContinueSkill.getSettablePaths
|
|
55110
|
+
});
|
|
55111
|
+
const result = ContinueSkillFrontmatterSchema.safeParse(loaded.frontmatter);
|
|
55112
|
+
if (!result.success) {
|
|
55113
|
+
const skillDirPath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName);
|
|
55114
|
+
throw new Error(`Invalid frontmatter in ${join(skillDirPath, SKILL_FILE_NAME)}: ${formatError(result.error)}`);
|
|
55115
|
+
}
|
|
55116
|
+
if (result.data.name !== loaded.dirName) {
|
|
55117
|
+
const skillFilePath = join(loaded.outputRoot, loaded.relativeDirPath, loaded.dirName, SKILL_FILE_NAME);
|
|
55118
|
+
throw new Error(`Frontmatter name (${result.data.name}) must match directory name (${loaded.dirName}) in ${skillFilePath}`);
|
|
55119
|
+
}
|
|
55120
|
+
return new ContinueSkill({
|
|
55121
|
+
outputRoot: loaded.outputRoot,
|
|
55122
|
+
relativeDirPath: loaded.relativeDirPath,
|
|
55123
|
+
dirName: loaded.dirName,
|
|
55124
|
+
frontmatter: result.data,
|
|
55125
|
+
body: loaded.body,
|
|
55126
|
+
otherFiles: loaded.otherFiles,
|
|
55127
|
+
validate: true,
|
|
55128
|
+
global: loaded.global
|
|
55129
|
+
});
|
|
55130
|
+
}
|
|
55131
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, dirName, global = false }) {
|
|
55132
|
+
return new ContinueSkill({
|
|
55133
|
+
outputRoot,
|
|
55134
|
+
relativeDirPath,
|
|
55135
|
+
dirName,
|
|
55136
|
+
frontmatter: {
|
|
55137
|
+
name: "",
|
|
55138
|
+
description: ""
|
|
55139
|
+
},
|
|
55140
|
+
body: "",
|
|
55141
|
+
otherFiles: [],
|
|
55142
|
+
validate: false,
|
|
55143
|
+
global
|
|
55144
|
+
});
|
|
55145
|
+
}
|
|
55146
|
+
};
|
|
55147
|
+
//#endregion
|
|
54027
55148
|
//#region src/features/skills/copilot-skill.ts
|
|
54028
55149
|
const CopilotSkillFrontmatterSchema = z.looseObject({
|
|
54029
55150
|
name: z.string(),
|
|
@@ -58888,6 +60009,14 @@ const toolSkillFactories = /* @__PURE__ */ new Map([
|
|
|
58888
60009
|
supportsGlobal: true
|
|
58889
60010
|
}
|
|
58890
60011
|
}],
|
|
60012
|
+
["continue", {
|
|
60013
|
+
class: ContinueSkill,
|
|
60014
|
+
meta: {
|
|
60015
|
+
supportsProject: true,
|
|
60016
|
+
supportsSimulated: false,
|
|
60017
|
+
supportsGlobal: true
|
|
60018
|
+
}
|
|
60019
|
+
}],
|
|
58891
60020
|
["copilot", {
|
|
58892
60021
|
class: CopilotSkill,
|
|
58893
60022
|
meta: {
|
|
@@ -67456,6 +68585,263 @@ var CodexcliRule = class CodexcliRule extends ToolRule {
|
|
|
67456
68585
|
}
|
|
67457
68586
|
};
|
|
67458
68587
|
//#endregion
|
|
68588
|
+
//#region src/features/rules/continue-rule.ts
|
|
68589
|
+
/**
|
|
68590
|
+
* Frontmatter schema for Continue rule files (`.continue/rules/*.md`).
|
|
68591
|
+
* `globs` and `regex` are documented as either a single pattern or a list.
|
|
68592
|
+
* @see https://docs.continue.dev/customize/deep-dives/rules
|
|
68593
|
+
*/
|
|
68594
|
+
const ContinueRuleFrontmatterSchema = z.looseObject({
|
|
68595
|
+
name: z.optional(z.string()),
|
|
68596
|
+
description: z.optional(z.string()),
|
|
68597
|
+
globs: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
68598
|
+
regex: z.optional(z.union([z.string(), z.array(z.string())])),
|
|
68599
|
+
alwaysApply: z.optional(z.boolean())
|
|
68600
|
+
});
|
|
68601
|
+
/**
|
|
68602
|
+
* Normalizes the documented `string` / `string[]` shapes to the list form the
|
|
68603
|
+
* adapter works with. Continue treats a scalar as one pattern (no comma
|
|
68604
|
+
* splitting), so the scalar simply becomes a one-element list. An empty list
|
|
68605
|
+
* and an empty string both mean "not set".
|
|
68606
|
+
*/
|
|
68607
|
+
function normalizePatternList(value) {
|
|
68608
|
+
if (value === void 0) return;
|
|
68609
|
+
const list = (typeof value === "string" ? [value] : value).filter((pattern) => pattern.trim() !== "");
|
|
68610
|
+
return list.length > 0 ? list : void 0;
|
|
68611
|
+
}
|
|
68612
|
+
/**
|
|
68613
|
+
* A universal glob is redundant on a Continue rule: a `.continue/rules/` file
|
|
68614
|
+
* with no `globs` is already implicitly always-on, and one with `globs` is
|
|
68615
|
+
* only pulled in when a context file matches, so emitting `**\/*` would turn
|
|
68616
|
+
* an always-on rule into a "when any file is in context" rule. Dropped on
|
|
68617
|
+
* generate and mapped back on import.
|
|
68618
|
+
*/
|
|
68619
|
+
const UNIVERSAL_GLOBS$1 = /* @__PURE__ */ new Set(["**/*", "*"]);
|
|
68620
|
+
/**
|
|
68621
|
+
* Rule generator for Continue (continuedev/continue), the open-source IDE
|
|
68622
|
+
* extension and CLI (`cn`).
|
|
68623
|
+
*
|
|
68624
|
+
* Rules format:
|
|
68625
|
+
* - {project}/AGENTS.md (root: true) — read from the workspace root and
|
|
68626
|
+
* always applied.
|
|
68627
|
+
* - {project}/.continue/rules/*.md (root: false) — Markdown with optional
|
|
68628
|
+
* `description` / `globs` / `regex` / `alwaysApply` frontmatter. A file
|
|
68629
|
+
* with no `globs`/`regex` is always applied; one with `globs` is applied
|
|
68630
|
+
* when a context file matches; `alwaysApply: true` forces it on and
|
|
68631
|
+
* `alwaysApply: false` makes it depend on `globs`/`regex` only.
|
|
68632
|
+
* - Global: ~/.continue/rules/*.md. Continue does not read `~/AGENTS.md`, so
|
|
68633
|
+
* the global root rule is written to `~/.continue/rules/AGENTS.md`, the
|
|
68634
|
+
* same layout the Roo adapter uses.
|
|
68635
|
+
*
|
|
68636
|
+
* @see https://docs.continue.dev/customize/deep-dives/rules
|
|
68637
|
+
*/
|
|
68638
|
+
var ContinueRule = class ContinueRule extends ToolRule {
|
|
68639
|
+
frontmatter;
|
|
68640
|
+
body;
|
|
68641
|
+
static getSettablePaths({ global, excludeToolDir } = {}) {
|
|
68642
|
+
const rulesDirPath = buildToolPath(CONTINUE_DIR, "rules", excludeToolDir);
|
|
68643
|
+
if (global) return {
|
|
68644
|
+
root: {
|
|
68645
|
+
relativeDirPath: rulesDirPath,
|
|
68646
|
+
relativeFilePath: CONTINUE_ROOT_RULE_FILE_NAME
|
|
68647
|
+
},
|
|
68648
|
+
nonRoot: { relativeDirPath: rulesDirPath }
|
|
68649
|
+
};
|
|
68650
|
+
return {
|
|
68651
|
+
root: {
|
|
68652
|
+
relativeDirPath: ".",
|
|
68653
|
+
relativeFilePath: CONTINUE_ROOT_RULE_FILE_NAME
|
|
68654
|
+
},
|
|
68655
|
+
nonRoot: { relativeDirPath: rulesDirPath }
|
|
68656
|
+
};
|
|
68657
|
+
}
|
|
68658
|
+
constructor({ frontmatter, body, ...rest }) {
|
|
68659
|
+
if (rest.validate) {
|
|
68660
|
+
const result = ContinueRuleFrontmatterSchema.safeParse(frontmatter);
|
|
68661
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
|
|
68662
|
+
}
|
|
68663
|
+
super({
|
|
68664
|
+
...rest,
|
|
68665
|
+
fileContent: rest.root ? body : ContinueRule.generateFileContent(body, frontmatter)
|
|
68666
|
+
});
|
|
68667
|
+
this.frontmatter = frontmatter;
|
|
68668
|
+
this.body = body;
|
|
68669
|
+
}
|
|
68670
|
+
static generateFileContent(body, frontmatter) {
|
|
68671
|
+
if (frontmatter.name === void 0 && frontmatter.description === void 0 && frontmatter.globs === void 0 && frontmatter.regex === void 0 && frontmatter.alwaysApply === void 0) return body;
|
|
68672
|
+
return stringifyFrontmatter(body, {
|
|
68673
|
+
name: frontmatter.name,
|
|
68674
|
+
description: frontmatter.description,
|
|
68675
|
+
globs: frontmatter.globs,
|
|
68676
|
+
regex: frontmatter.regex,
|
|
68677
|
+
alwaysApply: frontmatter.alwaysApply
|
|
68678
|
+
});
|
|
68679
|
+
}
|
|
68680
|
+
static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false, relativeDirPath: overrideDirPath }) {
|
|
68681
|
+
const paths = this.getSettablePaths({ global });
|
|
68682
|
+
const rootDirPath = overrideDirPath ?? paths.root.relativeDirPath;
|
|
68683
|
+
if (relativeFilePath === paths.root.relativeFilePath && rootDirPath === paths.root.relativeDirPath) {
|
|
68684
|
+
const fileContent = await readFileContent(join(outputRoot, rootDirPath, paths.root.relativeFilePath));
|
|
68685
|
+
return new ContinueRule({
|
|
68686
|
+
outputRoot,
|
|
68687
|
+
relativeDirPath: rootDirPath,
|
|
68688
|
+
relativeFilePath: paths.root.relativeFilePath,
|
|
68689
|
+
frontmatter: {},
|
|
68690
|
+
body: fileContent.trim(),
|
|
68691
|
+
validate,
|
|
68692
|
+
root: true
|
|
68693
|
+
});
|
|
68694
|
+
}
|
|
68695
|
+
const relativePath = join(paths.nonRoot.relativeDirPath, relativeFilePath);
|
|
68696
|
+
const filePath = join(outputRoot, relativePath);
|
|
68697
|
+
const { frontmatter, body: content } = parseFrontmatter(await readFileContent(filePath), filePath);
|
|
68698
|
+
const result = ContinueRuleFrontmatterSchema.safeParse(frontmatter);
|
|
68699
|
+
if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
|
|
68700
|
+
return new ContinueRule({
|
|
68701
|
+
outputRoot,
|
|
68702
|
+
relativeDirPath: paths.nonRoot.relativeDirPath,
|
|
68703
|
+
relativeFilePath,
|
|
68704
|
+
frontmatter: result.data,
|
|
68705
|
+
body: content.trim(),
|
|
68706
|
+
validate,
|
|
68707
|
+
root: false
|
|
68708
|
+
});
|
|
68709
|
+
}
|
|
68710
|
+
static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
|
|
68711
|
+
const paths = this.getSettablePaths({ global });
|
|
68712
|
+
const isRoot = relativeFilePath === paths.root.relativeFilePath && relativeDirPath === paths.root.relativeDirPath;
|
|
68713
|
+
return new ContinueRule({
|
|
68714
|
+
outputRoot,
|
|
68715
|
+
relativeDirPath,
|
|
68716
|
+
relativeFilePath,
|
|
68717
|
+
frontmatter: {},
|
|
68718
|
+
body: "",
|
|
68719
|
+
validate: false,
|
|
68720
|
+
root: isRoot
|
|
68721
|
+
});
|
|
68722
|
+
}
|
|
68723
|
+
/**
|
|
68724
|
+
* Resolves the `globs` to emit. A universal glob is dropped because a rule
|
|
68725
|
+
* without `globs` is already always-on in Continue (see `UNIVERSAL_GLOBS`),
|
|
68726
|
+
* and every glob is dropped alongside `alwaysApply: true`, which forces the
|
|
68727
|
+
* rule on regardless of them.
|
|
68728
|
+
*/
|
|
68729
|
+
static resolveContinueGlobs({ continueGlobs, parentGlobs, alwaysApply }) {
|
|
68730
|
+
if (alwaysApply === true) return;
|
|
68731
|
+
const targetGlobs = continueGlobs ?? parentGlobs;
|
|
68732
|
+
if (!targetGlobs || targetGlobs.length === 0) return;
|
|
68733
|
+
if (targetGlobs.every((glob) => UNIVERSAL_GLOBS$1.has(glob.trim()))) return;
|
|
68734
|
+
return targetGlobs;
|
|
68735
|
+
}
|
|
68736
|
+
static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true, global = false }) {
|
|
68737
|
+
const rulesyncFrontmatter = rulesyncRule.getFrontmatter();
|
|
68738
|
+
const root = rulesyncFrontmatter.root ?? false;
|
|
68739
|
+
const paths = this.getSettablePaths({ global });
|
|
68740
|
+
const body = rulesyncRule.getBody();
|
|
68741
|
+
if (root) return new ContinueRule({
|
|
68742
|
+
outputRoot,
|
|
68743
|
+
frontmatter: {},
|
|
68744
|
+
body,
|
|
68745
|
+
relativeDirPath: paths.root.relativeDirPath,
|
|
68746
|
+
relativeFilePath: paths.root.relativeFilePath,
|
|
68747
|
+
validate,
|
|
68748
|
+
root
|
|
68749
|
+
});
|
|
68750
|
+
const continueFrontmatter = rulesyncFrontmatter.continue;
|
|
68751
|
+
const continueRuleFrontmatter = {
|
|
68752
|
+
name: continueFrontmatter?.name,
|
|
68753
|
+
description: continueFrontmatter?.description ?? rulesyncFrontmatter.description,
|
|
68754
|
+
globs: ContinueRule.resolveContinueGlobs({
|
|
68755
|
+
continueGlobs: normalizePatternList(continueFrontmatter?.globs),
|
|
68756
|
+
parentGlobs: rulesyncFrontmatter.globs,
|
|
68757
|
+
alwaysApply: continueFrontmatter?.alwaysApply
|
|
68758
|
+
}),
|
|
68759
|
+
regex: normalizePatternList(continueFrontmatter?.regex),
|
|
68760
|
+
alwaysApply: continueFrontmatter?.alwaysApply
|
|
68761
|
+
};
|
|
68762
|
+
return new ContinueRule({
|
|
68763
|
+
outputRoot,
|
|
68764
|
+
frontmatter: continueRuleFrontmatter,
|
|
68765
|
+
body,
|
|
68766
|
+
relativeDirPath: paths.nonRoot.relativeDirPath,
|
|
68767
|
+
relativeFilePath: rulesyncRule.getRelativeFilePath(),
|
|
68768
|
+
validate,
|
|
68769
|
+
root
|
|
68770
|
+
});
|
|
68771
|
+
}
|
|
68772
|
+
toRulesyncRule() {
|
|
68773
|
+
const targets = ["*"];
|
|
68774
|
+
if (this.isRoot()) {
|
|
68775
|
+
const rulesyncFrontmatter = {
|
|
68776
|
+
targets,
|
|
68777
|
+
root: true,
|
|
68778
|
+
description: this.description,
|
|
68779
|
+
globs: ["**/*"]
|
|
68780
|
+
};
|
|
68781
|
+
return new RulesyncRule({
|
|
68782
|
+
outputRoot: this.getOutputRoot(),
|
|
68783
|
+
frontmatter: rulesyncFrontmatter,
|
|
68784
|
+
body: this.body,
|
|
68785
|
+
relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
|
|
68786
|
+
relativeFilePath: RULESYNC_OVERVIEW_FILE_NAME,
|
|
68787
|
+
validate: true
|
|
68788
|
+
});
|
|
68789
|
+
}
|
|
68790
|
+
const sourceGlobs = normalizePatternList(this.frontmatter.globs) ?? [];
|
|
68791
|
+
const regex = normalizePatternList(this.frontmatter.regex);
|
|
68792
|
+
const isAlways = this.frontmatter.alwaysApply !== false;
|
|
68793
|
+
const globs = sourceGlobs.length === 0 && isAlways ? ["**/*"] : sourceGlobs;
|
|
68794
|
+
const { name, alwaysApply } = this.frontmatter;
|
|
68795
|
+
const hasContinueSection = name !== void 0 || alwaysApply !== void 0 || regex !== void 0;
|
|
68796
|
+
const rulesyncFrontmatter = {
|
|
68797
|
+
targets,
|
|
68798
|
+
root: false,
|
|
68799
|
+
description: this.frontmatter.description,
|
|
68800
|
+
globs,
|
|
68801
|
+
...hasContinueSection && { continue: {
|
|
68802
|
+
...name !== void 0 && { name },
|
|
68803
|
+
alwaysApply,
|
|
68804
|
+
regex
|
|
68805
|
+
} }
|
|
68806
|
+
};
|
|
68807
|
+
return new RulesyncRule({
|
|
68808
|
+
outputRoot: this.getOutputRoot(),
|
|
68809
|
+
frontmatter: rulesyncFrontmatter,
|
|
68810
|
+
body: this.body,
|
|
68811
|
+
relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
|
|
68812
|
+
relativeFilePath: this.getRelativeFilePath(),
|
|
68813
|
+
validate: true
|
|
68814
|
+
});
|
|
68815
|
+
}
|
|
68816
|
+
validate() {
|
|
68817
|
+
if (!this.frontmatter) return {
|
|
68818
|
+
success: true,
|
|
68819
|
+
error: null
|
|
68820
|
+
};
|
|
68821
|
+
const result = ContinueRuleFrontmatterSchema.safeParse(this.frontmatter);
|
|
68822
|
+
if (result.success) return {
|
|
68823
|
+
success: true,
|
|
68824
|
+
error: null
|
|
68825
|
+
};
|
|
68826
|
+
return {
|
|
68827
|
+
success: false,
|
|
68828
|
+
error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${join(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
|
|
68829
|
+
};
|
|
68830
|
+
}
|
|
68831
|
+
getFrontmatter() {
|
|
68832
|
+
return this.frontmatter;
|
|
68833
|
+
}
|
|
68834
|
+
getBody() {
|
|
68835
|
+
return this.body;
|
|
68836
|
+
}
|
|
68837
|
+
static isTargetedByRulesyncRule(rulesyncRule) {
|
|
68838
|
+
return this.isTargetedByRulesyncRuleDefault({
|
|
68839
|
+
rulesyncRule,
|
|
68840
|
+
toolTarget: "continue"
|
|
68841
|
+
});
|
|
68842
|
+
}
|
|
68843
|
+
};
|
|
68844
|
+
//#endregion
|
|
67459
68845
|
//#region src/features/rules/copilot-rule.ts
|
|
67460
68846
|
const CopilotRuleFrontmatterSchema = z.looseObject({
|
|
67461
68847
|
description: z.optional(z.string()),
|
|
@@ -71537,6 +72923,14 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
|
|
|
71537
72923
|
collisionPolicy: "fold"
|
|
71538
72924
|
}
|
|
71539
72925
|
}],
|
|
72926
|
+
["continue", {
|
|
72927
|
+
class: ContinueRule,
|
|
72928
|
+
meta: {
|
|
72929
|
+
extension: "md",
|
|
72930
|
+
supportsGlobal: true,
|
|
72931
|
+
ruleDiscoveryMode: "auto"
|
|
72932
|
+
}
|
|
72933
|
+
}],
|
|
71540
72934
|
["copilot", {
|
|
71541
72935
|
class: CopilotRule,
|
|
71542
72936
|
meta: {
|
|
@@ -75025,4 +76419,4 @@ async function importChecksCore(params) {
|
|
|
75025
76419
|
//#endregion
|
|
75026
76420
|
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, CRUSH_LOCAL_RULE_FILE_NAME 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, HooksProcessor as y, RULESYNC_MCP_FILE_NAME as yn, withFallbackLoggerTarget as yt, RulesyncSubagentFrontmatterSchema as z, formatError as zn, listFilePathsRecursively as zt };
|
|
75027
76421
|
|
|
75028
|
-
//# sourceMappingURL=import-
|
|
76422
|
+
//# sourceMappingURL=import-D5n_V39C.js.map
|