rulesync 33.0.0 → 34.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.
@@ -386,7 +386,11 @@ const DeprecatedIgnoreFeatureSchema = z.literal("ignore").check(meta({
386
386
  const FeatureSchema = z.union([z.enum(ACTIVE_FEATURES), DeprecatedIgnoreFeatureSchema]);
387
387
  z.array(FeatureSchema);
388
388
  const FeatureWithWildcardSchema = z.union([z.enum([...ACTIVE_FEATURES, "*"]), DeprecatedIgnoreFeatureSchema]);
389
- const GitignoreDestinationSchema = z.enum(["gitignore", "gitattributes"]);
389
+ const GitignoreDestinationSchema = z.enum([
390
+ "gitignore",
391
+ "gitattributes",
392
+ "gitexclude"
393
+ ]);
390
394
  const FlattenedCommandNamingSchema = z.enum(["basename", "path"]);
391
395
  const FeatureOptionsSchema = z.record(z.string(), z.unknown());
392
396
  const FeatureValueSchema = z.union([
@@ -506,6 +510,7 @@ const rulesProcessorToolTargetTuple = [
506
510
  "claudecode-legacy",
507
511
  "cline",
508
512
  "codebuddy",
513
+ "codebuff",
509
514
  "codewhale",
510
515
  "codexcli",
511
516
  "commandcode",
@@ -1385,6 +1390,13 @@ async function getFileSize(filepath) {
1385
1390
  throw new Error(`Failed to get file size for "${filepath}": ${formatError(error)}`, { cause: error });
1386
1391
  }
1387
1392
  }
1393
+ /**
1394
+ * Whether a file carries any execute bit. Windows reports none, so a file
1395
+ * there is never seen as executable.
1396
+ */
1397
+ async function hasExecutableBit(filepath) {
1398
+ return ((await stat(filepath)).mode & 73) !== 0;
1399
+ }
1388
1400
  async function isSymlink(filepath) {
1389
1401
  try {
1390
1402
  return (await lstat(filepath)).isSymbolicLink();
@@ -3157,6 +3169,9 @@ var Config = class Config {
3157
3169
  const featureValue = value[feature];
3158
3170
  if (featureValue && typeof featureValue === "object" && isFeatureValueEnabled(featureValue)) return featureValue;
3159
3171
  }
3172
+ getRootGitignoreDestination() {
3173
+ return this.gitignoreDestination;
3174
+ }
3160
3175
  getGitignoreDestination(target, feature) {
3161
3176
  const rootLevel = this.gitignoreDestination;
3162
3177
  if (!isRulesyncConfigTargetsObject(this.targets)) return rootLevel;
@@ -3174,7 +3189,8 @@ var Config = class Config {
3174
3189
  return toolLevel ?? rootLevel;
3175
3190
  }
3176
3191
  static parseGitignoreDestination(value) {
3177
- if (value === "gitignore" || value === "gitattributes") return value;
3192
+ const result = GitignoreDestinationSchema.safeParse(value);
3193
+ return result.success ? result.data : void 0;
3178
3194
  }
3179
3195
  /**
3180
3196
  * Check if per-target features configuration is being used.
@@ -5244,12 +5260,20 @@ const JUNIE_HOOK_EVENTS = [
5244
5260
  "sessionEnd"
5245
5261
  ];
5246
5262
  /**
5247
- * Hook events supported by the GitLab Duo CLI. Only `SessionStart` is
5248
- * documented; its optional regex matcher filters on the session source
5249
- * (`startup`, `resume`). Only `type: "command"` hooks are supported.
5263
+ * Hook events supported by the GitLab Duo CLI. `SessionStart` is documented;
5264
+ * `PreToolUse` and `SessionEnd` ship in gitlab-lsp (`HOOK_EVENT_NAMES`, v9.28.0+)
5265
+ * but are not on docs.gitlab.com yet. The optional regex matcher filters on the
5266
+ * session source (`startup`, `resume`) for `SessionStart`, on `tool_name` for
5267
+ * `PreToolUse`, and on the end reason for `SessionEnd`. Only `type: "command"`
5268
+ * hooks are supported.
5250
5269
  * @see https://docs.gitlab.com/user/gitlab_duo_cli/customize/
5270
+ * @see https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/blob/main/packages/core/hooks/src/schema.ts
5251
5271
  */
5252
- const GITLABDUO_HOOK_EVENTS = ["sessionStart"];
5272
+ const GITLABDUO_HOOK_EVENTS = [
5273
+ "sessionStart",
5274
+ "preToolUse",
5275
+ "sessionEnd"
5276
+ ];
5253
5277
  /**
5254
5278
  * Hook events supported by Qwen Code.
5255
5279
  *
@@ -5590,7 +5614,10 @@ const CORTEXCODE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CA
5590
5614
  * or `agent` hooks with a `timeout` in seconds. The hooks reference documents ten events; the
5591
5615
  * plugins reference lists the full set, stating that plugin hooks "respond to
5592
5616
  * the same lifecycle events as user-defined hooks", so every event listed
5593
- * there is supported here.
5617
+ * there is supported here. The hooks reference also names `Setup`
5618
+ * (bootstrap/maintenance), and the published package's settings schema
5619
+ * (`@tencent-ai/codebuddy-code` 2.167.0) accepts `UserPromptExpansion`, whose
5620
+ * matcher filters on `command_name` as in Claude Code.
5594
5621
  *
5595
5622
  * @see https://www.codebuddy.ai/docs/cli/hooks
5596
5623
  * @see https://www.codebuddy.ai/docs/cli/plugins-reference
@@ -5621,7 +5648,9 @@ const CODEBUDDY_HOOK_EVENTS = [
5621
5648
  "postCompact",
5622
5649
  "elicitation",
5623
5650
  "elicitationResult",
5624
- "sessionEnd"
5651
+ "sessionEnd",
5652
+ "setup",
5653
+ "userPromptExpansion"
5625
5654
  ];
5626
5655
  /**
5627
5656
  * CodeBuddy Code events that fire on every occurrence and take no `matcher`.
@@ -5670,7 +5699,9 @@ const CANONICAL_TO_CODEBUDDY_EVENT_NAMES = {
5670
5699
  postCompact: "PostCompact",
5671
5700
  elicitation: "Elicitation",
5672
5701
  elicitationResult: "ElicitationResult",
5673
- sessionEnd: "SessionEnd"
5702
+ sessionEnd: "SessionEnd",
5703
+ setup: "Setup",
5704
+ userPromptExpansion: "UserPromptExpansion"
5674
5705
  };
5675
5706
  const CODEBUDDY_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANONICAL_TO_CODEBUDDY_EVENT_NAMES).map(([k, v]) => [v, k]));
5676
5707
  /**
@@ -6524,7 +6555,11 @@ const KIRO_IDE_TO_CANONICAL_EVENT_NAMES = Object.fromEntries(Object.entries(CANO
6524
6555
  /**
6525
6556
  * Map canonical camelCase event names to GitLab Duo CLI PascalCase.
6526
6557
  */
6527
- const CANONICAL_TO_GITLABDUO_EVENT_NAMES = { sessionStart: "SessionStart" };
6558
+ const CANONICAL_TO_GITLABDUO_EVENT_NAMES = {
6559
+ sessionStart: "SessionStart",
6560
+ preToolUse: "PreToolUse",
6561
+ sessionEnd: "SessionEnd"
6562
+ };
6528
6563
  /**
6529
6564
  * Map GitLab Duo CLI PascalCase event names to canonical camelCase.
6530
6565
  */
@@ -8265,6 +8300,48 @@ const ReasonixPermissionsOverrideSchema = z.looseObject({
8265
8300
  ]))
8266
8301
  });
8267
8302
  /**
8303
+ * Tool-scoped override block for Qoder. `outputStyle` is written to the
8304
+ * top-level `outputStyle` key of `.qoder/settings.json` (project) /
8305
+ * `~/.qoder/settings.json` (global): the name of the active output style, a
8306
+ * custom style from `.qoder/output-styles/` (see the `qoder.channel:
8307
+ * output-style` rule option) or a `plugin-name:style-name` reference. The
8308
+ * shared `permission` block continues to drive `permissions.allow`/`ask`/`deny`.
8309
+ *
8310
+ * @see https://docs.qoder.com/cli/output-styles
8311
+ * @see https://docs.qoder.com/cli/settings-reference
8312
+ * @example
8313
+ * { "outputStyle": "concise-review" }
8314
+ */
8315
+ const QoderPermissionsOverrideSchema = z.looseObject({
8316
+ permission: z.optional(ToolScopedPermissionSchema),
8317
+ outputStyle: z.optional(z.string().check(z.trim(), z.minLength(1)))
8318
+ });
8319
+ /**
8320
+ * One native Factory Droid permission rule. `match.prefix` positions are either
8321
+ * a single token or a non-empty list of alternative tokens.
8322
+ *
8323
+ * @see https://docs.factory.com/autonomy-and-safety/permission-rules
8324
+ */
8325
+ const FactorydroidPermissionRuleSchema = z.looseObject({
8326
+ id: z.string(),
8327
+ decision: z.enum([
8328
+ "allow",
8329
+ "ask",
8330
+ "block"
8331
+ ]),
8332
+ match: z.looseObject({ prefix: z.optional(z.array(z.union([z.string(), z.array(z.string()).check(z.minLength(1))])).check(z.minLength(1))) }),
8333
+ tests: z.optional(z.looseObject({
8334
+ match: z.optional(z.array(z.string())),
8335
+ noMatch: z.optional(z.array(z.string()))
8336
+ })),
8337
+ reason: z.optional(z.string()),
8338
+ enabled: z.optional(z.boolean())
8339
+ });
8340
+ const FactorydroidPermissionRulesSchema = z.looseObject({
8341
+ version: z.number(),
8342
+ rules: z.array(FactorydroidPermissionRuleSchema)
8343
+ });
8344
+ /**
8268
8345
  * Tool-scoped override block for Factory Droid. Factory Droid's `settings.json`
8269
8346
  * exposes security controls with no canonical per-command allow/ask/deny slot —
8270
8347
  * `commandBlocklist` (a hard-block tier that can never be approved, distinct from
@@ -8272,7 +8349,8 @@ const ReasonixPermissionsOverrideSchema = z.looseObject({
8272
8349
  * (`enabled`/`mode`/`filesystem`/`network`), `mcpPolicy`,
8273
8350
  * `mcpAutonomyOverrides` (per-MCP-tool autonomy levels), `enableDroidShield`,
8274
8351
  * and autonomy settings (`sessionDefaultSettings`, `maxAutonomyLevel`,
8275
- * `subagentAutonomyLevel`, `interactionMode`). Fields placed here are merged
8352
+ * `subagentAutonomyLevel`, `interactionMode`), and the native versioned
8353
+ * `permissionRules` (allow/ask/block). Fields placed here are merged
8276
8354
  * into `settings.json` and
8277
8355
  * emitted only for Factory Droid, while the shared `permission` block continues
8278
8356
  * to drive `commandAllowlist`/`commandDenylist`. Kept `looseObject` passthrough.
@@ -8283,7 +8361,8 @@ const ReasonixPermissionsOverrideSchema = z.looseObject({
8283
8361
  const FactorydroidPermissionsOverrideSchema = z.looseObject({
8284
8362
  permission: z.optional(ToolScopedPermissionSchema),
8285
8363
  commandBlocklist: z.optional(z.array(z.string())),
8286
- disabledSkills: z.optional(z.array(z.string()))
8364
+ disabledSkills: z.optional(z.array(z.string())),
8365
+ permissionRules: z.optional(FactorydroidPermissionRulesSchema)
8287
8366
  });
8288
8367
  /**
8289
8368
  * Tool-scoped override block for Warp. Warp's `[agents.profiles]` table exposes
@@ -9088,6 +9167,7 @@ const PermissionsConfigSchema = z.looseObject({
9088
9167
  codewhale: z.optional(CodewhalePermissionsOverrideSchema),
9089
9168
  zed: z.optional(ZedPermissionsOverrideSchema),
9090
9169
  devin: z.optional(DevinPermissionsOverrideSchema),
9170
+ qoder: z.optional(QoderPermissionsOverrideSchema),
9091
9171
  "antigravity-ide": z.optional(CanonicalPermissionsOverrideSchema),
9092
9172
  continue: z.optional(CanonicalPermissionsOverrideSchema),
9093
9173
  copilot: z.optional(CopilotPermissionsOverrideSchema),
@@ -9099,7 +9179,6 @@ const PermissionsConfigSchema = z.looseObject({
9099
9179
  goose: z.optional(CanonicalPermissionsOverrideSchema),
9100
9180
  omp: z.optional(OmpPermissionsOverrideSchema),
9101
9181
  pool: z.optional(CanonicalPermissionsOverrideSchema),
9102
- qoder: z.optional(CanonicalPermissionsOverrideSchema),
9103
9182
  grokcli: z.optional(CanonicalPermissionsOverrideSchema),
9104
9183
  "kimi-code": z.optional(KimiCodePermissionsOverrideSchema),
9105
9184
  roo: z.optional(CanonicalPermissionsOverrideSchema),
@@ -9620,7 +9699,9 @@ const RulesyncRuleFrontmatterSchema = z.object({
9620
9699
  qoder: z.optional(z.looseObject({
9621
9700
  trigger: z.optional(z.string()),
9622
9701
  glob: z.optional(z.union([z.string(), z.array(z.string())])),
9623
- description: z.optional(z.string())
9702
+ description: z.optional(z.string()),
9703
+ channel: z.optional(z.enum(["output-style"])),
9704
+ name: z.optional(z.string().check(z.trim(), z.minLength(1)))
9624
9705
  })),
9625
9706
  augmentcode: z.optional(z.looseObject({
9626
9707
  type: z.optional(z.string()),
@@ -12989,7 +13070,7 @@ const SHARED_CONFIG_OWNERSHIP = {
12989
13070
  },
12990
13071
  permissions: {
12991
13072
  kind: "replace-owned-keys",
12992
- ownedKeys: ["permissions"]
13073
+ ownedKeys: ["permissions", "outputStyle"]
12993
13074
  }
12994
13075
  }
12995
13076
  },
@@ -22403,6 +22484,7 @@ const QODER_DIR = ".qoder";
22403
22484
  const QODER_RULE_FILE_NAME = "AGENTS.md";
22404
22485
  const QODER_LOCAL_RULE_FILE_NAME = "AGENTS.local.md";
22405
22486
  const QODER_RULES_DIR_PATH = join(QODER_DIR, "rules");
22487
+ const QODER_OUTPUT_STYLES_DIR_PATH = join(QODER_DIR, "output-styles");
22406
22488
  const QODER_SETTINGS_FILE_NAME = "settings.json";
22407
22489
  const QODER_PROJECT_MCP_FILE_NAME = ".mcp.json";
22408
22490
  const QODER_COMMANDS_DIR_PATH = join(QODER_DIR, "commands");
@@ -29777,6 +29859,11 @@ const CORTEXCODE_SPEC = {
29777
29859
  canonical: "enabled",
29778
29860
  tool: "enabled"
29779
29861
  }],
29862
+ stringPassthroughFields: [{
29863
+ canonical: "statusMessage",
29864
+ tool: "statusMessage",
29865
+ commandOnly: true
29866
+ }],
29780
29867
  objectPassthroughFields: [{
29781
29868
  canonical: "source",
29782
29869
  tool: "source",
@@ -29791,7 +29878,7 @@ const CORTEXCODE_SPEC = {
29791
29878
  * (project scope) and of the dedicated `~/.snowflake/cortex/hooks.json` (user
29792
29879
  * scope), both in the Claude-Code shape: `{ "<Event>": [{ "matcher"?:
29793
29880
  * "<regex>", "hooks": [{ "type": "command" | "prompt", ..., "timeout"?:
29794
- * <seconds>, "enabled"?: <boolean>, "source"?: { "source", "ref"? } }] }] }`.
29881
+ * <seconds>, "enabled"?: <boolean>, "statusMessage"?: <string> (command only), "source"?: { "source", "ref"? } }] }] }`.
29795
29882
  * The project file also holds settings rulesync does not own, so generation
29796
29883
  * merges the `hooks` key into
29797
29884
  * either file (see `SHARED_CONFIG_OWNERSHIP`) instead of overwriting it, and
@@ -30730,7 +30817,8 @@ const FACTORYDROID_OVERRIDE_KEYS = [
30730
30817
  "allowManagedHooksOnly",
30731
30818
  "disabledSkills",
30732
30819
  "modelPolicy",
30733
- "missionPolicy"
30820
+ "missionPolicy",
30821
+ "permissionRules"
30734
30822
  ];
30735
30823
  //#endregion
30736
30824
  //#region src/utils/factorydroid-settings.ts
@@ -30945,7 +31033,7 @@ const GITLABDUO_CONVERTER_CONFIG = {
30945
31033
  *
30946
31034
  * Hooks live in a dedicated file, `<project>/.gitlab/duo/hooks.json` (project,
30947
31035
  * opt-in via `--enable-project-hooks` / `GITLAB_ENABLE_PROJECT_HOOKS=true`) or
30948
- * `~/.gitlab/duo/hooks.json` (user), shaped as `{ "hooks": { "SessionStart": [...] } }`.
31036
+ * `~/.gitlab/duo/hooks.json` (user), shaped as `{ "hooks": { "SessionStart": [...], "PreToolUse": [...] } }`.
30949
31037
  * rulesync owns the whole file, so it is regenerated and deleted as a unit.
30950
31038
  */
30951
31039
  var GitlabduoHooks = class GitlabduoHooks extends ToolHooks {
@@ -36599,6 +36687,7 @@ var ClineIgnore = class ClineIgnore extends ToolIgnore {
36599
36687
  const CODEBUFF_IGNORE_FILE_NAME = ".codebuffignore";
36600
36688
  const CODEBUFF_AGENTS_DIR = ".agents";
36601
36689
  const CODEBUFF_MCP_FILE_NAME = "mcp.json";
36690
+ const CODEBUFF_GLOBAL_RULE_FILE_NAME = ".AGENTS.md";
36602
36691
  //#endregion
36603
36692
  //#region src/features/ignore/codebuff-ignore.ts
36604
36693
  /**
@@ -43500,11 +43589,45 @@ const CANONICAL_TO_GITLABDUO_TRANSPORT = {
43500
43589
  sse: "sse"
43501
43590
  };
43502
43591
  /**
43592
+ * Map the canonical `timeout` (milliseconds) onto GitLab Duo's `timeoutMs`, a
43593
+ * per-server tool-call inactivity timeout (gitlab-lsp v9.25.0+) that must be a
43594
+ * positive integer. An explicit `timeoutMs` on the same server wins; a
43595
+ * `timeout` Duo would reject is dropped with a warning.
43596
+ *
43597
+ * @see https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/commit/e8c4a76a66e8f37d4c97fc03ffe983bfadf43f76
43598
+ */
43599
+ function toGitlabduoTimeout({ serverName, server, logger }) {
43600
+ const { timeout, ...rest } = server;
43601
+ if (timeout === void 0 || rest.timeoutMs !== void 0) return rest;
43602
+ if (typeof timeout !== "number" || !Number.isInteger(timeout) || timeout <= 0) {
43603
+ logger?.warn(`GitLab Duo CLI MCP server "${serverName}": dropping timeout ${JSON.stringify(timeout)}, as timeoutMs must be a positive integer of milliseconds.`);
43604
+ return rest;
43605
+ }
43606
+ return {
43607
+ ...rest,
43608
+ timeoutMs: timeout
43609
+ };
43610
+ }
43611
+ /**
43612
+ * Read GitLab Duo's `timeoutMs` back into the canonical `timeout`
43613
+ * (milliseconds). A server that already carries a `timeout` is left as is, so
43614
+ * both keys round-trip and `timeoutMs` keeps winning on the next generate.
43615
+ */
43616
+ function fromGitlabduoTimeout(server) {
43617
+ if (typeof server !== "object" || server === null || Array.isArray(server)) return server;
43618
+ const { timeoutMs, ...rest } = server;
43619
+ if (timeoutMs === void 0 || rest.timeout !== void 0) return server;
43620
+ return {
43621
+ ...rest,
43622
+ timeout: timeoutMs
43623
+ };
43624
+ }
43625
+ /**
43503
43626
  * Convert one canonical server into GitLab Duo's shape. Every documented
43504
43627
  * example spells the transport out as `type` (`stdio`, `http` or `sse`), so it
43505
43628
  * is always written, inferred from `command` / `url` when the source leaves it
43506
- * implicit. Every other field — `args`, `env`, `approvedTools`, ... — is carried
43507
- * through as authored.
43629
+ * implicit. The canonical `timeout` becomes `timeoutMs`; every other field —
43630
+ * `args`, `env`, `approvedTools`, ... — is carried through as authored.
43508
43631
  */
43509
43632
  function toGitlabduoServer({ serverName, serverConfig, logger }) {
43510
43633
  if (declaresNoTransport(serverConfig)) return warnAndSkipMcpServer({
@@ -43513,7 +43636,7 @@ function toGitlabduoServer({ serverName, serverConfig, logger }) {
43513
43636
  reason: "no command or url",
43514
43637
  logger
43515
43638
  });
43516
- const { type, transport, httpUrl: _httpUrl, url: _url, ...rest } = serverConfig;
43639
+ const { type, transport, httpUrl: _httpUrl, url: _url, ...others } = serverConfig;
43517
43640
  const declared = type ?? transport;
43518
43641
  const mapped = declared ? CANONICAL_TO_GITLABDUO_TRANSPORT[declared] : isRemoteMcpServer(serverConfig) ? "http" : "stdio";
43519
43642
  if (!mapped) return warnAndSkipMcpServer({
@@ -43522,8 +43645,13 @@ function toGitlabduoServer({ serverName, serverConfig, logger }) {
43522
43645
  reason: `the unsupported transport "${declared}" (GitLab Duo CLI accepts stdio, http and sse)`,
43523
43646
  logger
43524
43647
  });
43648
+ const rest = toGitlabduoTimeout({
43649
+ serverName,
43650
+ server: others,
43651
+ logger
43652
+ });
43525
43653
  if (mapped === "stdio") {
43526
- const { command, args } = rest;
43654
+ const { command, args } = serverConfig;
43527
43655
  if (Array.isArray(command)) {
43528
43656
  const [executable, ...leadingArgs] = command;
43529
43657
  return {
@@ -43612,7 +43740,13 @@ var GitlabduoMcp = class GitlabduoMcp extends ToolMcp {
43612
43740
  });
43613
43741
  }
43614
43742
  toRulesyncMcp() {
43615
- return this.toRulesyncMcpDefault();
43743
+ const { mcpServers, ...others } = this.json;
43744
+ if (typeof mcpServers !== "object" || mcpServers === null || Array.isArray(mcpServers)) return this.toRulesyncMcpDefault();
43745
+ const servers = Object.fromEntries(Object.entries(mcpServers).map(([name, server]) => [name, fromGitlabduoTimeout(server)]));
43746
+ return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({
43747
+ ...others,
43748
+ mcpServers: servers
43749
+ }) });
43616
43750
  }
43617
43751
  validate() {
43618
43752
  return {
@@ -46719,6 +46853,15 @@ const CONSUMED_CANONICAL_KEYS = /* @__PURE__ */ new Set([
46719
46853
  ]);
46720
46854
  const MILLISECONDS_PER_SECOND = 1e3;
46721
46855
  /**
46856
+ * The only keys a project override entry for a user-level server carries:
46857
+ * Pi reads a `.pi/mcp.json` entry without `command`, `url` or `type` as an
46858
+ * override of `enabled`, `exposure` and `toolExposure` of the user-level server
46859
+ * with the same name, and keeps the rest of that server.
46860
+ *
46861
+ * @see https://github.com/earendil-works/pi/blob/v1.1.0/packages/coding-agent/docs/mcp.md
46862
+ */
46863
+ const PI_OVERRIDE_PASSTHROUGH_KEYS = ["exposure", "toolExposure"];
46864
+ /**
46722
46865
  * Pi rejects a server whose name has any other character, and treats names
46723
46866
  * that differ only in `-` and `_` as one server (its tools share the
46724
46867
  * `mcp__<name>` namespace with `-` replaced by `_`), rejecting the second.
@@ -46754,17 +46897,12 @@ function hasUnexpandedEnvVarRef(serverConfig) {
46754
46897
  /**
46755
46898
  * The transport fields Pi reads for one server: `type` `stdio` with a string
46756
46899
  * `command` plus `args`, or `type` `http` with a `url`. A server Pi cannot
46757
- * start or reach — no transport, a remote transport without a URL, an SSE or
46758
- * WebSocket server, or a stdio entry without a command — is rejected by Pi, so
46759
- * it is skipped with a warning (`null`).
46900
+ * start or reach — a remote transport without a URL, an SSE or WebSocket
46901
+ * server, or a stdio entry without a command — is rejected by Pi, so it is
46902
+ * skipped with a warning (`null`). A server with no transport at all is a
46903
+ * project override instead (see `convertOverrideToPi`).
46760
46904
  */
46761
46905
  function convertTransportToPi({ serverName, serverConfig, logger }) {
46762
- if (declaresNoTransport(serverConfig)) return warnAndSkipMcpServer({
46763
- toolName: "Pi",
46764
- serverName,
46765
- reason: "no transport",
46766
- logger
46767
- });
46768
46906
  if (isRemoteMcpServer(serverConfig)) {
46769
46907
  const url = resolveRemoteMcpUrl(serverConfig);
46770
46908
  const stated = serverConfig.type ?? serverConfig.transport;
@@ -46805,56 +46943,117 @@ function convertTransportToPi({ serverName, serverConfig, logger }) {
46805
46943
  };
46806
46944
  }
46807
46945
  /**
46808
- * Convert the canonical server map to Pi's `mcpServers` shape. Besides the
46809
- * transport fields, Pi switches a server off with `enabled: false` and reads
46810
- * `timeout` in seconds (canonical: milliseconds). A server name Pi rejects is
46811
- * skipped with a warning, and so is `auth` in the project file, which Pi
46812
- * accepts only in the global one (it drops the whole server otherwise).
46946
+ * A transport-less server in the project file becomes a Pi override entry for
46947
+ * the user-level server of the same name. `disabled` maps to `enabled` both
46948
+ * ways (an explicit `disabled: false` is "Enable in this project"), and any key
46949
+ * Pi ignores on an override is dropped with a warning. The global file has no
46950
+ * layer below it to override, so the entry is skipped there.
46951
+ */
46952
+ function convertOverrideToPi({ serverName, serverConfig, global, logger }) {
46953
+ if (global) return warnAndSkipMcpServer({
46954
+ toolName: "Pi",
46955
+ serverName,
46956
+ reason: "no transport",
46957
+ logger
46958
+ });
46959
+ const record = serverConfig;
46960
+ const override = {};
46961
+ if (typeof serverConfig.disabled === "boolean") override.enabled = !serverConfig.disabled;
46962
+ for (const key of PI_OVERRIDE_PASSTHROUGH_KEYS) if (record[key] !== void 0) override[key] = record[key];
46963
+ if (Object.keys(override).length === 0) return warnAndSkipMcpServer({
46964
+ toolName: "Pi",
46965
+ serverName,
46966
+ reason: "no transport and nothing a Pi project override sets (disabled, exposure, toolExposure)",
46967
+ logger
46968
+ });
46969
+ const droppedKeys = Object.keys(record).filter((key) => key !== "disabled" && !PI_OVERRIDE_PASSTHROUGH_KEYS.includes(key) && !PROTOTYPE_POLLUTION_KEYS.has(key));
46970
+ if (droppedKeys.length > 0) logger?.warn(`Pi MCP: dropping ${droppedKeys.map((key) => quoteValueForWarning(key)).join(", ")} of the project override ${quoteValueForWarning(serverName)}: Pi reads only enabled, exposure and toolExposure from an entry without a transport.`);
46971
+ return override;
46972
+ }
46973
+ /**
46974
+ * Whether Pi rejects the server name, either outright or because an earlier
46975
+ * server already took its `mcp__<name>` namespace; warns when it does.
46976
+ */
46977
+ function isRejectedServerName({ serverName, namespaces, logger }) {
46978
+ if (!PI_SERVER_NAME_PATTERN.test(serverName)) {
46979
+ warnAndSkipMcpServer({
46980
+ toolName: "Pi",
46981
+ serverName,
46982
+ reason: "a name Pi rejects (use letters, digits, \"_\" and \"-\")",
46983
+ logger
46984
+ });
46985
+ return true;
46986
+ }
46987
+ const clash = namespaces.get(toPiNamespace(serverName));
46988
+ if (clash !== void 0) {
46989
+ warnAndSkipMcpServer({
46990
+ toolName: "Pi",
46991
+ serverName,
46992
+ reason: `a name Pi treats as the same server as ${quoteValueForWarning(clash)} ("-" and "_" are equivalent)`,
46993
+ logger
46994
+ });
46995
+ return true;
46996
+ }
46997
+ return false;
46998
+ }
46999
+ /**
47000
+ * Convert one canonical server to its Pi entry, or `null` when it is skipped.
47001
+ * Besides the transport fields, Pi switches a server off with `enabled: false`
47002
+ * and reads `timeout` in seconds (canonical: milliseconds). `auth` is dropped
47003
+ * with a warning from the project file, since Pi accepts it only in the global
47004
+ * one (it drops the whole server otherwise). A server without a transport is a
47005
+ * project override entry instead.
47006
+ */
47007
+ function convertServerToPi({ serverName, serverConfig, global, logger }) {
47008
+ if (declaresNoTransport(serverConfig)) return convertOverrideToPi({
47009
+ serverName,
47010
+ serverConfig,
47011
+ global,
47012
+ logger
47013
+ });
47014
+ const converted = convertTransportToPi({
47015
+ serverName,
47016
+ serverConfig,
47017
+ logger
47018
+ });
47019
+ if (!converted) return null;
47020
+ for (const [key, value] of Object.entries(serverConfig)) {
47021
+ if (CONSUMED_CANONICAL_KEYS.has(key) || PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
47022
+ converted[key] = value;
47023
+ }
47024
+ if (serverConfig.timeout !== void 0) {
47025
+ if (Number.isFinite(serverConfig.timeout) && serverConfig.timeout > 0) converted.timeout = serverConfig.timeout / MILLISECONDS_PER_SECOND;
47026
+ else logger?.warn(`Pi MCP: dropping the "timeout" of ${quoteValueForWarning(serverName)}: expected a positive number of milliseconds.`);
47027
+ }
47028
+ if (serverConfig.disabled === true) converted.enabled = false;
47029
+ if (!global && "auth" in converted) {
47030
+ delete converted.auth;
47031
+ logger?.warn(`Pi MCP: dropping the "auth" of ${quoteValueForWarning(serverName)}: Pi accepts it only in the global ~/.pi/agent/mcp.json.`);
47032
+ }
47033
+ if (hasUnexpandedEnvVarRef(converted)) logger?.warn(`Pi MCP: ${quoteValueForWarning(serverName)} carries an environment variable reference Pi does not expand. Pi expands only \${VAR} and $VAR in env and headers values, and only a leading ~/ in command, args and cwd.`);
47034
+ return converted;
47035
+ }
47036
+ /**
47037
+ * Convert the canonical server map to Pi's `mcpServers` shape. A server name
47038
+ * Pi rejects is skipped with a warning.
46813
47039
  */
46814
47040
  function convertToPiFormat({ mcpServers, global, logger }) {
46815
47041
  const result = {};
46816
47042
  const namespaces = /* @__PURE__ */ new Map();
46817
47043
  for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
46818
47044
  if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
46819
- if (!PI_SERVER_NAME_PATTERN.test(serverName)) {
46820
- warnAndSkipMcpServer({
46821
- toolName: "Pi",
46822
- serverName,
46823
- reason: "a name Pi rejects (use letters, digits, \"_\" and \"-\")",
46824
- logger
46825
- });
46826
- continue;
46827
- }
46828
- const clash = namespaces.get(toPiNamespace(serverName));
46829
- if (clash !== void 0) {
46830
- warnAndSkipMcpServer({
46831
- toolName: "Pi",
46832
- serverName,
46833
- reason: `a name Pi treats as the same server as ${quoteValueForWarning(clash)} ("-" and "_" are equivalent)`,
46834
- logger
46835
- });
46836
- continue;
46837
- }
46838
- const converted = convertTransportToPi({
47045
+ if (isRejectedServerName({
47046
+ serverName,
47047
+ namespaces,
47048
+ logger
47049
+ })) continue;
47050
+ const converted = convertServerToPi({
46839
47051
  serverName,
46840
47052
  serverConfig,
47053
+ global,
46841
47054
  logger
46842
47055
  });
46843
47056
  if (!converted) continue;
46844
- for (const [key, value] of Object.entries(serverConfig)) {
46845
- if (CONSUMED_CANONICAL_KEYS.has(key) || PROTOTYPE_POLLUTION_KEYS.has(key)) continue;
46846
- converted[key] = value;
46847
- }
46848
- if (serverConfig.timeout !== void 0) {
46849
- if (Number.isFinite(serverConfig.timeout) && serverConfig.timeout > 0) converted.timeout = serverConfig.timeout / MILLISECONDS_PER_SECOND;
46850
- else logger?.warn(`Pi MCP: dropping the "timeout" of ${quoteValueForWarning(serverName)}: expected a positive number of milliseconds.`);
46851
- }
46852
- if (serverConfig.disabled === true) converted.enabled = false;
46853
- if (!global && "auth" in converted) {
46854
- delete converted.auth;
46855
- logger?.warn(`Pi MCP: dropping the "auth" of ${quoteValueForWarning(serverName)}: Pi accepts it only in the global ~/.pi/agent/mcp.json.`);
46856
- }
46857
- if (hasUnexpandedEnvVarRef(converted)) logger?.warn(`Pi MCP: ${quoteValueForWarning(serverName)} carries an environment variable reference Pi does not expand. Pi expands only \${VAR} and $VAR in env and headers values, and only a leading ~/ in command, args and cwd.`);
46858
47057
  namespaces.set(toPiNamespace(serverName), serverName);
46859
47058
  result[serverName] = converted;
46860
47059
  }
@@ -46863,16 +47062,19 @@ function convertToPiFormat({ mcpServers, global, logger }) {
46863
47062
  /**
46864
47063
  * Convert Pi's `mcpServers` back to the canonical shape: `enabled: false`
46865
47064
  * becomes `disabled: true` and `timeout` (seconds) becomes milliseconds; every
46866
- * other key is already canonical.
47065
+ * other key is already canonical. On a transport-less override entry an
47066
+ * explicit `enabled: true` is kept as `disabled: false`, since it switches on a
47067
+ * user-level server the user turned off.
46867
47068
  */
46868
47069
  function convertFromPiFormat(mcpServers) {
46869
47070
  const result = {};
46870
47071
  for (const [serverName, serverConfig] of Object.entries(mcpServers)) {
46871
47072
  if (PROTOTYPE_POLLUTION_KEYS.has(serverName) || !isRecord$1(serverConfig)) continue;
46872
47073
  const { enabled, timeout, ...rest } = serverConfig;
46873
- const converted = enabled === false ? {
47074
+ const keepsEnabled = enabled === true && declaresNoTransport(rest);
47075
+ const converted = enabled === false || keepsEnabled ? {
46874
47076
  ...rest,
46875
- disabled: true
47077
+ disabled: !enabled
46876
47078
  } : rest;
46877
47079
  if (typeof timeout === "number") converted.timeout = timeout * MILLISECONDS_PER_SECOND;
46878
47080
  else if (timeout !== void 0) converted.timeout = timeout;
@@ -46948,8 +47150,11 @@ var PiMcp = class PiMcp extends ToolMcp {
46948
47150
  });
46949
47151
  }
46950
47152
  toRulesyncMcp() {
46951
- const mcpServers = isMcpServers(this.json.mcpServers) ? this.json.mcpServers : {};
46952
- return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({ mcpServers: convertFromPiFormat(mcpServers) }, null, 2) });
47153
+ const { shared, toolOnly } = splitMcpServersByTransport(convertFromPiFormat(isMcpServers(this.json.mcpServers) ? this.json.mcpServers : {}));
47154
+ return this.toRulesyncMcpDefault({ fileContent: JSON.stringify({
47155
+ mcpServers: shared,
47156
+ ...Object.keys(toolOnly).length > 0 && { pi: { mcpServers: toolOnly } }
47157
+ }, null, 2) });
46953
47158
  }
46954
47159
  validate() {
46955
47160
  return {
@@ -64742,9 +64947,11 @@ const DEFAULT_TOOLS_KEY = "defaultTools";
64742
64947
  * the built-in tools enabled at startup (v0.84.2) — authored through the `pi`
64743
64948
  * permissions override.
64744
64949
  *
64745
- * Scope semantics are the **opposite** of the union-merge most targets use:
64746
- * a project `defaultTools` array *replaces* the global array rather than adding
64747
- * to it, so the two scopes are written independently and never combined.
64950
+ * Scope layering is Pi's own rather than the union-merge most targets use:
64951
+ * a project `defaultTools` array with a plain name *replaces* the global
64952
+ * array, while one made only of `+name` / `-name` entries (Pi v0.99.0+) changes
64953
+ * the global selection instead. Entries are written verbatim, so the two
64954
+ * scopes are written independently and never combined by rulesync.
64748
64955
  * Project scope writes `.pi/settings.json`; global scope writes
64749
64956
  * `~/.pi/agent/settings.json`.
64750
64957
  *
@@ -65404,6 +65611,13 @@ function convertPoolToRulesync({ tools, paths }) {
65404
65611
  //#region src/features/permissions/qoder-permissions.ts
65405
65612
  /** Top-level key of `.qoder/settings.json` that rulesync owns. */
65406
65613
  const QODER_PERMISSIONS_KEY = "permissions";
65614
+ /**
65615
+ * Top-level key the `qoder` override authors: the active output style. Qoder
65616
+ * also reads `general.outputStyle`, but the top-level key wins, so that is the
65617
+ * one written and imported.
65618
+ * @see https://docs.qoder.com/cli/settings-reference
65619
+ */
65620
+ const QODER_OUTPUT_STYLE_KEY = "outputStyle";
65407
65621
  const CATCH_ALL_PATTERN$2 = "*";
65408
65622
  /** Prefix of the fully qualified MCP tool names (`mcp__<server>__<tool>`). */
65409
65623
  const MCP_TOOL_PREFIX = "mcp__";
@@ -65563,9 +65777,11 @@ function buildQoderRuleLists({ config, existingPermissions, logger }) {
65563
65777
  * through the shared-config gateway, the sibling keys of `permissions`
65564
65778
  * (`additionalDirectories`, `trustDirectories`) are kept, and the file is
65565
65779
  * never deleted. `.qoder/settings.local.json` (where `/allow` and `/deny`
65566
- * persist) is left to the user.
65780
+ * persist) is left to the user. The `qoder` override's `outputStyle` is
65781
+ * written to (and imported from) the top-level `outputStyle` key.
65567
65782
  *
65568
65783
  * @see https://docs.qoder.com/en/cli/permissions
65784
+ * @see https://docs.qoder.com/cli/output-styles
65569
65785
  */
65570
65786
  var QoderPermissions = class QoderPermissions extends ToolPermissions {
65571
65787
  constructor(params) {
@@ -65604,11 +65820,14 @@ var QoderPermissions = class QoderPermissions extends ToolPermissions {
65604
65820
  relativePath: join(paths.relativeDirPath, paths.relativeFilePath)
65605
65821
  });
65606
65822
  const existingPermissions = isRecord$1(existing[QODER_PERMISSIONS_KEY]) ? existing[QODER_PERMISSIONS_KEY] : {};
65823
+ const config = rulesyncPermissions.getJson();
65607
65824
  const lists = buildQoderRuleLists({
65608
- config: rulesyncPermissions.getJson(),
65825
+ config,
65609
65826
  existingPermissions,
65610
65827
  logger
65611
65828
  });
65829
+ const outputStyle = config.qoder?.outputStyle?.trim();
65830
+ if (outputStyle !== void 0) logger?.warn(`Qoder permissions: qoder.outputStyle = ${JSON.stringify(outputStyle)} was written to ${join(paths.relativeDirPath, paths.relativeFilePath)}${global ? " in the home directory" : ""}, so Qoder appends that style's instructions to the system prompt of every session it applies to.`);
65612
65831
  const permissions = { ...existingPermissions };
65613
65832
  for (const action of ACTIONS) if (lists[action].length > 0) permissions[action] = lists[action];
65614
65833
  else delete permissions[action];
@@ -65620,7 +65839,10 @@ var QoderPermissions = class QoderPermissions extends ToolPermissions {
65620
65839
  fileKey: sharedConfigFileKey(paths),
65621
65840
  feature: "permissions",
65622
65841
  existingContent,
65623
- patch: { [QODER_PERMISSIONS_KEY]: Object.keys(permissions).length > 0 ? permissions : void 0 },
65842
+ patch: {
65843
+ [QODER_PERMISSIONS_KEY]: Object.keys(permissions).length > 0 ? permissions : void 0,
65844
+ ...outputStyle !== void 0 && { [QODER_OUTPUT_STYLE_KEY]: outputStyle }
65845
+ },
65624
65846
  filePath
65625
65847
  }),
65626
65848
  validate: true,
@@ -65641,7 +65863,12 @@ var QoderPermissions = class QoderPermissions extends ToolPermissions {
65641
65863
  const existing = rules[parsed.pattern];
65642
65864
  if (existing === void 0 || ACTION_RANK[action] > ACTION_RANK[existing]) rules[parsed.pattern] = action;
65643
65865
  }
65644
- return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({ permission }, null, 2) });
65866
+ const outputStyle = settings[QODER_OUTPUT_STYLE_KEY];
65867
+ const hasOutputStyle = typeof outputStyle === "string" && outputStyle.trim() !== "";
65868
+ return this.toRulesyncPermissionsDefault({ fileContent: JSON.stringify({
65869
+ permission,
65870
+ ...hasOutputStyle && { qoder: { outputStyle } }
65871
+ }, null, 2) });
65645
65872
  }
65646
65873
  validate() {
65647
65874
  return {
@@ -84689,8 +84916,8 @@ var DevinSubagent = class extends ToolSubagent {
84689
84916
  * Derive the subagent name from this instance's relative file path.
84690
84917
  *
84691
84918
  * The tool-side layout is `<name>/AGENT.md`, so the name is the parent
84692
- * directory of the file. If the path is unexpectedly flat (e.g. a legacy
84693
- * `<name>.md`), fall back to the basename without extension.
84919
+ * directory of the file. Devin also reads a flat `<name>.md`, which import
84920
+ * picks up too; its name is the basename without extension.
84694
84921
  */
84695
84922
  getSubagentName() {
84696
84923
  const relativeFilePath = this.getRelativeFilePath();
@@ -84783,8 +85010,9 @@ var DevinSubagent = class extends ToolSubagent {
84783
85010
  /**
84784
85011
  * Subagent inside a Devin plugin bundle (`<plugin>/agents/<name>/AGENT.md`).
84785
85012
  * Plugin subagents use the same profile format as project ones and are
84786
- * exposed as `<plugin>:<name>`. Devin also reads a flat `agents/<name>.md`;
84787
- * the directory form is written to match the `devin` target.
85013
+ * exposed as `<plugin>:<name>`. Devin also reads a flat `agents/<name>.md`,
85014
+ * which import picks up too; the directory form is written to match the
85015
+ * `devin` target.
84788
85016
  *
84789
85017
  * @see https://docs.devin.ai/cli/extensibility/plugins/overview
84790
85018
  */
@@ -88346,6 +88574,17 @@ var ZoocodeSubagent = class extends RooSubagent {
88346
88574
  };
88347
88575
  //#endregion
88348
88576
  //#region src/features/subagents/subagents-processor.ts
88577
+ /**
88578
+ * The globs import scans for a tool: its import-only patterns when it reads
88579
+ * more layouts than Rulesync writes, otherwise the generation pattern.
88580
+ */
88581
+ function importFilePatternsOf(meta) {
88582
+ return meta.importFilePatterns ?? meta.filePattern;
88583
+ }
88584
+ /** Whether unparseable files are left out of this import or orphan sweep. */
88585
+ function skipsInvalidFiles(meta, forDeletion) {
88586
+ return meta.skipInvalidFiles === true || !forDeletion && meta.skipInvalidImportFiles === true;
88587
+ }
88349
88588
  const SubagentsProcessorToolTargetSchema = z.enum(subagentsProcessorToolTargetTuple);
88350
88589
  /**
88351
88590
  * Factory Map mapping tool targets to their subagent factories.
@@ -88542,7 +88781,9 @@ const toolSubagentFactories = /* @__PURE__ */ new Map([
88542
88781
  supportsProject: true,
88543
88782
  supportsSimulated: false,
88544
88783
  supportsGlobal: true,
88545
- filePattern: "*/AGENT.md"
88784
+ filePattern: "*/AGENT.md",
88785
+ importFilePatterns: ["*.md", "*/AGENT.md"],
88786
+ skipInvalidImportFiles: true
88546
88787
  }
88547
88788
  }],
88548
88789
  ["devin-plugin", {
@@ -88551,7 +88792,9 @@ const toolSubagentFactories = /* @__PURE__ */ new Map([
88551
88792
  supportsProject: true,
88552
88793
  supportsSimulated: false,
88553
88794
  supportsGlobal: false,
88554
- filePattern: "*/AGENT.md"
88795
+ filePattern: "*/AGENT.md",
88796
+ importFilePatterns: ["*.md", "*/AGENT.md"],
88797
+ skipInvalidImportFiles: true
88555
88798
  }
88556
88799
  }],
88557
88800
  ["factorydroid", {
@@ -89030,6 +89273,7 @@ var SubagentsProcessor = class extends FeatureProcessor {
89030
89273
  const factory = this.getFactory(this.toolTarget);
89031
89274
  const paths = factory.class.getSettablePaths({ global: this.global });
89032
89275
  const roots = forDeletion ? [paths.relativeDirPath] : [paths.relativeDirPath, ...paths.importDirPaths ?? []];
89276
+ const filePatterns = forDeletion ? factory.meta.filePattern : importFilePatternsOf(factory.meta);
89033
89277
  const toolSubagents = [];
89034
89278
  const claimedRelativeFilePaths = /* @__PURE__ */ new Map();
89035
89279
  const claimedImportIdentities = new ClaimedIdentities();
@@ -89046,7 +89290,7 @@ var SubagentsProcessor = class extends FeatureProcessor {
89046
89290
  this.logger.warn(`Skipping the orphan sweep for ${quoteForLog(baseDir)}; nothing under it is deleted: ${stripControlCharacters(formatError(error))}`);
89047
89291
  continue;
89048
89292
  }
89049
- const subagentFilePaths = (await findFilesByGlobs(factory.meta.filePattern, {
89293
+ const subagentFilePaths = (await findFilesByGlobs(filePatterns, {
89050
89294
  cwd: baseDir,
89051
89295
  followSymbolicLinks: !forDeletion
89052
89296
  })).toSorted((left, right) => compareRelativePathsShallowestFirst(relative(baseDir, left), relative(baseDir, right)));
@@ -89060,7 +89304,7 @@ var SubagentsProcessor = class extends FeatureProcessor {
89060
89304
  })));
89061
89305
  ownedFilePaths = subagentFilePaths.filter((_, index) => ownership[index]);
89062
89306
  }
89063
- const parsedFiles = factory.meta.skipInvalidFiles ? (await Promise.all(ownedFilePaths.map(async (path) => {
89307
+ const parsedFiles = skipsInvalidFiles(factory.meta, forDeletion) ? (await Promise.all(ownedFilePaths.map(async (path) => {
89064
89308
  try {
89065
89309
  return {
89066
89310
  path,
@@ -91936,6 +92180,85 @@ var CodebuddyRule = class CodebuddyRule extends ToolRule {
91936
92180
  }
91937
92181
  };
91938
92182
  //#endregion
92183
+ //#region src/features/rules/codebuff-rule.ts
92184
+ /**
92185
+ * Rule generator for Codebuff (Freebuff), global scope only.
92186
+ *
92187
+ * Codebuff reads one user-level instructions file from the home directory:
92188
+ * the first hidden file matching `AGENTS.md` or `CLAUDE.md` (case-insensitive,
92189
+ * in that priority order), so the root rule is written to `~/.AGENTS.md`. There
92190
+ * is no user-level rules directory, so non-root rules fold into that file.
92191
+ *
92192
+ * Project scope is not supported here: Codebuff reads the project-root
92193
+ * `AGENTS.md`, which the `agentsmd` target already writes. The RulesProcessor
92194
+ * keeps this target out of project mode (`supportsProject: false`), so the
92195
+ * settable paths are the home-directory ones regardless of `global`.
92196
+ *
92197
+ * @see https://github.com/CodebuffAI/freebuff/blob/main/sdk/src/run-state.ts
92198
+ * @see https://github.com/CodebuffAI/freebuff/blob/main/common/src/constants/knowledge.ts
92199
+ */
92200
+ var CodebuffRule = class CodebuffRule extends ToolRule {
92201
+ static getSettablePaths(_options = {}) {
92202
+ return { root: {
92203
+ relativeDirPath: ".",
92204
+ relativeFilePath: CODEBUFF_GLOBAL_RULE_FILE_NAME
92205
+ } };
92206
+ }
92207
+ static async fromFile({ outputRoot = process.cwd(), relativeFilePath, validate = true }) {
92208
+ const { root } = this.getSettablePaths({ global: true });
92209
+ if (relativeFilePath !== root.relativeFilePath) throw new Error(`CodebuffRule only supports the root rule: ${relativeFilePath}`);
92210
+ const fileContent = await readFileContent(join(outputRoot, root.relativeDirPath, root.relativeFilePath));
92211
+ return new CodebuffRule({
92212
+ outputRoot,
92213
+ relativeDirPath: root.relativeDirPath,
92214
+ relativeFilePath: root.relativeFilePath,
92215
+ fileContent,
92216
+ validate,
92217
+ root: true
92218
+ });
92219
+ }
92220
+ static fromRulesyncRule({ outputRoot = process.cwd(), rulesyncRule, validate = true }) {
92221
+ const { root } = this.getSettablePaths({ global: true });
92222
+ const frontmatter = rulesyncRule.getFrontmatter();
92223
+ return new CodebuffRule({
92224
+ outputRoot,
92225
+ relativeDirPath: root.relativeDirPath,
92226
+ relativeFilePath: root.relativeFilePath,
92227
+ fileContent: rulesyncRule.getBody(),
92228
+ validate,
92229
+ root: frontmatter.root ?? false,
92230
+ description: frontmatter.description,
92231
+ globs: frontmatter.globs
92232
+ });
92233
+ }
92234
+ toRulesyncRule() {
92235
+ return this.toRulesyncRuleDefault();
92236
+ }
92237
+ validate() {
92238
+ return {
92239
+ success: true,
92240
+ error: null
92241
+ };
92242
+ }
92243
+ static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
92244
+ const { root } = this.getSettablePaths({ global: true });
92245
+ return new CodebuffRule({
92246
+ outputRoot,
92247
+ relativeDirPath,
92248
+ relativeFilePath,
92249
+ fileContent: "",
92250
+ validate: false,
92251
+ root: relativeDirPath === root.relativeDirPath && relativeFilePath === root.relativeFilePath
92252
+ });
92253
+ }
92254
+ static isTargetedByRulesyncRule(rulesyncRule) {
92255
+ return this.isTargetedByRulesyncRuleDefault({
92256
+ rulesyncRule,
92257
+ toolTarget: "codebuff"
92258
+ });
92259
+ }
92260
+ };
92261
+ //#endregion
91939
92262
  //#region src/features/rules/codewhale-rule.ts
91940
92263
  /**
91941
92264
  * Rule generator for Codewhale.
@@ -93573,7 +93896,7 @@ var DshRule = class extends NestedAgentsmdRule {
93573
93896
  * `getExtraFixedFiles`): Droid loads "direct `.md` children of each
93574
93897
  * `output-styles` directory".
93575
93898
  */
93576
- const OUTPUT_STYLE_FILE_GLOB = "*.md";
93899
+ const OUTPUT_STYLE_FILE_GLOB$1 = "*.md";
93577
93900
  /**
93578
93901
  * Rule generator for Factory Droid.
93579
93902
  *
@@ -93695,7 +94018,7 @@ var FactorydroidRule = class FactorydroidRule extends ToolRule {
93695
94018
  static getExtraFixedFiles({ global = false } = {}) {
93696
94019
  return [...this.getChannelPaths({ global }).map(({ path }) => path), {
93697
94020
  relativeDirPath: this.getOutputStylesDirPath(),
93698
- relativeFilePath: OUTPUT_STYLE_FILE_GLOB,
94021
+ relativeFilePath: OUTPUT_STYLE_FILE_GLOB$1,
93699
94022
  importOnly: true
93700
94023
  }];
93701
94024
  }
@@ -95550,6 +95873,12 @@ const UNIVERSAL_GLOBS$1 = /* @__PURE__ */ new Set([
95550
95873
  * Any other mode is kept in the `qoder` section so it survives a round trip.
95551
95874
  */
95552
95875
  const INFERABLE_TRIGGERS = /* @__PURE__ */ new Set(["always_on", "glob"]);
95876
+ /**
95877
+ * The file-name glob `getExtraFixedFiles` returns for the output-styles
95878
+ * directory, so import enumerates every style in it (never deletion: the
95879
+ * directory also holds hand-written styles).
95880
+ */
95881
+ const OUTPUT_STYLE_FILE_GLOB = "*.md";
95553
95882
  function normalizeGlobList(value) {
95554
95883
  if (value === void 0) return [];
95555
95884
  return (typeof value === "string" ? splitBraceAwareList(value) : value).map((glob) => glob.trim()).filter((glob) => glob !== "");
@@ -95583,22 +95912,59 @@ function resolveTrigger(frontmatter) {
95583
95912
  * A `qoder.trigger` (for example `model_decision` or `manual`) always takes
95584
95913
  * precedence and round-trips through import.
95585
95914
  *
95915
+ * A non-root rule with `qoder.channel: output-style` becomes a custom output
95916
+ * style instead, `.qoder/output-styles/<rule file name>.md` (both scopes),
95917
+ * with the rule's `description` and optional `qoder.name` as the style's
95918
+ * `description` / `name` frontmatter.
95919
+ *
95586
95920
  * @see https://docs.qoder.com/en/cli/04-extending-qoder-cli/memory
95921
+ * @see https://docs.qoder.com/cli/output-styles
95587
95922
  */
95588
95923
  var QoderRule = class QoderRule extends ToolRule {
95589
95924
  frontmatter;
95590
95925
  body;
95591
- constructor({ frontmatter, body, ...rest }) {
95926
+ outputStyle;
95927
+ constructor({ frontmatter, body, outputStyle = false, ...rest }) {
95592
95928
  if (rest.validate) {
95593
95929
  const result = QoderRuleFrontmatterSchema.safeParse(frontmatter);
95594
95930
  if (!result.success) throw new Error(`Invalid frontmatter in ${join(rest.relativeDirPath, rest.relativeFilePath)}: ${formatError(result.error)}`);
95595
95931
  }
95596
95932
  super({
95597
95933
  ...rest,
95598
- fileContent: rest.root ? body : stringifyFrontmatter(body, frontmatter)
95934
+ fileContent: rest.root || outputStyle && Object.keys(frontmatter).length === 0 ? body : stringifyFrontmatter(body, frontmatter)
95599
95935
  });
95600
95936
  this.frontmatter = frontmatter;
95601
95937
  this.body = body;
95938
+ this.outputStyle = outputStyle;
95939
+ }
95940
+ /**
95941
+ * Whether the given path is a custom output style: a direct `.md` child of
95942
+ * `.qoder/output-styles/`, at either scope.
95943
+ */
95944
+ static isOutputStylePath({ relativeDirPath, relativeFilePath }) {
95945
+ return relativeDirPath === QODER_OUTPUT_STYLES_DIR_PATH && extname(relativeFilePath) === ".md" && basename(relativeFilePath) === relativeFilePath;
95946
+ }
95947
+ /** Whether a rule opts into the `output-style` channel (non-root only). */
95948
+ static isOutputStyleRule(rulesyncRule) {
95949
+ const frontmatter = rulesyncRule.getFrontmatter();
95950
+ return !frontmatter.root && frontmatter.qoder?.channel === "output-style";
95951
+ }
95952
+ /**
95953
+ * The output-styles directory, enumerated for import only: it also holds
95954
+ * styles users write by hand (including personal ones in
95955
+ * `~/.qoder/output-styles/`) that rulesync cannot tell from its own, so
95956
+ * `--delete` leaves it alone.
95957
+ */
95958
+ static getExtraFixedFiles(_params = {}) {
95959
+ return [{
95960
+ relativeDirPath: QODER_OUTPUT_STYLES_DIR_PATH,
95961
+ relativeFilePath: OUTPUT_STYLE_FILE_GLOB,
95962
+ importOnly: true
95963
+ }];
95964
+ }
95965
+ /** Qoder loads output styles itself; they are not rules to reference. */
95966
+ isExcludedFromRootReferences() {
95967
+ return this.outputStyle;
95602
95968
  }
95603
95969
  static getSettablePaths({ global = false, excludeToolDir } = {}) {
95604
95970
  return {
@@ -95611,6 +95977,23 @@ var QoderRule = class QoderRule extends ToolRule {
95611
95977
  }
95612
95978
  static async fromFile({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath, validate = true, global = false }) {
95613
95979
  const paths = this.getSettablePaths({ global });
95980
+ if (this.isOutputStylePath({
95981
+ relativeDirPath,
95982
+ relativeFilePath
95983
+ })) {
95984
+ const filePath = join(outputRoot, QODER_OUTPUT_STYLES_DIR_PATH, relativeFilePath);
95985
+ const { frontmatter, body } = parseFrontmatter(await readFileContent(filePath), filePath);
95986
+ return new QoderRule({
95987
+ outputRoot,
95988
+ relativeDirPath: QODER_OUTPUT_STYLES_DIR_PATH,
95989
+ relativeFilePath,
95990
+ frontmatter,
95991
+ body: body.trim(),
95992
+ validate,
95993
+ root: false,
95994
+ outputStyle: true
95995
+ });
95996
+ }
95614
95997
  if (relativeDirPath !== void 0 ? relativeDirPath !== QODER_RULES_DIR_PATH : relativeFilePath === paths.root.relativeFilePath) {
95615
95998
  const rootDirPath = relativeDirPath ?? paths.root.relativeDirPath;
95616
95999
  const fileContent = await readFileContent(join(outputRoot, rootDirPath, paths.root.relativeFilePath));
@@ -95643,6 +96026,11 @@ var QoderRule = class QoderRule extends ToolRule {
95643
96026
  const root = rulesyncFrontmatter.root ?? false;
95644
96027
  const paths = this.getSettablePaths({ global });
95645
96028
  const body = rulesyncRule.getBody();
96029
+ if (this.isOutputStyleRule(rulesyncRule)) return this.buildOutputStyle({
96030
+ outputRoot,
96031
+ rulesyncRule,
96032
+ validate
96033
+ });
95646
96034
  if (root) return new QoderRule({
95647
96035
  outputRoot,
95648
96036
  relativeDirPath: paths.root.relativeDirPath,
@@ -95662,8 +96050,32 @@ var QoderRule = class QoderRule extends ToolRule {
95662
96050
  root: false
95663
96051
  });
95664
96052
  }
96053
+ /**
96054
+ * Write an `output-style` rule as `.qoder/output-styles/<name>.md`. The file
96055
+ * name is the rule's own, so a rule nested under `.rulesync/rules/` is
96056
+ * refused: Qoder reads only the files directly in `output-styles/`.
96057
+ */
96058
+ static buildOutputStyle({ outputRoot, rulesyncRule, validate }) {
96059
+ const { description, qoder } = rulesyncRule.getFrontmatter();
96060
+ const sourcePath = rulesyncRule.getRelativeFilePath();
96061
+ if (basename(sourcePath) !== sourcePath) throw new Error(`Rule '${sourcePath}' sets qoder.channel: output-style but is nested in a subdirectory: Qoder reads only the files directly in .qoder/output-styles/, so move it to the top of ${RULESYNC_RULES_RELATIVE_DIR_PATH}.`);
96062
+ const name = qoder?.name;
96063
+ return new QoderRule({
96064
+ outputRoot,
96065
+ relativeDirPath: QODER_OUTPUT_STYLES_DIR_PATH,
96066
+ relativeFilePath: `${basename(sourcePath, extname(sourcePath))}.md`,
96067
+ frontmatter: {
96068
+ ...name !== void 0 && { name },
96069
+ ...description !== void 0 && { description }
96070
+ },
96071
+ body: rulesyncRule.getBody(),
96072
+ validate,
96073
+ root: false,
96074
+ outputStyle: true
96075
+ });
96076
+ }
95665
96077
  static buildFrontmatter(rulesyncFrontmatter, relativeFilePath) {
95666
- const { trigger: storedTrigger, glob: storedGlob, ...section } = rulesyncFrontmatter.qoder ?? {};
96078
+ const { trigger: storedTrigger, glob: storedGlob, channel: _channel, name: _name, ...section } = rulesyncFrontmatter.qoder ?? {};
95667
96079
  const specificGlobs = normalizeGlobList(storedGlob ?? rulesyncFrontmatter.globs).filter((glob) => !UNIVERSAL_GLOBS$1.has(glob));
95668
96080
  let trigger = storedTrigger ?? (specificGlobs.length > 0 ? "glob" : "always_on");
95669
96081
  const description = section.description ?? rulesyncFrontmatter.description;
@@ -95681,6 +96093,25 @@ var QoderRule = class QoderRule extends ToolRule {
95681
96093
  }
95682
96094
  toRulesyncRule() {
95683
96095
  if (this.isRoot()) return this.toRulesyncRuleDefault();
96096
+ if (this.outputStyle) {
96097
+ const { name, description } = this.frontmatter;
96098
+ return new RulesyncRule({
96099
+ outputRoot: this.getOutputRoot(),
96100
+ relativeDirPath: RULESYNC_RULES_RELATIVE_DIR_PATH,
96101
+ relativeFilePath: this.getRelativeFilePath(),
96102
+ frontmatter: {
96103
+ root: false,
96104
+ targets: ["qoder"],
96105
+ ...typeof description === "string" && { description },
96106
+ qoder: {
96107
+ channel: "output-style",
96108
+ ...typeof name === "string" && name.trim() !== "" && { name }
96109
+ }
96110
+ },
96111
+ body: this.body,
96112
+ validate: true
96113
+ });
96114
+ }
95684
96115
  const trigger = resolveTrigger(this.frontmatter);
95685
96116
  const { trigger: _trigger, alwaysApply: _alwaysApply, description, glob, paths, ...unmapped } = this.frontmatter;
95686
96117
  const fileGlobs = normalizeGlobList(glob ?? paths);
@@ -95729,6 +96160,10 @@ var QoderRule = class QoderRule extends ToolRule {
95729
96160
  return this.body;
95730
96161
  }
95731
96162
  static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
96163
+ const outputStyle = this.isOutputStylePath({
96164
+ relativeDirPath,
96165
+ relativeFilePath
96166
+ });
95732
96167
  return new QoderRule({
95733
96168
  outputRoot,
95734
96169
  relativeDirPath,
@@ -95736,7 +96171,8 @@ var QoderRule = class QoderRule extends ToolRule {
95736
96171
  frontmatter: {},
95737
96172
  body: "",
95738
96173
  validate: false,
95739
- root: relativeDirPath !== QODER_RULES_DIR_PATH && relativeFilePath === "AGENTS.md"
96174
+ root: !outputStyle && relativeDirPath !== QODER_RULES_DIR_PATH && relativeFilePath === "AGENTS.md",
96175
+ outputStyle
95740
96176
  });
95741
96177
  }
95742
96178
  static isTargetedByRulesyncRule(rulesyncRule) {
@@ -97423,6 +97859,16 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
97423
97859
  localRootFileName: CODEBUDDY_LOCAL_RULE_FILE_NAME
97424
97860
  }
97425
97861
  }],
97862
+ ["codebuff", {
97863
+ class: CodebuffRule,
97864
+ meta: {
97865
+ extension: "md",
97866
+ supportsProject: false,
97867
+ supportsGlobal: true,
97868
+ ruleDiscoveryMode: "auto",
97869
+ collisionPolicy: "fold"
97870
+ }
97871
+ }],
97426
97872
  ["codewhale", {
97427
97873
  class: CodewhaleRule,
97428
97874
  meta: {
@@ -97853,7 +98299,9 @@ const toolRuleFactories = /* @__PURE__ */ new Map([
97853
98299
  }]
97854
98300
  ]);
97855
98301
  const allToolTargetKeys = [...toolRuleFactories.keys()];
97856
- const rulesProcessorToolTargets = allToolTargetKeys;
98302
+ const rulesProcessorToolTargets = allToolTargetKeys.filter((target) => {
98303
+ return toolRuleFactories.get(target)?.meta.supportsProject !== false;
98304
+ });
97857
98305
  const rulesProcessorToolTargetsGlobal = allToolTargetKeys.filter((target) => {
97858
98306
  return toolRuleFactories.get(target)?.meta.supportsGlobal ?? false;
97859
98307
  });
@@ -97932,7 +98380,7 @@ var RulesProcessor = class extends FeatureProcessor {
97932
98380
  const factory = this.getFactory(this.toolTarget);
97933
98381
  const { meta } = factory;
97934
98382
  this.warnForIncludeRootOption(meta);
97935
- this.warnForFactorydroidRuleOptions({
98383
+ this.warnForOutputStyleRuleOptions({
97936
98384
  rules: alignedRules,
97937
98385
  factory
97938
98386
  });
@@ -98166,22 +98614,25 @@ var RulesProcessor = class extends FeatureProcessor {
98166
98614
  }) !== "auto") this.logger.warn("The rules option `ruleDiscoveryMode` has no effect with `includeRoot: false` on claudecode: the rule references it adds are written into CLAUDE.md, which is not generated.");
98167
98615
  }
98168
98616
  /**
98169
- * Warn about `factorydroid` frontmatter that does not do what it looks like
98170
- * it does: an `output-style` rule that also targets other tools reaches them
98171
- * as an ordinary rule (so presentation instructions end up in their project
98172
- * rules), and `factorydroid.name` is read only with `channel: output-style`.
98173
- * Checked while generating for factorydroid only, so each warning prints once.
98617
+ * Warn about `factorydroid` / `qoder` frontmatter that does not do what it
98618
+ * looks like it does: an `output-style` rule that also targets other tools
98619
+ * reaches them as an ordinary rule (so presentation instructions end up in
98620
+ * their project rules), and `<tool>.name` is read only with
98621
+ * `channel: output-style`. Checked while generating for that tool only, so
98622
+ * each warning prints once.
98174
98623
  */
98175
- warnForFactorydroidRuleOptions({ rules, factory }) {
98176
- if (this.toolTarget !== "factorydroid") return;
98624
+ warnForOutputStyleRuleOptions({ rules, factory }) {
98625
+ const toolTarget = this.toolTarget;
98626
+ if (toolTarget !== "factorydroid" && toolTarget !== "qoder") return;
98177
98627
  for (const rule of rules) {
98178
98628
  const frontmatter = rule.getFrontmatter();
98179
- if (frontmatter.root || !frontmatter.factorydroid || !factory.class.isTargetedByRulesyncRule(rule)) continue;
98180
- const { channel, name } = frontmatter.factorydroid;
98629
+ const section = frontmatter[toolTarget];
98630
+ if (frontmatter.root || !section || !factory.class.isTargetedByRulesyncRule(rule)) continue;
98631
+ const { channel, name } = section;
98181
98632
  const fileName = stripControlCharacters(rule.getRelativeFilePath());
98182
98633
  const targets = frontmatter.targets ?? ["*"];
98183
- if (channel === "output-style" && targets.some((target) => target !== "factorydroid")) this.logger.warn(`${fileName} sets factorydroid.channel: output-style but also targets other tools, which receive it as an ordinary rule. Set \`targets: ["factorydroid"]\` to keep the output style to Factory Droid.`);
98184
- if (channel !== "output-style" && name !== void 0) this.logger.warn(`${fileName} sets factorydroid.name, which is read only with factorydroid.channel: output-style; ignoring it.`);
98634
+ 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"}.`);
98635
+ 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.`);
98185
98636
  }
98186
98637
  }
98187
98638
  /**
@@ -101926,6 +102377,6 @@ async function importChecksCore(params) {
101926
102377
  return writtenCount;
101927
102378
  }
101928
102379
  //#endregion
101929
- export { RulesyncHooks as $, removeFile as $t, groupSpellingsByCaseFoldedIdentity as A, RULESYNC_RELATIVE_DIR_PATH as An, applyFileMode as At, QWENCODE_LOCAL_RULE_FILE_NAME as B, DEPRECATED_FEATURE_REPLACEMENTS as Bn, getFileSize as Bt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as C, RULESYNC_IGNORE_RELATIVE_FILE_PATH as Cn, fallbackLogger as Ct, FACTORYDROID_DIR as D, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Dn, withWarnOnceScope as Dt, ChecksProcessor as E, RULESYNC_MCP_SCHEMA_URL as En, resetRunWarningState as Et, CLAUDECODE_LOCAL_RULE_FILE_NAME as F, RULESYNC_USER_CONFIG_DIR_NAME as Fn, createTempDirectory as Ft, RulesyncSubagentFrontmatterSchema as G, quoteForLog as Gn, listDirectoryEntryNames as Gt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as H, truncateText as Hn, isFileNotFoundError as Ht, CLAUDECODE_MEMORIES_DIR_NAME as I, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as In, directoryExists as It, RulesyncRule as J, stripHiddenCharacters as Jn, pathEscapesRoot as Jt, RulesyncSkill as K, stripControlCharacters as Kn, listFilePathsRecursively as Kt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as L, parseCommaSeparatedList as Ln, ensureDir as Lt, CODEXCLI_DIR as M, RULESYNC_SKILLS_RELATIVE_DIR_PATH as Mn, assertTreeContainsNoSymlinks as Mt, CLAUDECODE_AGENT_MEMORY_LOCAL_DIR_NAME as N, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Nn, assertWritablePathInsideRoot as Nt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as O, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as On, CLIError as Ot, CLAUDECODE_DIR as P, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Pn, checkPathTraversal as Pt, RulesyncIgnore as Q, removeDirectoryStrict as Qt, CLAUDECODE_SKILLS_DIR_PATH as R, ALL_FEATURES as Rn, fileExists as Rt, COMMANDCODE_DIR as S, RULESYNC_HOOKS_RELATIVE_FILE_PATH as Sn, WarningCollectingLogger as St, CODEBUDDY_LOCAL_RULE_FILE_NAME as T, RULESYNC_MCP_RELATIVE_FILE_PATH as Tn, withFallbackLoggerTarget as Tt, getLocalSkillDirNames as U, hasDeceptiveHiddenCharacters as Un, isFileSystemError as Ut, AUGMENTCODE_DIR as V, formatError as Vn, getHomeDirectory as Vt, RulesyncSubagent as W, hasEnclosingMarkOutsideKeycap as Wn, isSymlink as Wt, RulesyncPermissions as X, readFileContentOrNull as Xt, RulesyncRuleFrontmatterSchema as Y, readFileContent as Yt, RulesyncMcp as Z, removeDirectory as Zt, LETTACODE_DIR as _, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as _n, SourceEntrySchema as _t, getProcessorRegistryEntry as a, toPosixPath as an, RulesyncCheck as at, CommandsProcessor as b, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as bn, ConsoleLogger as bt, RulesProcessor as c, ALL_TOOL_TARGETS as cn, loadYaml as ct, ELLIPSIS_WIDTH as d, ToolTargetSchema as dn, ConfigResolver as dt, removeFileStrict as en, getRulesyncSourceCandidates as et, displayWidthOf as f, CURATED_RULES_FEATURE_SUBDIR as fn, mergeInputRootConfigs as ft, HooksProcessor as g, RULESYNC_CHECKS_RELATIVE_DIR_PATH as gn, GITIGNORE_DESTINATION_KEY as gt, IgnoreProcessor as h, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as hn, ConfigFileSchema as ht, inspectInputRoots as i, splitPathSegments as in, RulesyncCommandFrontmatterSchema as it, CODEXCLI_BASH_RULES_FILE_NAME as j, RULESYNC_RULES_RELATIVE_DIR_PATH as jn, assertDirectoryIfExists as jt, caseFoldIdentity as k, RULESYNC_PERMISSIONS_SCHEMA_URL as kn, ErrorCodes as kt, SubagentsProcessor as l, ALL_TOOL_TARGETS_WITH_WILDCARD as ln, SHARED_USER_MANAGED_CONFIG_PATHS as lt, McpProcessor as m, MAX_FILE_SIZE as mn, CONFLICTING_TARGET_PAIRS as mt, formatSourceLoadFailure as n, resolvePath as nn, parseJsonc as nt, convertFromTool as o, writeFileBuffer as on, RulesyncCheckFrontmatterSchema as ot, shortenToWidth as p, FEATURE_SOURCE_TREE_ENTRIES as pn, resolveEffectiveInputRoots as pt, RulesyncSkillFrontmatterSchema as q, stripControlCharactersKeepingLineFeeds as qn, listSubdirectoryNames as qt, generate as r, runWithDirectoryRollback as rn, RulesyncCommand as rt, isPackagingToolTarget as s, writeFileContent as sn, stringifyFrontmatter as st, importFromTool as t, removeTempDirectory as tn, resolveRulesyncSourceWritePath as tt, SkillsProcessor as u, PACKAGING_TOOL_TARGETS as un, SKILL_FILE_NAME as ut, LETTACODE_SETTINGS_LOCAL_FILE_NAME as v, RULESYNC_CONFIG_RELATIVE_FILE_PATH as vn, assertTargetsFeaturesExclusive as vt, CODEBUDDY_DIR as w, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as wn, warnOnConflictingFlags as wt, REASONIX_LOCAL_RULE_FILE_NAME as x, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as xn, JsonLogger as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_CONFIG_SCHEMA_URL as yn, findControlCharacter as yt, QWENCODE_DIR as z, ALL_FEATURES_WITH_WILDCARD as zn, findFilesByGlobs as zt };
102380
+ export { RulesyncHooks as $, removeDirectoryStrict as $t, groupSpellingsByCaseFoldedIdentity as A, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as An, applyFileMode as At, QWENCODE_LOCAL_RULE_FILE_NAME as B, ALL_FEATURES as Bn, getFileSize as Bt, COMMANDCODE_SETTINGS_LOCAL_FILE_NAME as C, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as Cn, fallbackLogger as Ct, FACTORYDROID_DIR as D, RULESYNC_MCP_RELATIVE_FILE_PATH as Dn, withWarnOnceScope as Dt, ChecksProcessor as E, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as En, resetRunWarningState as Et, CLAUDECODE_LOCAL_RULE_FILE_NAME as F, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Fn, createTempDirectory as Ft, RulesyncSubagentFrontmatterSchema as G, hasDeceptiveHiddenCharacters as Gn, isSymlink as Gt, AUGMENTCODE_SETTINGS_LOCAL_FILE_NAME as H, DEPRECATED_FEATURE_REPLACEMENTS as Hn, hasExecutableBit as Ht, CLAUDECODE_MEMORIES_DIR_NAME as I, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as In, directoryExists as It, RulesyncRule as J, stripControlCharacters as Jn, listSubdirectoryNames as Jt, RulesyncSkill as K, hasEnclosingMarkOutsideKeycap as Kn, listDirectoryEntryNames as Kt, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as L, RULESYNC_USER_CONFIG_DIR_NAME as Ln, ensureDir as Lt, CODEXCLI_DIR as M, RULESYNC_RELATIVE_DIR_PATH as Mn, assertTreeContainsNoSymlinks as Mt, CLAUDECODE_AGENT_MEMORY_LOCAL_DIR_NAME as N, RULESYNC_RULES_RELATIVE_DIR_PATH as Nn, assertWritablePathInsideRoot as Nt, FACTORYDROID_SETTINGS_LOCAL_FILE_NAME as O, RULESYNC_MCP_SCHEMA_URL as On, CLIError as Ot, CLAUDECODE_DIR as P, RULESYNC_SKILLS_RELATIVE_DIR_PATH as Pn, checkPathTraversal as Pt, RulesyncIgnore as Q, removeDirectory as Qt, CLAUDECODE_SKILLS_DIR_PATH as R, RULESYNC_XDG_CONFIG_HOME_DEFAULT_DIR_NAME as Rn, fileExists as Rt, COMMANDCODE_DIR as S, RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH as Sn, WarningCollectingLogger as St, CODEBUDDY_LOCAL_RULE_FILE_NAME as T, RULESYNC_IGNORE_RELATIVE_FILE_PATH as Tn, withFallbackLoggerTarget as Tt, getLocalSkillDirNames as U, formatError as Un, isFileNotFoundError as Ut, AUGMENTCODE_DIR as V, ALL_FEATURES_WITH_WILDCARD as Vn, getHomeDirectory as Vt, RulesyncSubagent as W, truncateText as Wn, isFileSystemError as Wt, RulesyncPermissions as X, stripHiddenCharacters as Xn, readFileContent as Xt, RulesyncRuleFrontmatterSchema as Y, stripControlCharactersKeepingLineFeeds as Yn, pathEscapesRoot as Yt, RulesyncMcp as Z, readFileContentOrNull as Zt, LETTACODE_DIR as _, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as _n, SourceEntrySchema as _t, getProcessorRegistryEntry as a, runWithDirectoryRollback as an, RulesyncCheck as at, CommandsProcessor as b, RULESYNC_CONFIG_RELATIVE_FILE_PATH as bn, ConsoleLogger as bt, RulesProcessor as c, writeFileBuffer as cn, loadYaml as ct, ELLIPSIS_WIDTH as d, ALL_TOOL_TARGETS_WITH_WILDCARD as dn, ConfigResolver as dt, removeFile as en, getRulesyncSourceCandidates as et, displayWidthOf as f, PACKAGING_TOOL_TARGETS as fn, mergeInputRootConfigs as ft, HooksProcessor as g, MAX_FILE_SIZE as gn, GITIGNORE_DESTINATION_KEY as gt, IgnoreProcessor as h, FEATURE_SOURCE_TREE_ENTRIES as hn, ConfigFileSchema as ht, inspectInputRoots as i, resolvedPathEscapesRoot as in, RulesyncCommandFrontmatterSchema as it, CODEXCLI_BASH_RULES_FILE_NAME as j, RULESYNC_PERMISSIONS_SCHEMA_URL as jn, assertDirectoryIfExists as jt, caseFoldIdentity as k, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as kn, ErrorCodes as kt, SubagentsProcessor as l, writeFileContent as ln, SHARED_USER_MANAGED_CONFIG_PATHS as lt, McpProcessor as m, CURATED_RULES_FEATURE_SUBDIR as mn, CONFLICTING_TARGET_PAIRS as mt, formatSourceLoadFailure as n, removeTempDirectory as nn, parseJsonc as nt, convertFromTool as o, splitPathSegments as on, RulesyncCheckFrontmatterSchema as ot, shortenToWidth as p, ToolTargetSchema as pn, resolveEffectiveInputRoots as pt, RulesyncSkillFrontmatterSchema as q, quoteForLog as qn, listFilePathsRecursively as qt, generate as r, resolvePath as rn, RulesyncCommand as rt, isPackagingToolTarget as s, toPosixPath as sn, stringifyFrontmatter as st, importFromTool as t, removeFileStrict as tn, resolveRulesyncSourceWritePath as tt, SkillsProcessor as u, ALL_TOOL_TARGETS as un, SKILL_FILE_NAME as ut, LETTACODE_SETTINGS_LOCAL_FILE_NAME as v, RULESYNC_CHECKS_RELATIVE_DIR_PATH as vn, assertTargetsFeaturesExclusive as vt, CODEBUDDY_DIR as w, RULESYNC_HOOKS_RELATIVE_FILE_PATH as wn, warnOnConflictingFlags as wt, REASONIX_LOCAL_RULE_FILE_NAME as x, RULESYNC_CONFIG_SCHEMA_URL as xn, JsonLogger as xt, CRUSH_LOCAL_RULE_FILE_NAME as y, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as yn, findControlCharacter as yt, QWENCODE_DIR as z, parseCommaSeparatedList as zn, findFilesByGlobs as zt };
101930
102381
 
101931
- //# sourceMappingURL=import-5-Uennl1.js.map
102382
+ //# sourceMappingURL=import-DFwX4TTE.js.map