rulesync 14.0.1 → 14.1.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.
@@ -30,6 +30,7 @@ const ALL_FEATURES_WITH_WILDCARD = [...ALL_FEATURES, "*"];
30
30
  const FeatureSchema = z.enum(ALL_FEATURES);
31
31
  z.array(FeatureSchema);
32
32
  const GitignoreDestinationSchema = z.enum(["gitignore", "gitattributes"]);
33
+ const FlattenedCommandNamingSchema = z.enum(["basename", "path"]);
33
34
  const FeatureOptionsSchema = z.record(z.string(), z.unknown());
34
35
  const FeatureValueSchema = z.union([
35
36
  z.boolean(),
@@ -322,6 +323,7 @@ const hooksProcessorToolTargetTuple = [
322
323
  "copilot",
323
324
  "copilotcli",
324
325
  "opencode",
326
+ "pi",
325
327
  "factorydroid",
326
328
  "goose",
327
329
  "hermesagent",
@@ -695,16 +697,9 @@ var BaseLogger = class {
695
697
  return this._silent;
696
698
  }
697
699
  configure({ verbose, silent }) {
698
- if (verbose && silent) {
699
- this._silent = false;
700
- if (!isEnvTest()) this.onConflictingFlags();
701
- }
702
700
  this._silent = silent;
703
701
  this._verbose = verbose && !silent;
704
702
  }
705
- onConflictingFlags() {
706
- console.warn("Both --verbose and --silent specified; --silent takes precedence");
707
- }
708
703
  };
709
704
  /**
710
705
  * ConsoleLogger - human-readable terminal output
@@ -761,7 +756,6 @@ var JsonLogger = class extends BaseLogger {
761
756
  this._commandName = command;
762
757
  this._version = version;
763
758
  }
764
- onConflictingFlags() {}
765
759
  get jsonMode() {
766
760
  return true;
767
761
  }
@@ -809,17 +803,32 @@ var JsonLogger = class extends BaseLogger {
809
803
  debug(_message, ..._args) {}
810
804
  };
811
805
  /**
806
+ * Warn once when both `--verbose` and `--silent` were passed on the command
807
+ * line. Called at CLI-flag parsing time only (`wrapCommand`), so re-configuring
808
+ * a logger from config-file values never re-triggers it. Suppressed in JSON
809
+ * mode to keep non-JSON text off stderr, matching the former JsonLogger
810
+ * behavior.
811
+ */
812
+ function warnOnConflictingFlags({ verbose, silent, jsonMode }) {
813
+ if (!verbose || !silent || jsonMode || isEnvTest()) return;
814
+ console.warn("Both --verbose and --silent specified; --silent takes precedence");
815
+ }
816
+ /**
817
+ * Shared fallback logger for code paths that have no command logger threaded
818
+ * through (module-level translators, `warnWithFallback(undefined, ...)`).
819
+ * `wrapCommand` configures it from CLI flags and `ConfigResolver.resolve`
820
+ * re-configures it from the resolved config, so `silent`/`verbose` settings
821
+ * are honored even on paths where the command logger is not available.
822
+ */
823
+ const fallbackLogger = new ConsoleLogger();
824
+ /**
812
825
  * Emit a warning through `logger.warn` if a logger is supplied, otherwise
813
- * fall through to `console.warn`. Centralizes the "logger may be optional"
814
- * pattern so call sites stay terse and `oxlint`'s `no-console` exception
815
- * lives in exactly one place.
826
+ * fall through to the shared `fallbackLogger`. Centralizes the "logger may
827
+ * be optional" pattern so call sites stay terse and the fallback honors the
828
+ * configured `silent` mode.
816
829
  */
817
830
  function warnWithFallback(logger, message) {
818
- if (logger) {
819
- logger.warn(message);
820
- return;
821
- }
822
- console.warn(message);
831
+ (logger ?? fallbackLogger).warn(message);
823
832
  }
824
833
  //#endregion
825
834
  //#region src/utils/validation.ts
@@ -887,6 +896,7 @@ const ConfigParamsSchema = z.object({
887
896
  simulateCommands: optional(z.boolean()),
888
897
  simulateSubagents: optional(z.boolean()),
889
898
  simulateSkills: optional(z.boolean()),
899
+ flattenedCommandNaming: optional(FlattenedCommandNamingSchema),
890
900
  gitignoreTargetsOnly: optional(z.boolean()),
891
901
  gitignoreDestination: optional(GitignoreDestinationSchema),
892
902
  dryRun: optional(z.boolean()),
@@ -964,13 +974,14 @@ var Config = class Config {
964
974
  simulateCommands;
965
975
  simulateSubagents;
966
976
  simulateSkills;
977
+ flattenedCommandNaming;
967
978
  gitignoreTargetsOnly;
968
979
  gitignoreDestination;
969
980
  dryRun;
970
981
  check;
971
982
  inputRoot;
972
983
  sources;
973
- constructor({ outputRoots, targets, features, verbose, delete: isDelete, global, silent, simulateCommands, simulateSubagents, simulateSkills, gitignoreTargetsOnly, gitignoreDestination, dryRun, check, inputRoot, sources, configFileTargets }) {
984
+ constructor({ outputRoots, targets, features, verbose, delete: isDelete, global, silent, simulateCommands, simulateSubagents, simulateSkills, flattenedCommandNaming, gitignoreTargetsOnly, gitignoreDestination, dryRun, check, inputRoot, sources, configFileTargets }) {
974
985
  assertTargetsFeaturesExclusive({
975
986
  targets,
976
987
  features
@@ -997,6 +1008,7 @@ var Config = class Config {
997
1008
  this.simulateCommands = simulateCommands ?? false;
998
1009
  this.simulateSubagents = simulateSubagents ?? false;
999
1010
  this.simulateSkills = simulateSkills ?? false;
1011
+ this.flattenedCommandNaming = flattenedCommandNaming ?? "basename";
1000
1012
  this.gitignoreTargetsOnly = gitignoreTargetsOnly ?? true;
1001
1013
  this.gitignoreDestination = gitignoreDestination ?? "gitignore";
1002
1014
  this.dryRun = dryRun ?? false;
@@ -1165,6 +1177,9 @@ var Config = class Config {
1165
1177
  getSimulateCommands() {
1166
1178
  return this.simulateCommands;
1167
1179
  }
1180
+ getFlattenedCommandNaming() {
1181
+ return this.flattenedCommandNaming;
1182
+ }
1168
1183
  getSimulateSubagents() {
1169
1184
  return this.simulateSubagents;
1170
1185
  }
@@ -1217,6 +1232,7 @@ const getDefaults = () => ({
1217
1232
  simulateCommands: false,
1218
1233
  simulateSubagents: false,
1219
1234
  simulateSkills: false,
1235
+ flattenedCommandNaming: "basename",
1220
1236
  gitignoreTargetsOnly: true,
1221
1237
  gitignoreDestination: "gitignore",
1222
1238
  dryRun: false,
@@ -1246,6 +1262,7 @@ const mergeConfigs = (baseConfig, localConfig) => {
1246
1262
  simulateCommands: localConfig.simulateCommands ?? baseConfig.simulateCommands,
1247
1263
  simulateSubagents: localConfig.simulateSubagents ?? baseConfig.simulateSubagents,
1248
1264
  simulateSkills: localConfig.simulateSkills ?? baseConfig.simulateSkills,
1265
+ flattenedCommandNaming: localConfig.flattenedCommandNaming ?? baseConfig.flattenedCommandNaming,
1249
1266
  gitignoreTargetsOnly: localConfig.gitignoreTargetsOnly ?? baseConfig.gitignoreTargetsOnly,
1250
1267
  gitignoreDestination: localConfig.gitignoreDestination ?? baseConfig.gitignoreDestination,
1251
1268
  dryRun: localConfig.dryRun ?? baseConfig.dryRun,
@@ -1324,6 +1341,26 @@ var ConfigResolver = class {
1324
1341
  validatedConfigPath,
1325
1342
  localConfigPath
1326
1343
  });
1344
+ const resolvedVerbose = pick({
1345
+ cli: verbose,
1346
+ file: configByFile.verbose,
1347
+ fallback: getDefaults().verbose
1348
+ });
1349
+ const resolvedSilent = pick({
1350
+ cli: silent,
1351
+ file: configByFile.silent,
1352
+ fallback: getDefaults().silent
1353
+ });
1354
+ if (logger !== void 0) {
1355
+ logger.configure({
1356
+ verbose: resolvedVerbose,
1357
+ silent: resolvedSilent
1358
+ });
1359
+ fallbackLogger.configure({
1360
+ verbose: resolvedVerbose,
1361
+ silent: resolvedSilent
1362
+ });
1363
+ }
1327
1364
  const resolvedInputRoot = inputRoot ?? configByFile.inputRoot;
1328
1365
  const resolvedGlobal = resolveGlobal({
1329
1366
  logger,
@@ -1340,11 +1377,7 @@ var ConfigResolver = class {
1340
1377
  return new Config({
1341
1378
  targets: resolvedTargets,
1342
1379
  features: resolvedFeatures,
1343
- verbose: pick({
1344
- cli: verbose,
1345
- file: configByFile.verbose,
1346
- fallback: getDefaults().verbose
1347
- }),
1380
+ verbose: resolvedVerbose,
1348
1381
  delete: pick({
1349
1382
  cli: isDelete,
1350
1383
  file: configByFile.delete,
@@ -1359,11 +1392,7 @@ var ConfigResolver = class {
1359
1392
  global: resolvedGlobal
1360
1393
  }),
1361
1394
  global: resolvedGlobal,
1362
- silent: pick({
1363
- cli: silent,
1364
- file: configByFile.silent,
1365
- fallback: getDefaults().silent
1366
- }),
1395
+ silent: resolvedSilent,
1367
1396
  simulateCommands: pick({
1368
1397
  cli: simulateCommands,
1369
1398
  file: configByFile.simulateCommands,
@@ -1401,6 +1430,7 @@ var ConfigResolver = class {
1401
1430
  }),
1402
1431
  inputRoot: resolvedInputRoot !== void 0 ? resolve(resolvedInputRoot) : cwd,
1403
1432
  sources: configByFile.sources ?? getDefaults().sources,
1433
+ flattenedCommandNaming: configByFile.flattenedCommandNaming ?? getDefaults().flattenedCommandNaming,
1404
1434
  configFileTargets: extractConfigFileTargets(configByFile.targets)
1405
1435
  });
1406
1436
  }
@@ -5232,12 +5262,15 @@ var OpenCodeCommand = class OpenCodeCommand extends ToolCommand {
5232
5262
  }
5233
5263
  };
5234
5264
  const PI_AGENT_DIR = join(".pi", "agent");
5265
+ const PI_AGENT_EXTENSIONS_DIR_PATH = join(PI_AGENT_DIR, "extensions");
5235
5266
  const PI_AGENT_PROMPTS_DIR_PATH = join(PI_AGENT_DIR, "prompts");
5236
5267
  const PI_AGENT_SKILLS_DIR_PATH = join(PI_AGENT_DIR, "skills");
5268
+ const PI_EXTENSIONS_DIR_PATH = join(".pi", "extensions");
5237
5269
  const PI_PROMPTS_DIR_PATH = join(".pi", "prompts");
5238
5270
  const PI_SKILLS_DIR_PATH = join(".pi", "skills");
5239
5271
  const PI_RULE_FILE_NAME = "AGENTS.md";
5240
5272
  const PI_APPEND_SYSTEM_FILE_NAME = "APPEND_SYSTEM.md";
5273
+ const PI_HOOKS_FILE_NAME = "rulesync-hooks.ts";
5241
5274
  //#endregion
5242
5275
  //#region src/features/commands/pi-command.ts
5243
5276
  /**
@@ -6415,7 +6448,8 @@ var CommandsProcessor = class extends FeatureProcessor {
6415
6448
  toolTarget;
6416
6449
  global;
6417
6450
  getFactory;
6418
- constructor({ outputRoot = process.cwd(), inputRoot = process.cwd(), toolTarget, global = false, getFactory = defaultGetFactory$5, dryRun = false, logger }) {
6451
+ flattenedCommandNaming;
6452
+ constructor({ outputRoot = process.cwd(), inputRoot = process.cwd(), toolTarget, global = false, getFactory = defaultGetFactory$5, dryRun = false, flattenedCommandNaming = "basename", logger }) {
6419
6453
  super({
6420
6454
  outputRoot,
6421
6455
  inputRoot,
@@ -6427,6 +6461,7 @@ var CommandsProcessor = class extends FeatureProcessor {
6427
6461
  this.toolTarget = result.data;
6428
6462
  this.global = global;
6429
6463
  this.getFactory = getFactory;
6464
+ this.flattenedCommandNaming = flattenedCommandNaming;
6430
6465
  }
6431
6466
  async convertRulesyncFilesToToolFiles(rulesyncFiles) {
6432
6467
  const rulesyncCommands = rulesyncFiles.filter((file) => file instanceof RulesyncCommand);
@@ -6463,8 +6498,9 @@ var CommandsProcessor = class extends FeatureProcessor {
6463
6498
  });
6464
6499
  }
6465
6500
  flattenRelativeFilePath(rulesyncCommand) {
6466
- const flatPath = basename(rulesyncCommand.getRelativeFilePath());
6467
- if (flatPath === rulesyncCommand.getRelativeFilePath()) return rulesyncCommand;
6501
+ const relativeFilePath = rulesyncCommand.getRelativeFilePath();
6502
+ const flatPath = this.flattenedCommandNaming === "path" ? toPosixPath(relativeFilePath).split("/").join("-") : basename(relativeFilePath);
6503
+ if (flatPath === relativeFilePath) return rulesyncCommand;
6468
6504
  return rulesyncCommand.withRelativeFilePath(flatPath);
6469
6505
  }
6470
6506
  safeRelativePath(basePath, fullPath) {
@@ -6790,6 +6826,26 @@ const OPENCODE_HOOK_EVENTS = [
6790
6826
  /** Hook events supported by Kilo. (Currently identical to OpenCode) */
6791
6827
  const KILO_HOOK_EVENTS = OPENCODE_HOOK_EVENTS;
6792
6828
  /**
6829
+ * Hook events supported by Pi Coding Agent, bridged through a generated
6830
+ * TypeScript extension (Pi has no static hook config file; its extension API
6831
+ * exposes lifecycle events instead).
6832
+ *
6833
+ * Only canonical events with a semantically faithful Pi extension event are
6834
+ * listed; see CANONICAL_TO_PI_EVENT_NAMES for the mapping.
6835
+ *
6836
+ * @see https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/extensions.md
6837
+ */
6838
+ const PI_HOOK_EVENTS = [
6839
+ "sessionStart",
6840
+ "sessionEnd",
6841
+ "preToolUse",
6842
+ "postToolUse",
6843
+ "preModelInvocation",
6844
+ "beforeSubmitPrompt",
6845
+ "stop",
6846
+ "preCompact"
6847
+ ];
6848
+ /**
6793
6849
  * Hook events supported by GitHub Copilot (cloud coding agent).
6794
6850
  *
6795
6851
  * GitHub now documents an eight-event surface for `.github/hooks/*.json`:
@@ -7189,6 +7245,7 @@ const HooksConfigSchema = z.looseObject({
7189
7245
  copilotcli: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
7190
7246
  opencode: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
7191
7247
  kilo: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
7248
+ pi: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
7192
7249
  factorydroid: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
7193
7250
  codexcli: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
7194
7251
  goose: z.optional(z.looseObject({ hooks: z.optional(hooksRecordSchema) })),
@@ -7369,6 +7426,31 @@ const CANONICAL_TO_OPENCODE_EVENT_NAMES = {
7369
7426
  */
7370
7427
  const CANONICAL_TO_KILO_EVENT_NAMES = CANONICAL_TO_OPENCODE_EVENT_NAMES;
7371
7428
  /**
7429
+ * Map canonical camelCase event names to Pi Coding Agent extension event
7430
+ * names (snake_case).
7431
+ *
7432
+ * Mapping notes: `sessionEnd` → `session_shutdown` (fires on session
7433
+ * teardown), `beforeSubmitPrompt` → `input` (user input interception),
7434
+ * `preModelInvocation` → `context` (fires before each LLM call), and
7435
+ * `stop` → `agent_end` (agent finished responding; unlike Claude Code's
7436
+ * Stop, this also fires before Pi auto-retries or auto-compacts —
7437
+ * `agent_settled` would skip queued follow-ups instead, a pure trade-off).
7438
+ * Pi events without a faithful canonical counterpart (e.g. `turn_start`,
7439
+ * `agent_settled`) are intentionally unmapped.
7440
+ *
7441
+ * @see https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/extensions.md
7442
+ */
7443
+ const CANONICAL_TO_PI_EVENT_NAMES = {
7444
+ sessionStart: "session_start",
7445
+ sessionEnd: "session_shutdown",
7446
+ preToolUse: "tool_call",
7447
+ postToolUse: "tool_result",
7448
+ preModelInvocation: "context",
7449
+ beforeSubmitPrompt: "input",
7450
+ stop: "agent_end",
7451
+ preCompact: "session_before_compact"
7452
+ };
7453
+ /**
7372
7454
  * Map canonical camelCase event names to Copilot camelCase.
7373
7455
  */
7374
7456
  const CANONICAL_TO_COPILOT_EVENT_NAMES = {
@@ -10799,6 +10881,176 @@ var OpencodeHooks = class OpencodeHooks extends ToolHooks {
10799
10881
  }
10800
10882
  };
10801
10883
  //#endregion
10884
+ //#region src/features/hooks/pi-extension-generator.ts
10885
+ /**
10886
+ * Pi extension events fired per tool invocation; handlers for these receive
10887
+ * `event.toolName` and honor the canonical hook `matcher` as a regex on it.
10888
+ */
10889
+ const PI_TOOL_EVENTS = /* @__PURE__ */ new Set(["tool_call", "tool_result"]);
10890
+ /**
10891
+ * Validate a hook matcher as a regular expression and return it as a JS
10892
+ * string-literal (JSON.stringify quoting) safe to embed in generated code.
10893
+ */
10894
+ function matcherToEmbeddedLiteral(matcher) {
10895
+ let sanitized = matcher;
10896
+ for (const char of CONTROL_CHARS) sanitized = sanitized.replaceAll(char, "");
10897
+ if (sanitized === "*") sanitized = ".*";
10898
+ try {
10899
+ new RegExp(sanitized);
10900
+ } catch {
10901
+ throw new Error(`Invalid regex pattern in hook matcher: ${sanitized}`);
10902
+ }
10903
+ return JSON.stringify(sanitized);
10904
+ }
10905
+ function collectPiHandlers({ effectiveHooks, eventMap }) {
10906
+ const handlerGroups = {};
10907
+ for (const [canonicalEvent, definitions] of Object.entries(effectiveHooks)) {
10908
+ const piEvent = eventMap[canonicalEvent];
10909
+ if (!piEvent) continue;
10910
+ const handlers = [];
10911
+ for (const def of definitions) {
10912
+ if ((def.type ?? "command") !== "command") continue;
10913
+ if (!def.command) continue;
10914
+ handlers.push({
10915
+ command: def.command,
10916
+ matcher: def.matcher ? def.matcher : void 0
10917
+ });
10918
+ }
10919
+ if (handlers.length > 0) {
10920
+ const existing = handlerGroups[piEvent];
10921
+ if (existing) existing.push(...handlers);
10922
+ else handlerGroups[piEvent] = handlers;
10923
+ }
10924
+ }
10925
+ return handlerGroups;
10926
+ }
10927
+ function buildSubscriptionLines(handlerGroups) {
10928
+ const lines = [];
10929
+ for (const [piEvent, handlers] of Object.entries(handlerGroups)) {
10930
+ const usesToolName = PI_TOOL_EVENTS.has(piEvent) && handlers.some((h) => h.matcher);
10931
+ lines.push(` pi.on(${JSON.stringify(piEvent)}, async (${usesToolName ? "event" : ""}) => {`);
10932
+ for (const handler of handlers) {
10933
+ const embeddedCommand = JSON.stringify(handler.command);
10934
+ if (usesToolName && handler.matcher) {
10935
+ lines.push(` if (new RegExp(${matcherToEmbeddedLiteral(handler.matcher)}).test(event.toolName)) {`);
10936
+ lines.push(` await run(${embeddedCommand});`);
10937
+ lines.push(" }");
10938
+ } else lines.push(` await run(${embeddedCommand});`);
10939
+ }
10940
+ lines.push(" });");
10941
+ }
10942
+ return lines;
10943
+ }
10944
+ /**
10945
+ * Generate the rulesync-owned Pi extension (a TypeScript module with a
10946
+ * default-export factory receiving Pi's ExtensionAPI) that subscribes to the
10947
+ * mapped lifecycle events and executes the configured hook commands via the
10948
+ * platform shell. The generated extension observes events only: it never
10949
+ * blocks or mutates Pi events.
10950
+ *
10951
+ * @see https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/extensions.md
10952
+ */
10953
+ function generatePiExtensionCode({ config, supportedEvents, eventMap }) {
10954
+ const supported = new Set(supportedEvents);
10955
+ const configHooks = {
10956
+ ...config.hooks,
10957
+ ...config.pi?.hooks
10958
+ };
10959
+ const effectiveHooks = {};
10960
+ for (const [event, defs] of Object.entries(configHooks)) if (supported.has(event)) effectiveHooks[event] = defs;
10961
+ const subscriptionLines = buildSubscriptionLines(collectPiHandlers({
10962
+ effectiveHooks,
10963
+ eventMap
10964
+ }));
10965
+ const lines = ["// Generated by rulesync. Do not edit manually."];
10966
+ if (subscriptionLines.length === 0) {
10967
+ lines.push("export default function () {}");
10968
+ lines.push("");
10969
+ return lines.join("\n");
10970
+ }
10971
+ lines.push("import { exec } from \"node:child_process\";");
10972
+ lines.push("import { promisify } from \"node:util\";");
10973
+ lines.push("");
10974
+ lines.push("import type { ExtensionAPI } from \"@earendil-works/pi-coding-agent\";");
10975
+ lines.push("");
10976
+ lines.push("const run = promisify(exec);");
10977
+ lines.push("");
10978
+ lines.push("export default function (pi: ExtensionAPI) {");
10979
+ lines.push(...subscriptionLines);
10980
+ lines.push("}");
10981
+ lines.push("");
10982
+ return lines.join("\n");
10983
+ }
10984
+ //#endregion
10985
+ //#region src/features/hooks/pi-hooks.ts
10986
+ /**
10987
+ * Pi Coding Agent has no static hooks config file; its extension API exposes
10988
+ * lifecycle events instead. rulesync bridges canonical hooks by generating a
10989
+ * rulesync-owned TypeScript extension in Pi's extension discovery paths:
10990
+ * `.pi/extensions/rulesync-hooks.ts` (project) and
10991
+ * `~/.pi/agent/extensions/rulesync-hooks.ts` (global).
10992
+ *
10993
+ * @see https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/extensions.md
10994
+ */
10995
+ var PiHooks = class PiHooks extends ToolHooks {
10996
+ constructor(params) {
10997
+ super({
10998
+ ...params,
10999
+ fileContent: params.fileContent ?? ""
11000
+ });
11001
+ }
11002
+ static getSettablePaths(options) {
11003
+ return {
11004
+ relativeDirPath: options?.global ? PI_AGENT_EXTENSIONS_DIR_PATH : PI_EXTENSIONS_DIR_PATH,
11005
+ relativeFilePath: PI_HOOKS_FILE_NAME
11006
+ };
11007
+ }
11008
+ static async fromFile({ outputRoot = process.cwd(), validate = true, global = false }) {
11009
+ const paths = PiHooks.getSettablePaths({ global });
11010
+ const fileContent = await readFileContent(join(outputRoot, paths.relativeDirPath, paths.relativeFilePath));
11011
+ return new PiHooks({
11012
+ outputRoot,
11013
+ relativeDirPath: paths.relativeDirPath,
11014
+ relativeFilePath: paths.relativeFilePath,
11015
+ fileContent,
11016
+ validate
11017
+ });
11018
+ }
11019
+ static fromRulesyncHooks({ outputRoot = process.cwd(), rulesyncHooks, validate = true, global = false }) {
11020
+ const fileContent = generatePiExtensionCode({
11021
+ config: rulesyncHooks.getJson(),
11022
+ supportedEvents: PI_HOOK_EVENTS,
11023
+ eventMap: CANONICAL_TO_PI_EVENT_NAMES
11024
+ });
11025
+ const paths = PiHooks.getSettablePaths({ global });
11026
+ return new PiHooks({
11027
+ outputRoot,
11028
+ relativeDirPath: paths.relativeDirPath,
11029
+ relativeFilePath: paths.relativeFilePath,
11030
+ fileContent,
11031
+ validate
11032
+ });
11033
+ }
11034
+ toRulesyncHooks() {
11035
+ throw new Error("Not implemented because Pi hooks are generated as a TypeScript extension file.");
11036
+ }
11037
+ validate() {
11038
+ return {
11039
+ success: true,
11040
+ error: null
11041
+ };
11042
+ }
11043
+ static forDeletion({ outputRoot = process.cwd(), relativeDirPath, relativeFilePath }) {
11044
+ return new PiHooks({
11045
+ outputRoot,
11046
+ relativeDirPath,
11047
+ relativeFilePath,
11048
+ fileContent: "",
11049
+ validate: false
11050
+ });
11051
+ }
11052
+ };
11053
+ //#endregion
10802
11054
  //#region src/features/hooks/qwencode-hooks.ts
10803
11055
  /**
10804
11056
  * Build a single Qwen Code hook object from a canonical hook definition.
@@ -11568,6 +11820,17 @@ const toolHooksFactories = /* @__PURE__ */ new Map([
11568
11820
  supportedHookTypes: ["command"],
11569
11821
  supportsMatcher: true
11570
11822
  }],
11823
+ ["pi", {
11824
+ class: PiHooks,
11825
+ meta: {
11826
+ supportsProject: true,
11827
+ supportsGlobal: true,
11828
+ supportsImport: false
11829
+ },
11830
+ supportedEvents: PI_HOOK_EVENTS,
11831
+ supportedHookTypes: ["command"],
11832
+ supportsMatcher: true
11833
+ }],
11571
11834
  ["factorydroid", {
11572
11835
  class: FactorydroidHooks,
11573
11836
  meta: {
@@ -19321,7 +19584,7 @@ function convertAntigravityIdeToRulesyncPermissions(params) {
19321
19584
  }
19322
19585
  //#endregion
19323
19586
  //#region src/features/permissions/augmentcode-permissions.ts
19324
- const moduleLogger$1 = new ConsoleLogger();
19587
+ const moduleLogger$1 = fallbackLogger;
19325
19588
  z.enum([
19326
19589
  "allow",
19327
19590
  "deny",
@@ -23126,7 +23389,7 @@ const QwenSettingsPermissionsSchema = z.looseObject({
23126
23389
  deny: z.optional(z.array(z.string()))
23127
23390
  });
23128
23391
  const QwenSettingsSchema = z.looseObject({ permissions: z.optional(QwenSettingsPermissionsSchema) });
23129
- const moduleLogger = new ConsoleLogger();
23392
+ const moduleLogger = fallbackLogger;
23130
23393
  /**
23131
23394
  * Mapping from rulesync canonical tool category names (lowercase) to Qwen Code tool names (PascalCase).
23132
23395
  * Unknown names pass through as-is (e.g., mcp__server__tool).
@@ -40533,7 +40796,8 @@ function buildCommandsStrategy(ctx) {
40533
40796
  toolTarget,
40534
40797
  global,
40535
40798
  dryRun,
40536
- logger
40799
+ logger,
40800
+ flattenedCommandNaming: config.getFlattenedCommandNaming()
40537
40801
  }),
40538
40802
  loadSource: (p) => p.loadToolFiles(),
40539
40803
  toRulesync: (p, files) => p.convertToolFilesToRulesyncFiles(files),
@@ -41373,6 +41637,7 @@ async function generateCommandsCore(params) {
41373
41637
  toolTarget,
41374
41638
  global: config.getGlobal(),
41375
41639
  dryRun: config.isPreviewMode(),
41640
+ flattenedCommandNaming: config.getFlattenedCommandNaming(),
41376
41641
  logger
41377
41642
  });
41378
41643
  const result = await processFeatureWithRulesyncFiles({
@@ -41896,6 +42161,6 @@ async function importChecksCore(params) {
41896
42161
  return writtenCount;
41897
42162
  }
41898
42163
  //#endregion
41899
- export { isSymlink as $, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as A, RULESYNC_PERMISSIONS_JSONC_FILE_NAME as At, findControlCharacter as B, ALL_FEATURES_WITH_WILDCARD as Bt, CommandsProcessor as C, RULESYNC_MCP_FILE_NAME as Ct, CLAUDECODE_DIR as D, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as Dt, CODEXCLI_DIR as E, RULESYNC_MCP_SCHEMA_URL as Et, RulesyncCheck as F, RULESYNC_SKILLS_RELATIVE_DIR_PATH as Ft, checkPathTraversal as G, JsonLogger as H, RulesyncCheckFrontmatterSchema as I, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as It, ensureDir as J, createTempDirectory as K, stringifyFrontmatter as L, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as Lt, RulesyncCommand as M, RULESYNC_PERMISSIONS_SCHEMA_URL as Mt, RulesyncCommandFrontmatterSchema as N, RULESYNC_RELATIVE_DIR_PATH as Nt, CLAUDECODE_LOCAL_RULE_FILE_NAME as O, RULESYNC_OVERVIEW_FILE_NAME as Ot, ChecksProcessor as P, RULESYNC_RULES_RELATIVE_DIR_PATH as Pt, getHomeDirectory as Q, loadYaml as R, formatError as Rt, RulesyncHooks as S, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as St, CODEXCLI_BASH_RULES_FILE_NAME as T, RULESYNC_MCP_RELATIVE_FILE_PATH as Tt, CLIError as U, ConsoleLogger as V, ErrorCodes as W, findFilesByGlobs as X, fileExists as Y, getFileSize as Z, McpProcessor as _, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as _t, convertFromTool as a, toPosixPath as at, RulesyncIgnore as b, RULESYNC_HOOKS_RELATIVE_FILE_PATH as bt, RulesyncRuleFrontmatterSchema as c, ALL_TOOL_TARGETS_WITH_WILDCARD as ct, RulesyncSubagentFrontmatterSchema as d, RULESYNC_AIIGNORE_FILE_NAME as dt, listDirectoryFiles as et, SkillsProcessor as f, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as ft, RulesyncPermissions as g, RULESYNC_CONFIG_SCHEMA_URL as gt, RulesyncSkillFrontmatterSchema as h, RULESYNC_CONFIG_RELATIVE_FILE_PATH as ht, getProcessorRegistryEntry as i, removeTempDirectory as it, CLAUDECODE_SKILLS_DIR_PATH as j, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as jt, CLAUDECODE_MEMORIES_DIR_NAME as k, RULESYNC_PERMISSIONS_FILE_NAME as kt, SubagentsProcessor as l, ToolTargetSchema as lt, RulesyncSkill as m, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as mt, checkRulesyncDirExists as n, removeDirectory as nt, RulesProcessor as o, writeFileContent as ot, getLocalSkillDirNames as p, RULESYNC_CHECKS_RELATIVE_DIR_PATH as pt, directoryExists as q, generate as r, removeFile as rt, RulesyncRule as s, ALL_TOOL_TARGETS as st, importFromTool as t, readFileContent as tt, RulesyncSubagent as u, MAX_FILE_SIZE as ut, RulesyncMcp as v, RULESYNC_HOOKS_FILE_NAME as vt, SKILL_FILE_NAME$1 as w, RULESYNC_MCP_JSONC_FILE_NAME as wt, HooksProcessor as x, RULESYNC_IGNORE_RELATIVE_FILE_PATH as xt, IgnoreProcessor as y, RULESYNC_HOOKS_JSONC_FILE_NAME as yt, ConfigResolver as z, ALL_FEATURES as zt };
42164
+ export { getFileSize as $, CLAUDECODE_SETTINGS_LOCAL_FILE_NAME as A, RULESYNC_OVERVIEW_FILE_NAME as At, findControlCharacter as B, formatError as Bt, CommandsProcessor as C, RULESYNC_IGNORE_RELATIVE_FILE_PATH as Ct, CLAUDECODE_DIR as D, RULESYNC_MCP_RELATIVE_FILE_PATH as Dt, CODEXCLI_DIR as E, RULESYNC_MCP_JSONC_FILE_NAME as Et, RulesyncCheck as F, RULESYNC_RELATIVE_DIR_PATH as Ft, CLIError as G, JsonLogger as H, ALL_FEATURES_WITH_WILDCARD as Ht, RulesyncCheckFrontmatterSchema as I, RULESYNC_RULES_RELATIVE_DIR_PATH as It, createTempDirectory as J, ErrorCodes as K, stringifyFrontmatter as L, RULESYNC_SKILLS_RELATIVE_DIR_PATH as Lt, RulesyncCommand as M, RULESYNC_PERMISSIONS_JSONC_FILE_NAME as Mt, RulesyncCommandFrontmatterSchema as N, RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH as Nt, CLAUDECODE_LOCAL_RULE_FILE_NAME as O, RULESYNC_MCP_SCHEMA_URL as Ot, ChecksProcessor as P, RULESYNC_PERMISSIONS_SCHEMA_URL as Pt, findFilesByGlobs as Q, loadYaml as R, RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH as Rt, RulesyncHooks as S, RULESYNC_HOOKS_RELATIVE_FILE_PATH as St, CODEXCLI_BASH_RULES_FILE_NAME as T, RULESYNC_MCP_FILE_NAME as Tt, fallbackLogger as U, ConsoleLogger as V, ALL_FEATURES as Vt, warnOnConflictingFlags as W, ensureDir as X, directoryExists as Y, fileExists as Z, McpProcessor as _, RULESYNC_CONFIG_RELATIVE_FILE_PATH as _t, convertFromTool as a, removeFile as at, RulesyncIgnore as b, RULESYNC_HOOKS_FILE_NAME as bt, RulesyncRuleFrontmatterSchema as c, writeFileContent as ct, RulesyncSubagentFrontmatterSchema as d, ToolTargetSchema as dt, getHomeDirectory as et, SkillsProcessor as f, MAX_FILE_SIZE as ft, RulesyncPermissions as g, RULESYNC_COMMANDS_RELATIVE_DIR_PATH as gt, RulesyncSkillFrontmatterSchema as h, RULESYNC_CHECKS_RELATIVE_DIR_PATH as ht, getProcessorRegistryEntry as i, removeDirectory as it, CLAUDECODE_SKILLS_DIR_PATH as j, RULESYNC_PERMISSIONS_FILE_NAME as jt, CLAUDECODE_MEMORIES_DIR_NAME as k, RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH as kt, SubagentsProcessor as l, ALL_TOOL_TARGETS as lt, RulesyncSkill as m, RULESYNC_AIIGNORE_RELATIVE_FILE_PATH as mt, checkRulesyncDirExists as n, listDirectoryFiles as nt, RulesProcessor as o, removeTempDirectory as ot, getLocalSkillDirNames as p, RULESYNC_AIIGNORE_FILE_NAME as pt, checkPathTraversal as q, generate as r, readFileContent as rt, RulesyncRule as s, toPosixPath as st, importFromTool as t, isSymlink as tt, RulesyncSubagent as u, ALL_TOOL_TARGETS_WITH_WILDCARD as ut, RulesyncMcp as v, RULESYNC_CONFIG_SCHEMA_URL as vt, SKILL_FILE_NAME$1 as w, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH as wt, HooksProcessor as x, RULESYNC_HOOKS_JSONC_FILE_NAME as xt, IgnoreProcessor as y, RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH as yt, ConfigResolver as z, RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH as zt };
41900
42165
 
41901
- //# sourceMappingURL=import-CSnC5vl7.js.map
42166
+ //# sourceMappingURL=import-BN4K49Ta.js.map