rulesync 16.0.0 → 16.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as ErrorCodes, $t as RULESYNC_SKILLS_RELATIVE_DIR_PATH, A as RulesyncMcp, At as RULESYNC_AIIGNORE_RELATIVE_FILE_PATH, B as stringifyFrontmatter, Bt as RULESYNC_IGNORE_RELATIVE_FILE_PATH, C as RulesyncSubagent, Ct as writeFileContent, D as RulesyncRule, Dt as ToolTargetSchema, E as RulesyncSkillFrontmatterSchema, Et as PACKAGING_TOOL_TARGETS, F as parseJsonc, Ft as RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH, G as ConfigFileSchema, Gt as RULESYNC_MCP_SCHEMA_URL, H as SHARED_USER_MANAGED_CONFIG_PATHS, Ht as RULESYNC_MCP_FILE_NAME, I as RulesyncCommand, It as RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH, J as ConsoleLogger, Jt as RULESYNC_PERMISSIONS_LEGACY_FILE_NAME, K as SourceEntrySchema, Kt as RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH, L as RulesyncCommandFrontmatterSchema, Lt as RULESYNC_HOOKS_FILE_NAME, M as RulesyncHooks, Mt as RULESYNC_COMMANDS_RELATIVE_DIR_PATH, N as getRulesyncSourceCandidates, Nt as RULESYNC_CONFIG_RELATIVE_FILE_PATH, O as RulesyncRuleFrontmatterSchema, Ot as MAX_FILE_SIZE, P as resolveRulesyncSourceWritePath, Pt as RULESYNC_CONFIG_SCHEMA_URL, Q as CLIError, Qt as RULESYNC_RULES_RELATIVE_DIR_PATH, R as RulesyncCheck, Rt as RULESYNC_HOOKS_LEGACY_FILE_NAME, S as getLocalSkillDirNames, St as toPosixPath, T as RulesyncSkill, Tt as ALL_TOOL_TARGETS_WITH_WILDCARD, U as SKILL_FILE_NAME$1, Ut as RULESYNC_MCP_LEGACY_FILE_NAME, V as loadYaml, Vt as RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH, W as ConfigResolver, Wt as RULESYNC_MCP_RELATIVE_FILE_PATH, X as fallbackLogger, Xt as RULESYNC_PERMISSIONS_SCHEMA_URL, Y as JsonLogger, Yt as RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH, Z as warnOnConflictingFlags, Zt as RULESYNC_RELATIVE_DIR_PATH, _ as CLAUDECODE_SETTINGS_LOCAL_FILE_NAME, _t as removeFile, a as convertFromTool, at as directoryExists, b as CODEXCLI_BASH_RULES_FILE_NAME, bt as resolvePath, c as SubagentsProcessor, ct as findFilesByGlobs, d as IgnoreProcessor, dt as isSymlink, en as RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH, et as assertDirectoryIfExists, f as HooksProcessor, ft as listDirectoryFiles, g as CLAUDECODE_MEMORIES_DIR_NAME, gt as removeDirectoryStrict, h as CLAUDECODE_LOCAL_RULE_FILE_NAME, ht as removeDirectory, i as getProcessorRegistryEntry, in as formatError, it as createTempDirectory, j as RulesyncIgnore, jt as RULESYNC_CHECKS_RELATIVE_DIR_PATH, k as RulesyncPermissions, kt as RULESYNC_AIIGNORE_FILE_NAME, l as SkillsProcessor, lt as getFileSize, m as CLAUDECODE_DIR, mt as readFileContentOrNull, n as checkRulesyncDirExists, nn as ALL_FEATURES, nt as assertWritablePathInsideRoot, o as isPackagingToolTarget, ot as ensureDir, p as CommandsProcessor, pt as readFileContent, q as findControlCharacter, qt as RULESYNC_PERMISSIONS_FILE_NAME, r as generate, rn as ALL_FEATURES_WITH_WILDCARD, rt as checkPathTraversal, s as RulesProcessor, st as fileExists, t as importFromTool, tn as RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, tt as assertTreeContainsNoSymlinks, u as McpProcessor, ut as getHomeDirectory, v as CLAUDECODE_SKILLS_DIR_PATH, vt as removeFileStrict, w as RulesyncSubagentFrontmatterSchema, wt as ALL_TOOL_TARGETS, x as CODEXCLI_DIR, xt as runWithDirectoryRollback, y as ChecksProcessor, yt as removeTempDirectory, z as RulesyncCheckFrontmatterSchema, zt as RULESYNC_HOOKS_RELATIVE_FILE_PATH } from "../import-BT5KR_K-.js";
2
+ import { $ as ErrorCodes, $t as RULESYNC_SKILLS_RELATIVE_DIR_PATH, A as RulesyncMcp, At as RULESYNC_AIIGNORE_RELATIVE_FILE_PATH, B as stringifyFrontmatter, Bt as RULESYNC_IGNORE_RELATIVE_FILE_PATH, C as RulesyncSubagent, Ct as writeFileContent, D as RulesyncRule, Dt as ToolTargetSchema, E as RulesyncSkillFrontmatterSchema, Et as PACKAGING_TOOL_TARGETS, F as parseJsonc, Ft as RULESYNC_CURATED_RULES_RELATIVE_DIR_PATH, G as ConfigFileSchema, Gt as RULESYNC_MCP_SCHEMA_URL, H as SHARED_USER_MANAGED_CONFIG_PATHS, Ht as RULESYNC_MCP_FILE_NAME, I as RulesyncCommand, It as RULESYNC_CURATED_SKILLS_RELATIVE_DIR_PATH, J as ConsoleLogger, Jt as RULESYNC_PERMISSIONS_LEGACY_FILE_NAME, K as SourceEntrySchema, Kt as RULESYNC_NPM_SOURCES_LOCK_RELATIVE_FILE_PATH, L as RulesyncCommandFrontmatterSchema, Lt as RULESYNC_HOOKS_FILE_NAME, M as RulesyncHooks, Mt as RULESYNC_COMMANDS_RELATIVE_DIR_PATH, N as getRulesyncSourceCandidates, Nt as RULESYNC_CONFIG_RELATIVE_FILE_PATH, O as RulesyncRuleFrontmatterSchema, Ot as MAX_FILE_SIZE, P as resolveRulesyncSourceWritePath, Pt as RULESYNC_CONFIG_SCHEMA_URL, Q as CLIError, Qt as RULESYNC_RULES_RELATIVE_DIR_PATH, R as RulesyncCheck, Rt as RULESYNC_HOOKS_LEGACY_FILE_NAME, S as getLocalSkillDirNames, St as toPosixPath, T as RulesyncSkill, Tt as ALL_TOOL_TARGETS_WITH_WILDCARD, U as SKILL_FILE_NAME$1, Ut as RULESYNC_MCP_LEGACY_FILE_NAME, V as loadYaml, Vt as RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH, W as ConfigResolver, Wt as RULESYNC_MCP_RELATIVE_FILE_PATH, X as fallbackLogger, Xt as RULESYNC_PERMISSIONS_SCHEMA_URL, Y as JsonLogger, Yt as RULESYNC_PERMISSIONS_RELATIVE_FILE_PATH, Z as warnOnConflictingFlags, Zt as RULESYNC_RELATIVE_DIR_PATH, _ as CLAUDECODE_SETTINGS_LOCAL_FILE_NAME, _t as removeFile, a as convertFromTool, at as directoryExists, b as CODEXCLI_BASH_RULES_FILE_NAME, bt as resolvePath, c as SubagentsProcessor, ct as findFilesByGlobs, d as IgnoreProcessor, dt as isSymlink, en as RULESYNC_SOURCES_LOCK_RELATIVE_FILE_PATH, et as assertDirectoryIfExists, f as HooksProcessor, ft as listDirectoryFiles, g as CLAUDECODE_MEMORIES_DIR_NAME, gt as removeDirectoryStrict, h as CLAUDECODE_LOCAL_RULE_FILE_NAME, ht as removeDirectory, i as getProcessorRegistryEntry, in as formatError, it as createTempDirectory, j as RulesyncIgnore, jt as RULESYNC_CHECKS_RELATIVE_DIR_PATH, k as RulesyncPermissions, kt as RULESYNC_AIIGNORE_FILE_NAME, l as SkillsProcessor, lt as getFileSize, m as CLAUDECODE_DIR, mt as readFileContentOrNull, n as checkRulesyncDirExists, nn as ALL_FEATURES, nt as assertWritablePathInsideRoot, o as isPackagingToolTarget, ot as ensureDir, p as CommandsProcessor, pt as readFileContent, q as findControlCharacter, qt as RULESYNC_PERMISSIONS_FILE_NAME, r as generate, rn as ALL_FEATURES_WITH_WILDCARD, rt as checkPathTraversal, s as RulesProcessor, st as fileExists, t as importFromTool, tn as RULESYNC_SUBAGENTS_RELATIVE_DIR_PATH, tt as assertTreeContainsNoSymlinks, u as McpProcessor, ut as getHomeDirectory, v as CLAUDECODE_SKILLS_DIR_PATH, vt as removeFileStrict, w as RulesyncSubagentFrontmatterSchema, wt as ALL_TOOL_TARGETS, x as CODEXCLI_DIR, xt as runWithDirectoryRollback, y as ChecksProcessor, yt as removeTempDirectory, z as RulesyncCheckFrontmatterSchema, zt as RULESYNC_HOOKS_RELATIVE_FILE_PATH } from "../import-CArKOPG_.js";
3
3
  import { Command } from "commander";
4
4
  import { nonnegative, optional, refine, z } from "zod/mini";
5
5
  import { cp, mkdtemp, realpath, rm } from "node:fs/promises";
@@ -17,8 +17,9 @@ import { promisify } from "node:util";
17
17
  import { RequestError } from "@octokit/request-error";
18
18
  import { Octokit } from "@octokit/rest";
19
19
  import { gunzipSync } from "node:zlib";
20
- import { FastMCP } from "fastmcp";
21
20
  import * as fs from "node:fs";
21
+ import { existsSync, watch } from "node:fs";
22
+ import { FastMCP } from "fastmcp";
22
23
  import { Readable, Transform } from "node:stream";
23
24
  import { pipeline } from "node:stream/promises";
24
25
  //#region src/utils/parse-comma-separated-list.ts
@@ -4374,6 +4375,216 @@ async function fetchCommand(logger, options) {
4374
4375
  throw error;
4375
4376
  }
4376
4377
  }
4378
+ /**
4379
+ * Coalesces file-system change notifications into debounced, non-overlapping
4380
+ * runs.
4381
+ *
4382
+ * Guarantees:
4383
+ * - At most one `run` is in flight at any time.
4384
+ * - Every notified path is reported to exactly one `run` as a trigger.
4385
+ * - Notifications that arrive while a run is in flight schedule exactly one
4386
+ * follow-up run after it finishes, so a change is never lost and never
4387
+ * causes a run per event.
4388
+ */
4389
+ var WatchScheduler = class {
4390
+ run;
4391
+ onError;
4392
+ debounceMs;
4393
+ pending = /* @__PURE__ */ new Set();
4394
+ timer;
4395
+ running;
4396
+ closed = false;
4397
+ constructor({ run, onError, debounceMs = 300 }) {
4398
+ this.run = run;
4399
+ this.onError = onError;
4400
+ this.debounceMs = debounceMs;
4401
+ }
4402
+ notify({ path }) {
4403
+ if (this.closed) return;
4404
+ this.pending.add(path);
4405
+ this.schedule();
4406
+ }
4407
+ /**
4408
+ * Stops accepting notifications and waits for an in-flight run to settle.
4409
+ * Pending (not yet started) changes are dropped.
4410
+ */
4411
+ async close() {
4412
+ this.closed = true;
4413
+ this.clearTimer();
4414
+ this.pending.clear();
4415
+ await this.running;
4416
+ }
4417
+ clearTimer() {
4418
+ if (this.timer !== void 0) {
4419
+ clearTimeout(this.timer);
4420
+ this.timer = void 0;
4421
+ }
4422
+ }
4423
+ schedule() {
4424
+ this.clearTimer();
4425
+ this.timer = setTimeout(() => {
4426
+ this.timer = void 0;
4427
+ this.flush();
4428
+ }, this.debounceMs);
4429
+ }
4430
+ async flush() {
4431
+ if (this.closed || this.running !== void 0 || this.pending.size === 0) return;
4432
+ const triggers = [...this.pending];
4433
+ this.pending.clear();
4434
+ const running = (async () => {
4435
+ try {
4436
+ await this.run({ triggers });
4437
+ } catch (error) {
4438
+ this.onError({
4439
+ error,
4440
+ triggers
4441
+ });
4442
+ }
4443
+ })();
4444
+ this.running = running;
4445
+ await running;
4446
+ this.running = void 0;
4447
+ if (!this.closed && this.pending.size > 0) this.schedule();
4448
+ }
4449
+ };
4450
+ /**
4451
+ * Watches one directory, re-attaching the underlying `fs.watch` if the
4452
+ * directory is deleted and later recreated.
4453
+ *
4454
+ * Without this, a `git checkout` to a branch without `.rulesync/` (or any
4455
+ * tool that replaces the directory rather than its contents) would silently
4456
+ * kill the watcher: the deleted inode emits no further events and no error,
4457
+ * so watch mode would keep running while never regenerating again.
4458
+ *
4459
+ * The first attach is not guarded — a missing directory at startup is a real
4460
+ * configuration error and must surface to the caller.
4461
+ */
4462
+ function watchTargetWithRearm({ target, onChange, onError, rearmIntervalMs }) {
4463
+ let watcher;
4464
+ let rearmTimer;
4465
+ let closed = false;
4466
+ const attach = () => {
4467
+ const created = watch(target.directory, {
4468
+ recursive: target.recursive,
4469
+ persistent: true
4470
+ }, (_eventType, filename) => {
4471
+ if (filename === null || filename === void 0) {
4472
+ onChange({ path: target.directory });
4473
+ verifyStillWatching();
4474
+ return;
4475
+ }
4476
+ const relativePath = filename.toString();
4477
+ if (target.include && !target.include(relativePath)) {
4478
+ verifyStillWatching();
4479
+ return;
4480
+ }
4481
+ onChange({ path: join(target.directory, relativePath) });
4482
+ verifyStillWatching();
4483
+ });
4484
+ created.on("error", (error) => {
4485
+ onError({
4486
+ error,
4487
+ directory: target.directory
4488
+ });
4489
+ verifyStillWatching();
4490
+ });
4491
+ watcher = created;
4492
+ };
4493
+ const scheduleRearm = () => {
4494
+ if (closed || rearmTimer !== void 0) return;
4495
+ rearmTimer = setInterval(() => {
4496
+ if (closed || !existsSync(target.directory)) return;
4497
+ clearInterval(rearmTimer);
4498
+ rearmTimer = void 0;
4499
+ try {
4500
+ attach();
4501
+ } catch (error) {
4502
+ onError({
4503
+ error,
4504
+ directory: target.directory
4505
+ });
4506
+ scheduleRearm();
4507
+ return;
4508
+ }
4509
+ onChange({ path: target.directory });
4510
+ }, rearmIntervalMs);
4511
+ };
4512
+ const verifyStillWatching = () => {
4513
+ if (closed || watcher === void 0 || existsSync(target.directory)) return;
4514
+ watcher.close();
4515
+ watcher = void 0;
4516
+ scheduleRearm();
4517
+ };
4518
+ attach();
4519
+ return { close: () => {
4520
+ closed = true;
4521
+ if (rearmTimer !== void 0) {
4522
+ clearInterval(rearmTimer);
4523
+ rearmTimer = void 0;
4524
+ }
4525
+ watcher?.close();
4526
+ watcher = void 0;
4527
+ } };
4528
+ }
4529
+ /**
4530
+ * Starts one watcher per target and forwards matching events to `onChange` as
4531
+ * absolute paths. If any target fails to attach, the watchers started so far
4532
+ * are closed before the error propagates, so no descriptor is leaked.
4533
+ */
4534
+ function watchTargets({ targets, onChange, onError, rearmIntervalMs = 500 }) {
4535
+ const handles = [];
4536
+ const closeAll = () => {
4537
+ for (const handle of handles) handle.close();
4538
+ };
4539
+ try {
4540
+ for (const target of targets) handles.push(watchTargetWithRearm({
4541
+ target,
4542
+ onChange,
4543
+ onError,
4544
+ rearmIntervalMs
4545
+ }));
4546
+ } catch (error) {
4547
+ closeAll();
4548
+ throw error;
4549
+ }
4550
+ return { close: closeAll };
4551
+ }
4552
+ /**
4553
+ * Builds the set of directories watch mode observes: the `.rulesync/` source
4554
+ * tree (recursively) and, filtered down to the configuration files themselves,
4555
+ * the directory holding `rulesync.jsonc`.
4556
+ *
4557
+ * Only input paths are watched. Generated output lives outside `.rulesync/`, so
4558
+ * a regeneration cannot re-trigger the watcher.
4559
+ */
4560
+ function buildWatchTargets({ inputRoot, configFilePath }) {
4561
+ const configFilePaths = buildConfigFilePaths({ configFilePath });
4562
+ return [{
4563
+ directory: join(inputRoot, RULESYNC_RELATIVE_DIR_PATH),
4564
+ recursive: true
4565
+ }, {
4566
+ directory: dirname(configFilePath),
4567
+ recursive: false,
4568
+ include: (relativePath) => configFilePaths.has(join(dirname(configFilePath), relativePath))
4569
+ }];
4570
+ }
4571
+ /**
4572
+ * The absolute paths of the configuration files watch mode observes: the base
4573
+ * configuration file and the `rulesync.local.jsonc` sitting next to it, which
4574
+ * is exactly what `ConfigResolver` loads.
4575
+ */
4576
+ function buildConfigFilePaths({ configFilePath }) {
4577
+ return /* @__PURE__ */ new Set([configFilePath, join(dirname(configFilePath), RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH)]);
4578
+ }
4579
+ /**
4580
+ * Renders trigger paths relative to `baseDir` for logging, truncating long
4581
+ * bursts so a `git checkout` does not flood the terminal.
4582
+ */
4583
+ function formatTriggerPaths({ triggers, baseDir, max = 5 }) {
4584
+ const displayed = triggers.slice(0, max).map((trigger) => relative(baseDir, trigger) || trigger);
4585
+ const remaining = triggers.length - displayed.length;
4586
+ return remaining > 0 ? `${displayed.join(", ")} (+${remaining} more)` : displayed.join(", ");
4587
+ }
4377
4588
  //#endregion
4378
4589
  //#region src/cli/commands/generate.ts
4379
4590
  /**
@@ -4463,7 +4674,20 @@ function buildSummaryParts(result) {
4463
4674
  return parts;
4464
4675
  }
4465
4676
  async function generateCommand(logger, options) {
4466
- const config = await ConfigResolver.resolve(options, { logger });
4677
+ if (options.watch) {
4678
+ await generateWatchCommand(logger, options);
4679
+ return;
4680
+ }
4681
+ await generateOnce(logger, options);
4682
+ }
4683
+ /**
4684
+ * Runs one generation. `resolvedConfig` lets a caller that already resolved
4685
+ * the configuration (watch mode's startup validation) reuse it instead of
4686
+ * paying for a second resolution — and, more importantly, instead of emitting
4687
+ * the resolver's warnings twice.
4688
+ */
4689
+ async function generateOnce(logger, options, { resolvedConfig } = {}) {
4690
+ const config = resolvedConfig ?? await ConfigResolver.resolve(options, { logger });
4467
4691
  const check = config.getCheck();
4468
4692
  const isPreview = config.isPreviewMode();
4469
4693
  const modePrefix = isPreview ? "[DRY RUN]" : "";
@@ -4558,6 +4782,76 @@ async function generateCommand(logger, options) {
4558
4782
  if (isPreview) logger.info(`${modePrefix} Would write ${totalGenerated} file(s) total (${parts.join(" + ")})`);
4559
4783
  else logger.success(`🎉 All done! Written ${totalGenerated} file(s) total (${parts.join(" + ")})`);
4560
4784
  }
4785
+ /**
4786
+ * Rejects flag combinations that contradict a long-running watch: `--check`
4787
+ * and `--dry-run` are one-shot verification modes (the former is meant to exit
4788
+ * non-zero), and `--json` buffers a single result document until the command
4789
+ * returns, which never happens while watching.
4790
+ */
4791
+ function assertWatchModeCompatible({ isCheck, isDryRun, isJsonMode }) {
4792
+ const conflicts = [
4793
+ isCheck ? "--check" : void 0,
4794
+ isDryRun ? "--dry-run" : void 0,
4795
+ isJsonMode ? "--json" : void 0
4796
+ ].filter((flag) => flag !== void 0);
4797
+ if (conflicts.length > 0) throw new CLIError(`--watch cannot be combined with ${conflicts.join(", ")}.`, ErrorCodes.VALIDATION_FAILED);
4798
+ }
4799
+ async function generateWatchCommand(logger, options) {
4800
+ const config = await ConfigResolver.resolve(options, { logger });
4801
+ assertWatchModeCompatible({
4802
+ isCheck: config.getCheck(),
4803
+ isDryRun: config.getDryRun(),
4804
+ isJsonMode: logger.jsonMode
4805
+ });
4806
+ const inputRoot = config.getInputRoot();
4807
+ const configFilePath = config.getConfigFilePath();
4808
+ const configFilePaths = buildConfigFilePaths({ configFilePath });
4809
+ await generateOnce(logger, options, { resolvedConfig: config });
4810
+ const targets = buildWatchTargets({
4811
+ inputRoot,
4812
+ configFilePath
4813
+ });
4814
+ const scheduler = new WatchScheduler({
4815
+ run: async ({ triggers }) => {
4816
+ logger.info(`\nChange detected: ${formatTriggerPaths({
4817
+ triggers,
4818
+ baseDir: inputRoot
4819
+ })}`);
4820
+ if (triggers.some((trigger) => configFilePaths.has(trigger))) logger.warn("Configuration file changed. The set of watched paths is fixed at startup — restart 'rulesync generate --watch' if you changed 'inputRoot' or the configuration file location.");
4821
+ await generateOnce(logger, options);
4822
+ },
4823
+ onError: ({ error }) => {
4824
+ logger.error(`Generation failed: ${formatError(error)}`);
4825
+ logger.info("Still watching for changes...");
4826
+ }
4827
+ });
4828
+ const handle = watchTargets({
4829
+ targets,
4830
+ onChange: ({ path }) => {
4831
+ scheduler.notify({ path });
4832
+ },
4833
+ onError: ({ error, directory }) => {
4834
+ logger.error(`Watch error on ${directory}: ${formatError(error)}`);
4835
+ }
4836
+ });
4837
+ logger.info(`\nWatching for changes in:\n${targets.map((target) => ` ${target.directory}`).join("\n")}`);
4838
+ logger.info("Press Ctrl+C to stop.");
4839
+ await new Promise((resolveShutdown) => {
4840
+ const shutdown = () => {
4841
+ process.off("SIGINT", shutdown);
4842
+ process.off("SIGTERM", shutdown);
4843
+ handle.close();
4844
+ scheduler.close().catch((error) => {
4845
+ logger.error(`Failed to stop the watcher cleanly: ${formatError(error)}`);
4846
+ }).finally(() => {
4847
+ logger.info("\nStopped watching.");
4848
+ resolveShutdown();
4849
+ });
4850
+ };
4851
+ process.once("SIGINT", shutdown);
4852
+ process.once("SIGTERM", shutdown);
4853
+ });
4854
+ }
4561
4855
  //#endregion
4562
4856
  //#region src/cli/commands/gitignore-derive.ts
4563
4857
  const TARGETS_NOT_DERIVED = /* @__PURE__ */ new Set([
@@ -9002,7 +9296,7 @@ function wrapCommand$1({ name, errorCode, handler, getVersion, loggerFactory = c
9002
9296
  }
9003
9297
  //#endregion
9004
9298
  //#region src/cli/program.ts
9005
- const getVersion = () => "16.0.0";
9299
+ const getVersion = () => "16.2.0";
9006
9300
  const FEATURES_HELP = `${ALL_FEATURES.join(",")}; ignore is deprecated, use permissions`;
9007
9301
  function wrapCommand(name, errorCode, handler) {
9008
9302
  return wrapCommand$1({
@@ -9071,7 +9365,7 @@ function createProgram() {
9071
9365
  silent: options.silent
9072
9366
  });
9073
9367
  }));
9074
- program.command("generate").description("Generate configuration files for AI tools").option("-t, --targets <tools>", "Comma-separated list of tools to generate for (e.g., 'copilot,cursor,cline' or '*' for all)", parseCommaSeparatedList).option("-f, --features <features>", `Comma-separated list of features to generate (${FEATURES_HELP}) or '*' for all`, parseCommaSeparatedList).option("--delete", "Delete all existing files in output directories before generating").option("-o, --output-roots <paths>", "Output root directories to generate files into (comma-separated for multiple paths)", parseCommaSeparatedList).option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").option("-c, --config <path>", "Path to configuration file").option("-g, --global", "Generate for global(user scope) configuration files").option("--simulate-commands", "Generate simulated commands. This feature is only available for copilot, cursor and codexcli.").option("--simulate-subagents", "Generate simulated subagents. This feature is only available for copilot and codexcli.").option("--simulate-skills", "Generate simulated skills. This feature is only available for copilot, cursor and codexcli.").option("--input-root <path>", "Path to the directory containing .rulesync/ (parent of .rulesync/)").option("--dry-run", "Dry run: show changes without writing files").option("--check", "Check if files are up to date (exits with code 1 if changes needed)").action(wrapCommand("generate", "GENERATION_FAILED", async (logger, options) => {
9368
+ program.command("generate").description("Generate configuration files for AI tools").option("-t, --targets <tools>", "Comma-separated list of tools to generate for (e.g., 'copilot,cursor,cline' or '*' for all)", parseCommaSeparatedList).option("-f, --features <features>", `Comma-separated list of features to generate (${FEATURES_HELP}) or '*' for all`, parseCommaSeparatedList).option("--delete", "Delete all existing files in output directories before generating").option("-o, --output-roots <paths>", "Output root directories to generate files into (comma-separated for multiple paths)", parseCommaSeparatedList).option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").option("-c, --config <path>", "Path to configuration file").option("-g, --global", "Generate for global(user scope) configuration files").option("--simulate-commands", "Generate simulated commands. This feature is only available for copilot, cursor and codexcli.").option("--simulate-subagents", "Generate simulated subagents. This feature is only available for copilot and codexcli.").option("--simulate-skills", "Generate simulated skills. This feature is only available for copilot, cursor and codexcli.").option("--input-root <path>", "Path to the directory containing .rulesync/ (parent of .rulesync/)").option("--dry-run", "Dry run: show changes without writing files").option("--check", "Check if files are up to date (exits with code 1 if changes needed)").option("-w, --watch", "Keep running and regenerate whenever rulesync source files change (cannot be combined with --check, --dry-run or --json)").action(wrapCommand("generate", "GENERATION_FAILED", async (logger, options) => {
9075
9369
  await generateCommand(logger, options);
9076
9370
  }));
9077
9371
  program.command("update").description("Update rulesync to the latest version").option("--check", "Check for updates without installing").option("--force", "Force update even if already at latest version").option("--token <token>", "GitHub token for API access").option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(wrapCommand("update", "UPDATE_FAILED", async (logger, options) => {