rulesync 34.0.0 → 35.0.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.
@@ -457,36 +457,36 @@ const isFeatureValueEnabled = (value) => {
457
457
  const parseCommaSeparatedList = (value) => value.split(",").map((s) => s.trim()).filter(Boolean);
458
458
  //#endregion
459
459
  //#region src/constants/rulesync-paths.ts
460
- const { join: join$418 } = node_path.posix;
460
+ const { join: join$420 } = node_path.posix;
461
461
  const RULESYNC_CONFIG_RELATIVE_FILE_PATH = "rulesync.jsonc";
462
462
  const RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH = "rulesync.local.jsonc";
463
463
  const RULESYNC_USER_CONFIG_DIR_NAME = "rulesync";
464
464
  const RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME = ".config";
465
465
  const RULESYNC_RELATIVE_DIR_PATH = ".rulesync";
466
466
  const RULES_FEATURE_SUBDIR = "rules";
467
- const CURATED_RULES_FEATURE_SUBDIR = join$418(RULES_FEATURE_SUBDIR, ".curated");
467
+ const CURATED_RULES_FEATURE_SUBDIR = join$420(RULES_FEATURE_SUBDIR, ".curated");
468
468
  const COMMANDS_FEATURE_SUBDIR = "commands";
469
469
  const SUBAGENTS_FEATURE_SUBDIR = "subagents";
470
470
  const CHECKS_FEATURE_SUBDIR = "checks";
471
471
  const SKILLS_FEATURE_SUBDIR = "skills";
472
- const CURATED_SKILLS_FEATURE_SUBDIR = join$418(SKILLS_FEATURE_SUBDIR, ".curated");
473
- const RULESYNC_RULES_RELATIVE_DIR_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, RULES_FEATURE_SUBDIR);
474
- const RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, CURATED_RULES_FEATURE_SUBDIR);
475
- const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, COMMANDS_FEATURE_SUBDIR);
476
- const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, SUBAGENTS_FEATURE_SUBDIR);
477
- const RULESYNC_CHECKS_RELATIVE_DIR_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, CHECKS_FEATURE_SUBDIR);
478
- const RULESYNC_MCP_RELATIVE_FILE_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, "mcp.jsonc");
479
- const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, "hooks.jsonc");
480
- const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, "permissions.jsonc");
481
- join$418(RULESYNC_RELATIVE_DIR_PATH, "mcp.json");
482
- const RULESYNC_HOOKS_LEGACY_RELATIVE_FILE_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, "hooks.json");
483
- const RULESYNC_PERMISSIONS_LEGACY_RELATIVE_FILE_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, "permissions.json");
472
+ const CURATED_SKILLS_FEATURE_SUBDIR = join$420(SKILLS_FEATURE_SUBDIR, ".curated");
473
+ const RULESYNC_RULES_RELATIVE_DIR_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, RULES_FEATURE_SUBDIR);
474
+ const RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, CURATED_RULES_FEATURE_SUBDIR);
475
+ const RULESYNC_COMMANDS_RELATIVE_DIR_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, COMMANDS_FEATURE_SUBDIR);
476
+ const RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, SUBAGENTS_FEATURE_SUBDIR);
477
+ const RULESYNC_CHECKS_RELATIVE_DIR_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, CHECKS_FEATURE_SUBDIR);
478
+ const RULESYNC_MCP_RELATIVE_FILE_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, "mcp.jsonc");
479
+ const RULESYNC_HOOKS_RELATIVE_FILE_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, "hooks.jsonc");
480
+ const RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, "permissions.jsonc");
481
+ join$420(RULESYNC_RELATIVE_DIR_PATH, "mcp.json");
482
+ const RULESYNC_HOOKS_LEGACY_RELATIVE_FILE_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, "hooks.json");
483
+ const RULESYNC_PERMISSIONS_LEGACY_RELATIVE_FILE_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, "permissions.json");
484
484
  const RULESYNC_AIIGNORE_FILE_NAME = ".aiignore";
485
- const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, ".aiignore");
485
+ const RULESYNC_AIIGNORE_RELATIVE_FILE_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, ".aiignore");
486
486
  const RULESYNC_IGNORE_RELATIVE_FILE_PATH = ".rulesyncignore";
487
487
  const RULESYNC_OVERVIEW_FILE_NAME = "overview.md";
488
- const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, SKILLS_FEATURE_SUBDIR);
489
- const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$418(RULESYNC_RELATIVE_DIR_PATH, CURATED_SKILLS_FEATURE_SUBDIR);
488
+ const RULESYNC_SKILLS_RELATIVE_DIR_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, SKILLS_FEATURE_SUBDIR);
489
+ const RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH = join$420(RULESYNC_RELATIVE_DIR_PATH, CURATED_SKILLS_FEATURE_SUBDIR);
490
490
  const RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync.lock";
491
491
  const RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH = "rulesync-npm.lock.json";
492
492
  const RULESYNC_MCP_FILE_NAME = "mcp.jsonc";
@@ -605,6 +605,7 @@ const ignoreProcessorToolTargetTuple = [
605
605
  "reasonix",
606
606
  "roo",
607
607
  "tabnine",
608
+ "trae",
608
609
  "devin",
609
610
  "vibe",
610
611
  "warp",
@@ -714,6 +715,7 @@ const commandsProcessorToolTargetTuple = [
714
715
  "rovodev",
715
716
  "tabnine",
716
717
  "takt",
718
+ "trae",
717
719
  "devin",
718
720
  "warp",
719
721
  "zcode",
@@ -1405,20 +1407,22 @@ async function fileExists(filepath) {
1405
1407
  return false;
1406
1408
  }
1407
1409
  }
1408
- async function getFileSize(filepath) {
1410
+ /**
1411
+ * The size of a file and whether it carries any execute bit, read with a single
1412
+ * `stat`. Windows reports no execute bits, so a file there is never seen as
1413
+ * executable.
1414
+ */
1415
+ async function getFileSizeAndExecutableBit(filepath) {
1409
1416
  try {
1410
- return (await (0, node_fs_promises.stat)(filepath)).size;
1417
+ const stats = await (0, node_fs_promises.stat)(filepath);
1418
+ return {
1419
+ size: stats.size,
1420
+ executable: (stats.mode & 73) !== 0
1421
+ };
1411
1422
  } catch (error) {
1412
- throw new Error(`Failed to get file size for "${filepath}": ${formatError(error)}`, { cause: error });
1423
+ throw new Error(`Failed to stat "${filepath}": ${formatError(error)}`, { cause: error });
1413
1424
  }
1414
1425
  }
1415
- /**
1416
- * Whether a file carries any execute bit. Windows reports none, so a file
1417
- * there is never seen as executable.
1418
- */
1419
- async function hasExecutableBit(filepath) {
1420
- return ((await (0, node_fs_promises.stat)(filepath)).mode & 73) !== 0;
1421
- }
1422
1426
  async function isSymlink(filepath) {
1423
1427
  try {
1424
1428
  return (await (0, node_fs_promises.lstat)(filepath)).isSymbolicLink();
@@ -5289,7 +5293,7 @@ const JUNIE_HOOK_EVENTS = [
5289
5293
  * `PreToolUse`, and on the end reason for `SessionEnd`. Only `type: "command"`
5290
5294
  * hooks are supported.
5291
5295
  * @see https://docs.gitlab.com/user/gitlab_duo_cli/customize/
5292
- * @see https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/blob/main/packages/core/hooks/src/schema.ts
5296
+ * @see https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/blob/v9.28.0/packages/core/hooks/src/schema.ts
5293
5297
  */
5294
5298
  const GITLABDUO_HOOK_EVENTS = [
5295
5299
  "sessionStart",
@@ -5643,6 +5647,7 @@ const CORTEXCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CA
5643
5647
  *
5644
5648
  * @see https://www.codebuddy.ai/docs/cli/hooks
5645
5649
  * @see https://www.codebuddy.ai/docs/cli/plugins-reference
5650
+ * @see https://www.npmjs.com/package/@tencent-ai/codebuddy-code/v/2.167.0
5646
5651
  */
5647
5652
  const CODEBUDDY_HOOK_EVENTS = [
5648
5653
  "sessionStart",
@@ -23618,6 +23623,157 @@ var TaktCommand = class TaktCommand extends ToolCommand {
23618
23623
  }
23619
23624
  };
23620
23625
  //#endregion
23626
+ //#region src/constants/trae-paths.ts
23627
+ /**
23628
+ * Trae (ByteDance's AI IDE, "TraeCode" in its current docs) keeps project
23629
+ * configuration under `.trae/` and user configuration under `~/.trae/`.
23630
+ *
23631
+ * @see https://docs.trae.ai/ide/rules?_lang=en
23632
+ * @see https://docs.trae.ai/ide/skills?_lang=en
23633
+ * @see https://docs.trae.ai/ide/add-mcp-servers?_lang=en
23634
+ * @see https://docs.trae.ai/ide/slash-commands?_lang=en
23635
+ * @see https://docs.trae.ai/ide/ignore-files?_lang=en
23636
+ */
23637
+ const TRAE_DIR = ".trae";
23638
+ /** Project MCP servers (`mcpServers` object), project scope only. */
23639
+ const TRAE_MCP_FILE_NAME = "mcp.json";
23640
+ /** Skills: `.trae/skills/<name>/SKILL.md` (project) and `~/.trae/skills/` (global). */
23641
+ const TRAE_SKILLS_DIR_PATH = (0, node_path.join)(TRAE_DIR, "skills");
23642
+ /**
23643
+ * Custom commands: Markdown files under `.trae/commands/` (project, up to three nested
23644
+ * directory levels) and `~/.trae/commands/` (global).
23645
+ */
23646
+ const TRAE_COMMANDS_DIR_PATH = (0, node_path.join)(TRAE_DIR, "commands");
23647
+ /** Indexing ignore list (gitignore syntax), `.trae/.ignore`, project scope only. */
23648
+ const TRAE_IGNORE_FILE_NAME = ".ignore";
23649
+ //#endregion
23650
+ //#region src/features/commands/trae-command.ts
23651
+ const TraeCommandFrontmatterSchema = zod_mini.z.looseObject({
23652
+ description: zod_mini.z.optional(zod_mini.z.string()),
23653
+ name: zod_mini.z.optional(zod_mini.z.string())
23654
+ });
23655
+ /**
23656
+ * Custom command for Trae.
23657
+ *
23658
+ * Trae reads Markdown command files under `.trae/commands/` (project, up to
23659
+ * three levels of nested directories) and `~/.trae/commands/` (global). The
23660
+ * YAML frontmatter carries `name` and `description`, and the body below the
23661
+ * closing `---` holds the instructions. rulesync writes the rulesync
23662
+ * `description`, a `name` equal to the file name stem (as Trae's editor does),
23663
+ * plus any fields of the `trae` section (which win over the derived ones), and
23664
+ * keeps nested command paths.
23665
+ *
23666
+ * @see https://docs.trae.ai/ide/slash-commands?_lang=en
23667
+ */
23668
+ var TraeCommand = class TraeCommand extends ToolCommand {
23669
+ frontmatter;
23670
+ body;
23671
+ constructor({ frontmatter, body, ...rest }) {
23672
+ if (rest.validate) {
23673
+ const result = TraeCommandFrontmatterSchema.safeParse(frontmatter);
23674
+ if (!result.success) throw new Error(`Invalid frontmatter in ${(0, node_path.join)(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
23675
+ }
23676
+ super({
23677
+ ...rest,
23678
+ fileContent: stringifyFrontmatter(body, frontmatter)
23679
+ });
23680
+ this.frontmatter = frontmatter;
23681
+ this.body = body;
23682
+ }
23683
+ static getSettablePaths(_options = {}) {
23684
+ return { relativeDirPath: TRAE_COMMANDS_DIR_PATH };
23685
+ }
23686
+ getBody() {
23687
+ return this.body;
23688
+ }
23689
+ getFrontmatter() {
23690
+ return this.frontmatter;
23691
+ }
23692
+ toRulesyncCommand() {
23693
+ const { description, name, ...otherFields } = this.frontmatter;
23694
+ const restFields = name === void 0 || name === (0, node_path.basename)(this.relativeFilePath, ".md") ? otherFields : {
23695
+ name,
23696
+ ...otherFields
23697
+ };
23698
+ const rulesyncFrontmatter = {
23699
+ targets: ["*"],
23700
+ description,
23701
+ ...Object.keys(restFields).length > 0 && { trae: restFields }
23702
+ };
23703
+ return new RulesyncCommand({
23704
+ outputRoot: ".",
23705
+ frontmatter: rulesyncFrontmatter,
23706
+ body: this.body,
23707
+ relativeDirPath: RulesyncCommand.getSettablePaths().relativeDirPath,
23708
+ relativeFilePath: this.relativeFilePath,
23709
+ fileContent: stringifyFrontmatter(this.body, rulesyncFrontmatter),
23710
+ validate: true
23711
+ });
23712
+ }
23713
+ static fromRulesyncCommand({ outputRoot = process.cwd(), rulesyncCommand, validate = true, global = false }) {
23714
+ const rulesyncFrontmatter = rulesyncCommand.getFrontmatter();
23715
+ const traeFrontmatter = {
23716
+ name: (0, node_path.basename)(rulesyncCommand.getRelativeFilePath(), ".md"),
23717
+ description: rulesyncFrontmatter.description,
23718
+ ...rulesyncFrontmatter.trae
23719
+ };
23720
+ return new TraeCommand({
23721
+ outputRoot,
23722
+ frontmatter: traeFrontmatter,
23723
+ body: rulesyncCommand.getBody(),
23724
+ relativeDirPath: this.getSettablePaths({ global }).relativeDirPath,
23725
+ relativeFilePath: rulesyncCommand.getRelativeFilePath(),
23726
+ validate
23727
+ });
23728
+ }
23729
+ validate() {
23730
+ if (!this.frontmatter) return {
23731
+ success: true,
23732
+ error: null
23733
+ };
23734
+ const result = TraeCommandFrontmatterSchema.safeParse(this.frontmatter);
23735
+ if (result.success) return {
23736
+ success: true,
23737
+ error: null
23738
+ };
23739
+ return {
23740
+ success: false,
23741
+ error: /* @__PURE__ */ new Error(`Invalid frontmatter in ${(0, node_path.join)(this.relativeDirPath, this.relativeFilePath)}: ${formatError(result.error)}`)
23742
+ };
23743
+ }
23744
+ static isTargetedByRulesyncCommand(rulesyncCommand) {
23745
+ return this.isTargetedByRulesyncCommandDefault({
23746
+ rulesyncCommand,
23747
+ toolTarget: "trae"
23748
+ });
23749
+ }
23750
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true, global = false }) {
23751
+ const paths = this.getSettablePaths({ global });
23752
+ const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, relativeFilePath);
23753
+ const { frontmatter, body } = parseFrontmatter(await readFileContent(filePath), filePath);
23754
+ const result = TraeCommandFrontmatterSchema.safeParse(frontmatter);
23755
+ if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
23756
+ return new TraeCommand({
23757
+ outputRoot,
23758
+ relativeDirPath: paths.relativeDirPath,
23759
+ relativeFilePath,
23760
+ frontmatter: result.data,
23761
+ body: body.trim(),
23762
+ validate
23763
+ });
23764
+ }
23765
+ static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
23766
+ return new TraeCommand({
23767
+ outputRoot,
23768
+ relativeDirPath,
23769
+ relativeFilePath,
23770
+ frontmatter: { description: "" },
23771
+ body: "",
23772
+ validate: false
23773
+ });
23774
+ }
23775
+ };
23776
+ //#endregion
23621
23777
  //#region src/constants/warp-paths.ts
23622
23778
  const WARP_DIR = ".warp";
23623
23779
  const WARP_SKILLS_DIR_PATH = (0, node_path.join)(WARP_DIR, "skills");
@@ -24328,6 +24484,16 @@ const toolCommandFactories = /* @__PURE__ */ new Map([
24328
24484
  supportsSubdirectory: false
24329
24485
  }
24330
24486
  }],
24487
+ ["trae", {
24488
+ class: TraeCommand,
24489
+ meta: {
24490
+ extension: "md",
24491
+ supportsProject: true,
24492
+ supportsGlobal: true,
24493
+ isSimulated: false,
24494
+ supportsSubdirectory: true
24495
+ }
24496
+ }],
24331
24497
  ["devin", {
24332
24498
  class: DevinCommand,
24333
24499
  meta: {
@@ -38053,6 +38219,61 @@ var TabnineIgnore = class TabnineIgnore extends ToolIgnore {
38053
38219
  }
38054
38220
  };
38055
38221
  //#endregion
38222
+ //#region src/features/ignore/trae-ignore.ts
38223
+ /**
38224
+ * Trae ignore file implementation.
38225
+ *
38226
+ * Trae reads `.trae/.ignore` (gitignore syntax) as a supplement to
38227
+ * `.gitignore` when it indexes the codebase; ignored files are also left out
38228
+ * of `#Workspace` / `#Folder` context. There is no user-level ignore file, so
38229
+ * only project scope is supported.
38230
+ *
38231
+ * @see https://docs.trae.ai/ide/ignore-files?_lang=en
38232
+ */
38233
+ var TraeIgnore = class TraeIgnore extends ToolIgnore {
38234
+ static getSettablePaths(_params = {}) {
38235
+ return {
38236
+ relativeDirPath: TRAE_DIR,
38237
+ relativeFilePath: TRAE_IGNORE_FILE_NAME
38238
+ };
38239
+ }
38240
+ toRulesyncIgnore() {
38241
+ return this.toRulesyncIgnoreDefault();
38242
+ }
38243
+ static fromRulesyncIgnore({ outputRoot = process.cwd(), rulesyncIgnore, global = false }) {
38244
+ const paths = this.getSettablePaths({ global });
38245
+ return new TraeIgnore({
38246
+ outputRoot,
38247
+ relativeDirPath: paths.relativeDirPath,
38248
+ relativeFilePath: paths.relativeFilePath,
38249
+ fileContent: rulesyncIgnore.getFileContent(),
38250
+ global
38251
+ });
38252
+ }
38253
+ static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
38254
+ const { relativeDirPath, relativeFilePath } = this.getSettablePaths({ global });
38255
+ const fileContent = await readFileContent((0, node_path.join)(outputRoot, relativeDirPath, relativeFilePath));
38256
+ return new TraeIgnore({
38257
+ outputRoot,
38258
+ relativeDirPath,
38259
+ relativeFilePath,
38260
+ fileContent,
38261
+ validate,
38262
+ global
38263
+ });
38264
+ }
38265
+ static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, global = false }) {
38266
+ return new TraeIgnore({
38267
+ outputRoot,
38268
+ relativeDirPath,
38269
+ relativeFilePath,
38270
+ fileContent: "",
38271
+ validate: false,
38272
+ global
38273
+ });
38274
+ }
38275
+ };
38276
+ //#endregion
38056
38277
  //#region src/features/ignore/vibe-ignore.ts
38057
38278
  var VibeIgnore = class VibeIgnore extends ToolIgnore {
38058
38279
  static getSettablePaths() {
@@ -38281,6 +38502,7 @@ const toolIgnoreFactories = /* @__PURE__ */ new Map([
38281
38502
  ["zoocode", { class: RooIgnore }],
38282
38503
  ["devin", { class: DevinIgnore }],
38283
38504
  ["tabnine", { class: TabnineIgnore }],
38505
+ ["trae", { class: TraeIgnore }],
38284
38506
  ["vibe", { class: VibeIgnore }],
38285
38507
  ["warp", { class: WarpIgnore }],
38286
38508
  ["zed", { class: ZedIgnore }]
@@ -43633,12 +43855,14 @@ function toGitlabduoTimeout({ serverName, server, logger }) {
43633
43855
  /**
43634
43856
  * Read GitLab Duo's `timeoutMs` back into the canonical `timeout`
43635
43857
  * (milliseconds). A server that already carries a `timeout` is left as is, so
43636
- * both keys round-trip and `timeoutMs` keeps winning on the next generate.
43858
+ * both keys round-trip and `timeoutMs` keeps winning on the next generate. A
43859
+ * non-number `timeoutMs` is not copied, since the canonical `timeout` must be a
43860
+ * number; it is kept verbatim as a passthrough key instead.
43637
43861
  */
43638
43862
  function fromGitlabduoTimeout(server) {
43639
43863
  if (typeof server !== "object" || server === null || Array.isArray(server)) return server;
43640
43864
  const { timeoutMs, ...rest } = server;
43641
- if (timeoutMs === void 0 || rest.timeout !== void 0) return server;
43865
+ if (typeof timeoutMs !== "number" || rest.timeout !== void 0) return server;
43642
43866
  return {
43643
43867
  ...rest,
43644
43868
  timeout: timeoutMs
@@ -47111,8 +47335,8 @@ function convertFromPiFormat(mcpServers) {
47111
47335
  * existing file (`autoEnableCodemode`, ...) are preserved. Pi expands `${VAR}`
47112
47336
  * in `env` and `headers` values itself, so those are written unchanged.
47113
47337
  *
47114
- * @see https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/mcp.md
47115
- * @see https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/mcp-servers.ts
47338
+ * @see https://github.com/earendil-works/pi/blob/v1.1.0/packages/coding-agent/docs/mcp.md
47339
+ * @see https://github.com/earendil-works/pi/blob/v1.1.0/packages/coding-agent/src/core/mcp-servers.ts
47116
47340
  */
47117
47341
  var PiMcp = class PiMcp extends ToolMcp {
47118
47342
  json;
@@ -49226,21 +49450,6 @@ function deriveTransportAllowlist(servers) {
49226
49450
  return allowlist;
49227
49451
  }
49228
49452
  //#endregion
49229
- //#region src/constants/trae-paths.ts
49230
- /**
49231
- * Trae (ByteDance's AI IDE, "TraeCode" in its current docs) keeps project
49232
- * configuration under `.trae/` and user configuration under `~/.trae/`.
49233
- *
49234
- * @see https://docs.trae.ai/ide/rules?_lang=en
49235
- * @see https://docs.trae.ai/ide/skills?_lang=en
49236
- * @see https://docs.trae.ai/ide/add-mcp-servers?_lang=en
49237
- */
49238
- const TRAE_DIR = ".trae";
49239
- /** Project MCP servers (`mcpServers` object), project scope only. */
49240
- const TRAE_MCP_FILE_NAME = "mcp.json";
49241
- /** Skills: `.trae/skills/<name>/SKILL.md` (project) and `~/.trae/skills/` (global). */
49242
- const TRAE_SKILLS_DIR_PATH = (0, node_path.join)(TRAE_DIR, "skills");
49243
- //#endregion
49244
49453
  //#region src/features/mcp/trae-mcp.ts
49245
49454
  /** A `${...}` reference Trae does not expand: anything but `${workspaceFolder}`. */
49246
49455
  const UNEXPANDED_VARIABLE_REGEX = /\$\{(?!workspaceFolder\})[^}]+\}/;
@@ -51850,6 +52059,11 @@ function parsedGlobsIntersect(left, right, budget) {
51850
52059
  * @see https://antigravity.google/docs/permissions?tab=cli
51851
52060
  */
51852
52061
  const REGEX_PREFIX = "regex:";
52062
+ /**
52063
+ * The Antigravity actions whose target is a command: `command`, and
52064
+ * `unsandboxed`, which takes the same word-by-word prefix or `regex:` target.
52065
+ */
52066
+ const ANTIGRAVITY_COMMAND_ACTIONS = /* @__PURE__ */ new Set(["command", "unsandboxed"]);
51853
52067
  const TRAILING_ANCHOR = /(?<!\\)\$$/;
51854
52068
  const PLAIN_TARGET_GLOB_CHARACTERS = /[*?[{\\]/;
51855
52069
  const REGEX_METACHARACTERS$1 = /[\\^$.|?*+()[\]{}]/;
@@ -52227,8 +52441,8 @@ function toAntigravityCommandLiteralPrefix(pattern) {
52227
52441
  }
52228
52442
  /**
52229
52443
  * Read one regex word back as a glob, or `undefined` when it uses anything a
52230
- * glob cannot say. Antigravity anchors each word anyway, so `^` and `$` are
52231
- * optional. Only escaped characters and `.*` translate; anything else keeps
52444
+ * glob cannot say. Antigravity anchors each word anyway (`^(?:pattern)$`), so
52445
+ * `^` and `$` are optional. Only escaped characters and `.*` translate; anything else keeps
52232
52446
  * the rule a regex.
52233
52447
  */
52234
52448
  function regexWordToGlob(word) {
@@ -52254,8 +52468,10 @@ function regexWordToGlob(word) {
52254
52468
  }
52255
52469
  /** Read a per-word regex back as a glob, or `undefined` when it has no glob spelling. */
52256
52470
  function regexToGlob(source) {
52471
+ const words = splitWords(source);
52472
+ if (words.length === 0) return;
52257
52473
  const globs = [];
52258
- for (const word of splitWords(source)) {
52474
+ for (const word of words) {
52259
52475
  const glob = regexWordToGlob(word);
52260
52476
  if (glob === void 0) return;
52261
52477
  globs.push(glob);
@@ -52280,36 +52496,23 @@ function fromAntigravityCommandTarget(target) {
52280
52496
  return `${target} *`;
52281
52497
  }
52282
52498
  /**
52283
- * Whether a regex word means the same anchored or not: each end is either
52284
- * anchored (`^`, an unescaped `$`) or open-ended (`.*`).
52285
- */
52286
- function isAnchoredWord(word) {
52287
- const startAnchored = word.startsWith("^") || word.startsWith(".*");
52288
- const endAnchored = TRAILING_ANCHOR.test(word) || word.endsWith(".*");
52289
- return startAnchored && endAnchored;
52290
- }
52291
- /**
52292
52499
  * Explain what importing an Antigravity `command` target may lose, or return
52293
52500
  * `undefined` when the import is faithful.
52294
52501
  *
52295
- * - A `regex:` target with a word that is not anchored is imported as a glob
52296
- * that assumes anchoring. If Antigravity matches such a word unanchored,
52297
- * the imported rule is narrower (`regex:rm` also matches `rmdir`).
52298
- * - A plain target holding characters that are special in a glob is most
52299
- * likely the verbatim output of an older rulesync (`command(rm -rf *)`).
52300
- * Antigravity matches it literally, so it imports as a regex that matches a
52301
- * literal `*` word and is inert in the other tools it is exported to.
52502
+ * A plain target holding characters that are special in a glob is most
52503
+ * likely the verbatim output of an older rulesync (`command(rm -rf *)`).
52504
+ * Antigravity matches it literally, so it imports as a regex that matches a
52505
+ * literal `*` word and is inert in the other tools it is exported to.
52506
+ *
52507
+ * A `regex:` word without `^...$` (`regex:rm`) is read as anchored, which is
52508
+ * faithful: Antigravity documents that each token is evaluated as the
52509
+ * anchored regex `^(?:pattern)$`.
52510
+ *
52511
+ * @see https://antigravity.google/docs/permissions
52302
52512
  */
52303
52513
  function describeAntigravityCommandImportLoss(target) {
52304
- if (target.startsWith(REGEX_PREFIX)) {
52305
- const source = target.slice(6);
52306
- if (splitWords(source).some((word) => !isAnchoredWord(word)) && regexToGlob(source) !== void 0) return {
52307
- reason: "it has a regex word without `^...$` anchors and is imported as a glob that assumes Antigravity anchors every word, so the imported rule may be narrower than the original",
52308
- denyConsequence: "The imported deny may block less than it does in Antigravity."
52309
- };
52310
- return;
52311
- }
52312
- if (target !== "*" && PLAIN_TARGET_GLOB_CHARACTERS.test(target)) return {
52514
+ if (target.startsWith(REGEX_PREFIX) || target === "*") return;
52515
+ if (PLAIN_TARGET_GLOB_CHARACTERS.test(target)) return {
52313
52516
  reason: "it holds characters that are special in a glob (`*`, `?`, `[`, `{` or `\\`), which Antigravity matches literally (likely output of an older rulesync), so it is imported as a literal-match regex that no other tool enforces",
52314
52517
  denyConsequence: "This deny is NOT enforced by the other tools it is generated for."
52315
52518
  };
@@ -52341,17 +52544,20 @@ function importAntigravityCommandTarget({ target, entry, decision, logger, toolL
52341
52544
  * every file.
52342
52545
  * - `read_url` takes a domain and covers its subdomains. The URL path is
52343
52546
  * ignored, and `*` covers every domain.
52344
- * - `command` takes literal words or a per-word `regex:`, matched word by
52345
- * word (see `antigravity-command-patterns.ts`).
52547
+ * - `execute_url` takes a domain, like `read_url`.
52548
+ * - `command` and `unsandboxed` take literal words or a per-word `regex:`,
52549
+ * matched word by word (see `antigravity-command-patterns.ts`).
52346
52550
  *
52347
52551
  * A glob copied verbatim (`read_file(**\/*.env)`) is a literal path that
52348
52552
  * matches nothing, so a deny written that way blocks nothing. A pattern with
52349
- * no Antigravity spelling is skipped with a warning instead, except a command
52350
- * deny with leading literal words, which is widened to them.
52553
+ * no Antigravity spelling is skipped with a warning instead, except a deny
52554
+ * with a literal lead (the first words of a command, the leading directories
52555
+ * of a path), which is widened to it.
52351
52556
  *
52352
52557
  * @see https://antigravity.google/docs/permissions?tab=cli
52353
52558
  */
52354
52559
  const PATH_ACTIONS = /* @__PURE__ */ new Set(["read_file", "write_file"]);
52560
+ const URL_ACTIONS = /* @__PURE__ */ new Set(["read_url", "execute_url"]);
52355
52561
  const MATCH_ALL_PATH_GLOBS = /* @__PURE__ */ new Set([
52356
52562
  "*",
52357
52563
  "**",
@@ -52363,7 +52569,8 @@ const HOSTNAME = /^[a-z0-9-]+(?:\.[a-z0-9-]+)*$/;
52363
52569
  const DENY_HINTS = {
52364
52570
  read_file: "deny a directory or file path instead",
52365
52571
  write_file: "deny a directory or file path instead",
52366
- command: "deny the first words of the command instead"
52572
+ command: "deny the first words of the command instead",
52573
+ unsandboxed: "deny the first words of the command instead"
52367
52574
  };
52368
52575
  function hasGlob(pattern) {
52369
52576
  return BRACE_ALTERNATIVES.test(pattern) || parseGlobPattern(pattern).steps.some((step) => step.kind !== "literal");
@@ -52414,37 +52621,88 @@ function toUrlTarget(pattern) {
52414
52621
  };
52415
52622
  }
52416
52623
  function toTarget(action, pattern) {
52417
- if (action === "command") return toAntigravityCommandTarget(pattern);
52624
+ if (ANTIGRAVITY_COMMAND_ACTIONS.has(action)) return toAntigravityCommandTarget(pattern);
52418
52625
  if (PATH_ACTIONS.has(action)) return toPathTarget(pattern);
52419
- if (action === "read_url") return toUrlTarget(pattern);
52626
+ if (URL_ACTIONS.has(action)) return toUrlTarget(pattern);
52420
52627
  return { target: pattern };
52421
52628
  }
52422
52629
  /**
52630
+ * The leading directories of a path glob (`src` for `src/*.ts`), or
52631
+ * `undefined` when it has none. A directory target covers everything inside
52632
+ * it, so it matches at least as much as the glob. The filesystem root alone
52633
+ * (`/*`) is not a lead: a deny of it would block every file. Neither is a lead
52634
+ * with `..` in it.
52635
+ */
52636
+ function toPathLiteralPrefix(pattern) {
52637
+ const path = pattern.startsWith("./") ? pattern.slice(2) : pattern;
52638
+ const prefix = [];
52639
+ for (const segment of path.split("/").slice(0, -1)) {
52640
+ if (hasGlob(segment) || segment.includes("{") || segment.includes("}")) break;
52641
+ prefix.push(segment);
52642
+ }
52643
+ if (prefix.includes("..") || prefix.every((segment) => segment === "" || segment === ".")) return;
52644
+ return prefix.join("/");
52645
+ }
52646
+ /** The widened target of a deny that has no exact Antigravity spelling. */
52647
+ function toWidenedDenyTarget(action, pattern) {
52648
+ if (ANTIGRAVITY_COMMAND_ACTIONS.has(action)) return toAntigravityCommandLiteralPrefix(pattern);
52649
+ if (PATH_ACTIONS.has(action)) return toPathLiteralPrefix(pattern);
52650
+ }
52651
+ /**
52652
+ * Warn about a path target that is a home or variable reference. Antigravity
52653
+ * documents absolute and workspace-relative paths only, so such a reference
52654
+ * may be matched literally and never apply. The entry is still written, since
52655
+ * dropping a deny would lose it where Antigravity does expand the reference.
52656
+ */
52657
+ function warnHomeOrVariablePath({ action, target, rule, decision, logger, toolLabel }) {
52658
+ if (!PATH_ACTIONS.has(action) || !HOME_OR_VARIABLE_PATH.test(target)) return;
52659
+ const consequence = decision === "deny" ? "this deny may block nothing" : decision === "ask" ? `${toolLabel} may fall back to its default, so it may run without a prompt when ${toolLabel} is set to always proceed` : `${toolLabel} may fall back to its default`;
52660
+ logger.warn(`${toolLabel} permissions: ${rule}: \`~\` and environment variables may not be expanded in Antigravity path targets, so ${action}(${target}) may match nothing and ${consequence}; use an absolute path instead.`);
52661
+ }
52662
+ /**
52423
52663
  * Build the entry for one canonical rule, or return `undefined` when
52424
- * Antigravity cannot express it. A command deny is widened to its leading
52425
- * literal words rather than skipped: Antigravity applies deny over ask and
52426
- * allow, so an overlapping allow that is translated (`git push *`) cannot let
52427
- * through what the deny (`git push * --force`) was meant to block. A skipped
52428
- * deny is warned about loudly, because nothing enforces it.
52664
+ * Antigravity cannot express it. A deny is widened to its literal lead rather
52665
+ * than skipped (`git push * --force` → `command(git push)`, `src/*.ts` →
52666
+ * `read_file(src)`): Antigravity applies deny over ask and allow, so an
52667
+ * overlapping allow that is translated (`git push *`) cannot let through what
52668
+ * the deny was meant to block. A skipped deny is warned about loudly, because
52669
+ * nothing enforces it, and a widened one is warned about too, because it
52670
+ * blocks more than it says.
52429
52671
  */
52430
52672
  function buildAntigravityPermissionEntry({ action, category, pattern, decision, logger, toolLabel }) {
52431
52673
  const result = pattern === "*" ? { target: "*" } : toTarget(action, pattern);
52432
52674
  const rule = `${category}: { "${pattern}": "${decision}" }`;
52433
52675
  if ("skipReason" in result) {
52434
- const prefix = action === "command" && decision === "deny" ? toAntigravityCommandLiteralPrefix(pattern) : void 0;
52676
+ const prefix = decision === "deny" ? toWidenedDenyTarget(action, pattern) : void 0;
52435
52677
  if (prefix !== void 0) {
52436
- logger.warn(`${toolLabel} permissions: widening ${rule} to ${action}(${prefix}). ${result.skipReason}. The deny now blocks every command that starts with \`${prefix}\`, overriding any allow or ask for them.`);
52678
+ const scope = PATH_ACTIONS.has(action) ? `every path under \`${prefix}\`` : `every command that starts with \`${prefix}\``;
52679
+ logger.warn(`${toolLabel} permissions: widening ${rule} to ${action}(${prefix}). ${result.skipReason}. The deny now blocks ${scope}, overriding any allow or ask for them.`);
52680
+ warnHomeOrVariablePath({
52681
+ action,
52682
+ target: prefix,
52683
+ rule,
52684
+ decision,
52685
+ logger,
52686
+ toolLabel
52687
+ });
52437
52688
  return `${action}(${prefix})`;
52438
52689
  }
52439
52690
  const consequence = decision === "deny" ? `This deny is NOT enforced in ${toolLabel}; ${DENY_HINTS[action] ?? "write it in a form Antigravity can match"}` : decision === "ask" ? `${toolLabel} falls back to its default for it, so it runs without a prompt when ${toolLabel} is set to always proceed` : `${toolLabel} falls back to its default for it`;
52440
52691
  logger.warn(`${toolLabel} permissions: skipping ${rule}. ${result.skipReason}. ${consequence}.`);
52441
52692
  return;
52442
52693
  }
52443
- if (result.note !== void 0 && decision !== "deny") logger.warn(`${toolLabel} permissions: ${rule}: ${result.note}.`);
52444
- if (PATH_ACTIONS.has(action) && HOME_OR_VARIABLE_PATH.test(result.target)) {
52445
- const consequence = decision === "deny" ? "this deny may block nothing" : `${toolLabel} may fall back to its default`;
52446
- logger.warn(`${toolLabel} permissions: ${rule}: \`~\` and environment variables may not be expanded in Antigravity path targets, so ${action}(${result.target}) may match nothing and ${consequence}; use an absolute path instead.`);
52694
+ if (result.note !== void 0 && (decision !== "deny" || URL_ACTIONS.has(action))) {
52695
+ const consequence = decision === "deny" ? ", so the deny also overrides any allow or ask there" : "";
52696
+ logger.warn(`${toolLabel} permissions: ${rule}: ${result.note}${consequence}.`);
52447
52697
  }
52698
+ warnHomeOrVariablePath({
52699
+ action,
52700
+ target: result.target,
52701
+ rule,
52702
+ decision,
52703
+ logger,
52704
+ toolLabel
52705
+ });
52448
52706
  return result.target === "*" ? action : `${action}(${result.target})`;
52449
52707
  }
52450
52708
  //#endregion
@@ -52778,7 +53036,8 @@ function pickDocumentedValue({ key, value, documented }) {
52778
53036
  * Deliberate mapping from rulesync canonical permission categories to the
52779
53037
  * Antigravity CLI Fine-Grained Permissions Engine action vocabulary
52780
53038
  * (`read_file`, `write_file`, `read_url`, `command`, `mcp`, plus `execute_url` /
52781
- * `unsandboxed` which have no canonical equivalent and pass through). `edit` and
53039
+ * `unsandboxed` which have no canonical equivalent and keep their names; their
53040
+ * targets are translated like `read_url` / `command` ones). `edit` and
52782
53041
  * `write` both map to `write_file`, and `webfetch` / `websearch` both map to
52783
53042
  * `read_url`; on import those collapse to the canonical `write` / `webfetch`
52784
53043
  * form (a documented, lossy normalization). This shares the action vocabulary of
@@ -53025,7 +53284,7 @@ function convertAntigravityCliToRulesyncPermissions(params) {
53025
53284
  for (const entry of entries) {
53026
53285
  const { toolName, pattern: target } = parsePermissionEntry$1(entry);
53027
53286
  const canonical = toCanonicalToolName$9(toolName);
53028
- const pattern = toolName === "command" ? importAntigravityCommandTarget({
53287
+ const pattern = ANTIGRAVITY_COMMAND_ACTIONS.has(toolName) ? importAntigravityCommandTarget({
53029
53288
  target,
53030
53289
  entry,
53031
53290
  decision: action,
@@ -53047,7 +53306,8 @@ function convertAntigravityCliToRulesyncPermissions(params) {
53047
53306
  * Deliberate mapping from rulesync canonical permission categories to the
53048
53307
  * Antigravity IDE action vocabulary (`read_file`, `write_file`, `read_url`,
53049
53308
  * `command`, `mcp`, plus `execute_url` / `unsandboxed` which have no canonical
53050
- * equivalent and pass through). `edit` and `write` both map to `write_file`, and
53309
+ * equivalent and keep their names; their targets are translated like
53310
+ * `read_url` / `command` ones). `edit` and `write` both map to `write_file`, and
53051
53311
  * `webfetch` / `websearch` both map to `read_url`; on import those collapse to
53052
53312
  * the canonical `write` / `webfetch` form (a documented, lossy normalization).
53053
53313
  */
@@ -53249,7 +53509,7 @@ function convertAntigravityIdeToRulesyncPermissions(params) {
53249
53509
  for (const entry of entries) {
53250
53510
  const { action: ideAction, pattern: target } = parsePermissionEntry(entry);
53251
53511
  const canonical = toCanonicalCategory$2(ideAction);
53252
- const pattern = ideAction === "command" ? importAntigravityCommandTarget({
53512
+ const pattern = ANTIGRAVITY_COMMAND_ACTIONS.has(ideAction) ? importAntigravityCommandTarget({
53253
53513
  target,
53254
53514
  entry,
53255
53515
  decision: action,
@@ -72526,6 +72786,335 @@ var PermissionsProcessor = class extends FeatureProcessor {
72526
72786
  }
72527
72787
  };
72528
72788
  //#endregion
72789
+ //#region src/types/tool-display.ts
72790
+ const TOOL_DISPLAY = [
72791
+ {
72792
+ key: "agentsmd",
72793
+ label: "AGENTS.md",
72794
+ group: "standard"
72795
+ },
72796
+ {
72797
+ key: "agentsskills",
72798
+ label: "AgentsSkills",
72799
+ group: "standard"
72800
+ },
72801
+ {
72802
+ key: "amp",
72803
+ label: "Amp",
72804
+ group: "ai"
72805
+ },
72806
+ {
72807
+ key: "bob",
72808
+ label: "IBM Bob",
72809
+ group: "ai"
72810
+ },
72811
+ {
72812
+ key: "claudecode",
72813
+ label: "Claude Code",
72814
+ group: "ai"
72815
+ },
72816
+ {
72817
+ key: "claudecode-plugin",
72818
+ label: "Claude Code plugin",
72819
+ group: "ai"
72820
+ },
72821
+ {
72822
+ key: "codebuddy",
72823
+ label: "CodeBuddy Code",
72824
+ group: "ai"
72825
+ },
72826
+ {
72827
+ key: "codewhale",
72828
+ label: "Codewhale",
72829
+ group: "ai"
72830
+ },
72831
+ {
72832
+ key: "codexcli",
72833
+ label: "Codex CLI",
72834
+ group: "ai"
72835
+ },
72836
+ {
72837
+ key: "commandcode",
72838
+ label: "Command Code",
72839
+ group: "ai"
72840
+ },
72841
+ {
72842
+ key: "copilot",
72843
+ label: "GitHub Copilot",
72844
+ group: "ai"
72845
+ },
72846
+ {
72847
+ key: "copilotcli",
72848
+ label: "GitHub Copilot CLI",
72849
+ group: "ai"
72850
+ },
72851
+ {
72852
+ key: "crush",
72853
+ label: "Crush",
72854
+ group: "ai"
72855
+ },
72856
+ {
72857
+ key: "goose",
72858
+ label: "Goose",
72859
+ group: "ai"
72860
+ },
72861
+ {
72862
+ key: "hermesagent",
72863
+ label: "Hermes Agent",
72864
+ group: "ai"
72865
+ },
72866
+ {
72867
+ key: "grokcli",
72868
+ label: "Grok CLI",
72869
+ group: "ai"
72870
+ },
72871
+ {
72872
+ key: "cursor",
72873
+ label: "Cursor",
72874
+ group: "ai"
72875
+ },
72876
+ {
72877
+ key: "deepagents",
72878
+ label: "deepagents-cli",
72879
+ group: "ai"
72880
+ },
72881
+ {
72882
+ key: "factorydroid",
72883
+ label: "Factory Droid",
72884
+ group: "ai"
72885
+ },
72886
+ {
72887
+ key: "opencode",
72888
+ label: "OpenCode",
72889
+ group: "ai"
72890
+ },
72891
+ {
72892
+ key: "openclaw",
72893
+ label: "OpenClaw",
72894
+ group: "ai"
72895
+ },
72896
+ {
72897
+ key: "mimocode",
72898
+ label: "MiMo Code",
72899
+ group: "ai"
72900
+ },
72901
+ {
72902
+ key: "gitlabduo",
72903
+ label: "GitLab Duo CLI",
72904
+ group: "ai"
72905
+ },
72906
+ {
72907
+ key: "cline",
72908
+ label: "Cline",
72909
+ group: "ai"
72910
+ },
72911
+ {
72912
+ key: "kilo",
72913
+ label: "Kilo Code",
72914
+ group: "ai"
72915
+ },
72916
+ {
72917
+ key: "kimi-code",
72918
+ label: "Kimi Code",
72919
+ group: "ai"
72920
+ },
72921
+ {
72922
+ key: "kimi-code-plugin",
72923
+ label: "Kimi Code plugin",
72924
+ group: "ai"
72925
+ },
72926
+ {
72927
+ key: "roo",
72928
+ label: "Roo Code ⚠️",
72929
+ group: "ai"
72930
+ },
72931
+ {
72932
+ key: "zoocode",
72933
+ label: "Zoo Code",
72934
+ group: "ai"
72935
+ },
72936
+ {
72937
+ key: "rovodev",
72938
+ label: "Rovodev (Atlassian) ⚠️",
72939
+ group: "ai"
72940
+ },
72941
+ {
72942
+ key: "takt",
72943
+ label: "Takt",
72944
+ group: "ai"
72945
+ },
72946
+ {
72947
+ key: "trae",
72948
+ label: "Trae",
72949
+ group: "ai"
72950
+ },
72951
+ {
72952
+ key: "tabnine",
72953
+ label: "Tabnine CLI ⚠️",
72954
+ group: "ai"
72955
+ },
72956
+ {
72957
+ key: "continue",
72958
+ label: "Continue ⚠️",
72959
+ group: "ai"
72960
+ },
72961
+ {
72962
+ key: "cortexcode",
72963
+ label: "Snowflake Cortex Code",
72964
+ group: "ai"
72965
+ },
72966
+ {
72967
+ key: "vibe",
72968
+ label: "Vibe Code",
72969
+ group: "ai"
72970
+ },
72971
+ {
72972
+ key: "vibe-plugin",
72973
+ label: "Vibe Code plugin",
72974
+ group: "ai"
72975
+ },
72976
+ {
72977
+ key: "qoder",
72978
+ label: "Qoder",
72979
+ group: "ai"
72980
+ },
72981
+ {
72982
+ key: "qwencode",
72983
+ label: "Qwen Code",
72984
+ group: "ai"
72985
+ },
72986
+ {
72987
+ key: "musecode",
72988
+ label: "Meta Muse Code",
72989
+ group: "ai"
72990
+ },
72991
+ {
72992
+ key: "reasonix",
72993
+ label: "Reasonix",
72994
+ group: "ai"
72995
+ },
72996
+ {
72997
+ key: "codebuff",
72998
+ label: "Codebuff (Freebuff)",
72999
+ group: "ai"
73000
+ },
73001
+ {
73002
+ key: "kiro",
73003
+ label: "Kiro ⚠️",
73004
+ group: "ai"
73005
+ },
73006
+ {
73007
+ key: "kiro-cli",
73008
+ label: "Kiro CLI",
73009
+ group: "ai"
73010
+ },
73011
+ {
73012
+ key: "kiro-ide",
73013
+ label: "Kiro IDE",
73014
+ group: "ai"
73015
+ },
73016
+ {
73017
+ key: "lettacode",
73018
+ label: "Letta Code",
73019
+ group: "ai"
73020
+ },
73021
+ {
73022
+ key: "antigravity-ide",
73023
+ label: "Google Antigravity IDE",
73024
+ group: "ai"
73025
+ },
73026
+ {
73027
+ key: "antigravity-cli",
73028
+ label: "Google Antigravity CLI",
73029
+ group: "ai"
73030
+ },
73031
+ {
73032
+ key: "antigravity-plugin",
73033
+ label: "Google Antigravity plugin",
73034
+ group: "ai"
73035
+ },
73036
+ {
73037
+ key: "aiassistant",
73038
+ label: "JetBrains AI Assistant",
73039
+ group: "ai"
73040
+ },
73041
+ {
73042
+ key: "junie",
73043
+ label: "JetBrains Junie",
73044
+ group: "ai"
73045
+ },
73046
+ {
73047
+ key: "augmentcode",
73048
+ label: "AugmentCode",
73049
+ group: "ai"
73050
+ },
73051
+ {
73052
+ key: "augmentcode-plugin",
73053
+ label: "AugmentCode plugin",
73054
+ group: "ai"
73055
+ },
73056
+ {
73057
+ key: "devin",
73058
+ label: "Devin Desktop",
73059
+ group: "ai"
73060
+ },
73061
+ {
73062
+ key: "devin-plugin",
73063
+ label: "Devin plugin",
73064
+ group: "ai"
73065
+ },
73066
+ {
73067
+ key: "warp",
73068
+ label: "Warp",
73069
+ group: "ai"
73070
+ },
73071
+ {
73072
+ key: "warpcli",
73073
+ label: "Warp Agent CLI",
73074
+ group: "ai"
73075
+ },
73076
+ {
73077
+ key: "replit",
73078
+ label: "Replit",
73079
+ group: "ai"
73080
+ },
73081
+ {
73082
+ key: "pi",
73083
+ label: "Pi Coding Agent",
73084
+ group: "ai"
73085
+ },
73086
+ {
73087
+ key: "omp",
73088
+ label: "oh-my-pi",
73089
+ group: "ai"
73090
+ },
73091
+ {
73092
+ key: "zed",
73093
+ label: "Zed",
73094
+ group: "ai"
73095
+ },
73096
+ {
73097
+ key: "zcode",
73098
+ label: "ZCode (Z.ai)",
73099
+ group: "ai"
73100
+ },
73101
+ {
73102
+ key: "zcode-plugin",
73103
+ label: "ZCode plugin",
73104
+ group: "ai"
73105
+ },
73106
+ {
73107
+ key: "pool",
73108
+ label: "Pool (Poolside)",
73109
+ group: "ai"
73110
+ },
73111
+ {
73112
+ key: "dsh",
73113
+ label: "DeepSeek Harness",
73114
+ group: "ai"
73115
+ }
73116
+ ];
73117
+ //#endregion
72529
73118
  //#region src/features/skills/simulated-skill.ts
72530
73119
  const SimulatedSkillFrontmatterSchema = zod_mini.z.looseObject({
72531
73120
  name: zod_mini.z.string(),
@@ -84871,7 +85460,7 @@ var DeepagentsSubagent = class DeepagentsSubagent extends ToolSubagent {
84871
85460
  //#endregion
84872
85461
  //#region src/features/subagents/devin-subagent.ts
84873
85462
  const DevinSubagentFrontmatterSchema = zod_mini.z.looseObject({
84874
- name: zod_mini.z.string(),
85463
+ name: zod_mini.z.optional(zod_mini.z.string()),
84875
85464
  description: zod_mini.z.optional(zod_mini.z.string()),
84876
85465
  model: zod_mini.z.optional(zod_mini.z.string()),
84877
85466
  "allowed-tools": zod_mini.z.optional(zod_mini.z.array(zod_mini.z.string())),
@@ -84924,7 +85513,7 @@ var DevinSubagent = class extends ToolSubagent {
84924
85513
  outputRoot: ".",
84925
85514
  frontmatter: {
84926
85515
  targets: ["*"],
84927
- name,
85516
+ name: name ?? this.getSubagentName(),
84928
85517
  description,
84929
85518
  ...Object.keys(devinSection).length > 0 && { devin: devinSection }
84930
85519
  },
@@ -84998,7 +85587,8 @@ var DevinSubagent = class extends ToolSubagent {
84998
85587
  const paths = this.getSettablePaths({ global });
84999
85588
  const filePath = (0, node_path.join)(outputRoot, paths.relativeDirPath, relativeFilePath);
85000
85589
  const fileContent = await readFileContent(filePath);
85001
- const { frontmatter, body: content } = parseFrontmatter(fileContent, filePath);
85590
+ const { frontmatter, body: content, hasFrontmatter } = parseFrontmatter(fileContent, filePath);
85591
+ if (!hasFrontmatter) throw new Error(`Missing frontmatter in ${filePath}`);
85002
85592
  const result = DevinSubagentFrontmatterSchema.safeParse(frontmatter);
85003
85593
  if (!result.success) throw new Error(`Invalid frontmatter in ${filePath}: ${formatError(result.error)}`);
85004
85594
  return new this({
@@ -98646,6 +99236,7 @@ var RulesProcessor = class extends FeatureProcessor {
98646
99236
  warnForOutputStyleRuleOptions({ rules, factory }) {
98647
99237
  const toolTarget = this.toolTarget;
98648
99238
  if (toolTarget !== "factorydroid" && toolTarget !== "qoder") return;
99239
+ const toolLabel = TOOL_DISPLAY.find((entry) => entry.key === toolTarget)?.label ?? toolTarget;
98649
99240
  for (const rule of rules) {
98650
99241
  const frontmatter = rule.getFrontmatter();
98651
99242
  const section = frontmatter[toolTarget];
@@ -98653,7 +99244,7 @@ var RulesProcessor = class extends FeatureProcessor {
98653
99244
  const { channel, name } = section;
98654
99245
  const fileName = stripControlCharacters(rule.getRelativeFilePath());
98655
99246
  const targets = frontmatter.targets ?? ["*"];
98656
- if (channel === "output-style" && targets.some((target) => target !== toolTarget)) this.logger.warn(`${fileName} sets ${toolTarget}.channel: output-style but also targets other tools, which receive it as an ordinary rule. Set \`targets: ["${toolTarget}"]\` to keep the output style to ${toolTarget === "qoder" ? "Qoder" : "Factory Droid"}.`);
99247
+ if (channel === "output-style" && targets.some((target) => target !== toolTarget)) this.logger.warn(`${fileName} sets ${toolTarget}.channel: output-style but also targets other tools, which receive it as an ordinary rule. Set \`targets: ["${toolTarget}"]\` to keep the output style to ${toolLabel}.`);
98657
99248
  if (channel !== "output-style" && name !== void 0) this.logger.warn(`${fileName} sets ${toolTarget}.name, which is read only with ${toolTarget}.channel: output-style; ignoring it.`);
98658
99249
  }
98659
99250
  }
@@ -103053,10 +103644,10 @@ Object.defineProperty(exports, "generate", {
103053
103644
  return generate;
103054
103645
  }
103055
103646
  });
103056
- Object.defineProperty(exports, "getFileSize", {
103647
+ Object.defineProperty(exports, "getFileSizeAndExecutableBit", {
103057
103648
  enumerable: true,
103058
103649
  get: function() {
103059
- return getFileSize;
103650
+ return getFileSizeAndExecutableBit;
103060
103651
  }
103061
103652
  });
103062
103653
  Object.defineProperty(exports, "getHomeDirectory", {
@@ -103101,12 +103692,6 @@ Object.defineProperty(exports, "hasEnclosingMarkOutsideKeycap", {
103101
103692
  return hasEnclosingMarkOutsideKeycap;
103102
103693
  }
103103
103694
  });
103104
- Object.defineProperty(exports, "hasExecutableBit", {
103105
- enumerable: true,
103106
- get: function() {
103107
- return hasExecutableBit;
103108
- }
103109
- });
103110
103695
  Object.defineProperty(exports, "importFromTool", {
103111
103696
  enumerable: true,
103112
103697
  get: function() {
@@ -103191,6 +103776,12 @@ Object.defineProperty(exports, "pathEscapesRoot", {
103191
103776
  return pathEscapesRoot;
103192
103777
  }
103193
103778
  });
103779
+ Object.defineProperty(exports, "posixRelativePathEscapesRoot", {
103780
+ enumerable: true,
103781
+ get: function() {
103782
+ return posixRelativePathEscapesRoot;
103783
+ }
103784
+ });
103194
103785
  Object.defineProperty(exports, "quoteForLog", {
103195
103786
  enumerable: true,
103196
103787
  get: function() {
@@ -103263,10 +103854,10 @@ Object.defineProperty(exports, "resolveRulesyncSourceWritePath", {
103263
103854
  return resolveRulesyncSourceWritePath;
103264
103855
  }
103265
103856
  });
103266
- Object.defineProperty(exports, "resolvedPathEscapesRoot", {
103857
+ Object.defineProperty(exports, "resolvedRelativePath", {
103267
103858
  enumerable: true,
103268
103859
  get: function() {
103269
- return resolvedPathEscapesRoot;
103860
+ return resolvedRelativePath;
103270
103861
  }
103271
103862
  });
103272
103863
  Object.defineProperty(exports, "runWithDirectoryRollback", {