rulesync 8.0.0 → 8.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.
@@ -44,12 +44,13 @@ var FeaturesSchema = import_mini.z.array(FeatureSchema);
44
44
  var FeatureOptionsSchema = import_mini.z.record(import_mini.z.string(), import_mini.z.unknown());
45
45
  var FeatureValueSchema = import_mini.z.union([import_mini.z.boolean(), FeatureOptionsSchema]);
46
46
  var PerFeatureConfigSchema = import_mini.z.record(import_mini.z.string(), FeatureValueSchema);
47
+ var PerTargetFeaturesValueSchema = import_mini.z.union([
48
+ import_mini.z.array(import_mini.z.enum(ALL_FEATURES_WITH_WILDCARD)),
49
+ PerFeatureConfigSchema
50
+ ]);
47
51
  var RulesyncFeaturesSchema = import_mini.z.union([
48
52
  import_mini.z.array(import_mini.z.enum(ALL_FEATURES_WITH_WILDCARD)),
49
- import_mini.z.record(
50
- import_mini.z.string(),
51
- import_mini.z.union([import_mini.z.array(import_mini.z.enum(ALL_FEATURES_WITH_WILDCARD)), PerFeatureConfigSchema])
52
- )
53
+ import_mini.z.record(import_mini.z.string(), PerTargetFeaturesValueSchema)
53
54
  ]);
54
55
  var isFeatureValueEnabled = (value) => {
55
56
  if (value === true) return true;
@@ -951,6 +952,14 @@ var ALL_TOOL_TARGETS_WITH_WILDCARD = [...ALL_TOOL_TARGETS, "*"];
951
952
  var ToolTargetSchema = import_mini3.z.enum(ALL_TOOL_TARGETS);
952
953
  var ToolTargetsSchema = import_mini3.z.array(ToolTargetSchema);
953
954
  var RulesyncTargetsSchema = import_mini3.z.array(import_mini3.z.enum(ALL_TOOL_TARGETS_WITH_WILDCARD));
955
+ var RulesyncConfigTargetsObjectSchema = import_mini3.z.record(import_mini3.z.string(), PerTargetFeaturesValueSchema);
956
+ var RulesyncConfigTargetsSchema = import_mini3.z.union([
957
+ RulesyncTargetsSchema,
958
+ RulesyncConfigTargetsObjectSchema
959
+ ]);
960
+ var isRulesyncConfigTargetsObject = (value) => {
961
+ return !Array.isArray(value);
962
+ };
954
963
 
955
964
  // src/features/commands/rulesync-command.ts
956
965
  var RulesyncCommandFrontmatterSchema = import_mini4.z.looseObject({
@@ -13495,12 +13504,16 @@ var CopilotSubagent = class _CopilotSubagent extends ToolSubagent {
13495
13504
  const body = rulesyncSubagent.getBody();
13496
13505
  const fileContent = stringifyFrontmatter(body, copilotFrontmatter);
13497
13506
  const paths = this.getSettablePaths({ global });
13507
+ let relativeFilePath = rulesyncSubagent.getRelativeFilePath();
13508
+ if (!relativeFilePath.endsWith(".agent.md")) {
13509
+ relativeFilePath = relativeFilePath.replace(/\.md$/, ".agent.md");
13510
+ }
13498
13511
  return new _CopilotSubagent({
13499
13512
  baseDir,
13500
13513
  frontmatter: copilotFrontmatter,
13501
13514
  body,
13502
13515
  relativeDirPath: paths.relativeDirPath,
13503
- relativeFilePath: rulesyncSubagent.getRelativeFilePath(),
13516
+ relativeFilePath,
13504
13517
  fileContent,
13505
13518
  validate,
13506
13519
  global
@@ -18019,7 +18032,8 @@ var rulesProcessorToolTargets = [
18019
18032
  var RulesProcessorToolTargetSchema = import_mini65.z.enum(rulesProcessorToolTargets);
18020
18033
  var formatRulePaths = (rules) => rules.map((r) => (0, import_node_path131.join)(r.getRelativeDirPath(), r.getRelativeFilePath())).join(", ");
18021
18034
  var RulesFeatureOptionsSchema = import_mini65.z.looseObject({
18022
- ruleDiscoveryMode: import_mini65.z.optional(import_mini65.z.enum(["none", "explicit"]))
18035
+ ruleDiscoveryMode: import_mini65.z.optional(import_mini65.z.enum(["none", "explicit"])),
18036
+ includeLocalRoot: import_mini65.z.optional(import_mini65.z.boolean())
18023
18037
  });
18024
18038
  var resolveRuleDiscoveryMode = ({
18025
18039
  defaultMode,
@@ -18040,6 +18054,19 @@ var resolveRuleDiscoveryMode = ({
18040
18054
  }
18041
18055
  return parsed.data.ruleDiscoveryMode === "none" ? "auto" : "toon";
18042
18056
  };
18057
+ var IncludeLocalRootSchema = import_mini65.z.looseObject({
18058
+ includeLocalRoot: import_mini65.z.optional(import_mini65.z.boolean())
18059
+ });
18060
+ var resolveIncludeLocalRoot = (options) => {
18061
+ if (!options) return true;
18062
+ const parsed = IncludeLocalRootSchema.safeParse(options);
18063
+ if (!parsed.success) {
18064
+ throw new Error(
18065
+ `Invalid options for rules feature: ${parsed.error.message}. \`includeLocalRoot\` must be a boolean.`
18066
+ );
18067
+ }
18068
+ return parsed.data.includeLocalRoot ?? true;
18069
+ };
18043
18070
  var toolRuleFactories = /* @__PURE__ */ new Map([
18044
18071
  [
18045
18072
  "agentsmd",
@@ -18403,7 +18430,8 @@ var RulesProcessor = class extends FeatureProcessor {
18403
18430
  global: this.global
18404
18431
  });
18405
18432
  }).filter((rule) => rule !== null);
18406
- if (localRootRules.length > 0 && !this.global) {
18433
+ const includeLocalRoot = resolveIncludeLocalRoot(this.featureOptions);
18434
+ if (localRootRules.length > 0 && !this.global && includeLocalRoot) {
18407
18435
  const localRootRule = localRootRules[0];
18408
18436
  if (localRootRule && factory.class.isTargetedByRulesyncRule(localRootRule)) {
18409
18437
  this.handleLocalRootRule(toolRules, localRootRule, factory);
@@ -19975,7 +20003,7 @@ var SourceEntrySchema = import_mini69.z.object({
19975
20003
  });
19976
20004
  var ConfigParamsSchema = import_mini69.z.object({
19977
20005
  baseDirs: import_mini69.z.array(import_mini69.z.string()),
19978
- targets: RulesyncTargetsSchema,
20006
+ targets: RulesyncConfigTargetsSchema,
19979
20007
  features: RulesyncFeaturesSchema,
19980
20008
  verbose: import_mini69.z.boolean(),
19981
20009
  delete: import_mini69.z.boolean(),
@@ -19985,6 +20013,7 @@ var ConfigParamsSchema = import_mini69.z.object({
19985
20013
  simulateCommands: (0, import_mini69.optional)(import_mini69.z.boolean()),
19986
20014
  simulateSubagents: (0, import_mini69.optional)(import_mini69.z.boolean()),
19987
20015
  simulateSkills: (0, import_mini69.optional)(import_mini69.z.boolean()),
20016
+ gitignoreTargetsOnly: (0, import_mini69.optional)(import_mini69.z.boolean()),
19988
20017
  dryRun: (0, import_mini69.optional)(import_mini69.z.boolean()),
19989
20018
  check: (0, import_mini69.optional)(import_mini69.z.boolean()),
19990
20019
  // Declarative skill sources
@@ -20001,10 +20030,42 @@ var CONFLICTING_TARGET_PAIRS = [
20001
20030
  ["claudecode", "claudecode-legacy"]
20002
20031
  ];
20003
20032
  var LEGACY_TARGETS = ["augmentcode-legacy", "claudecode-legacy"];
20033
+ var assertTargetsFeaturesExclusive = ({
20034
+ targets,
20035
+ features
20036
+ }) => {
20037
+ const targetsIsObject = targets !== void 0 && !Array.isArray(targets);
20038
+ const featuresIsObject = features !== void 0 && !Array.isArray(features);
20039
+ if (targetsIsObject && features !== void 0) {
20040
+ throw new Error(
20041
+ "Invalid config: when 'targets' is in object form, 'features' must be omitted. Declare per-target features inside the 'targets' object instead."
20042
+ );
20043
+ }
20044
+ if (featuresIsObject && targets !== void 0) {
20045
+ throw new Error(
20046
+ "Invalid config: when 'features' is in object form, 'targets' must be omitted. Migrate to the 'targets' object form, e.g. `targets: { claudecode: [...] }`."
20047
+ );
20048
+ }
20049
+ };
20050
+ var assertTargetsOrFeaturesProvided = ({
20051
+ targets,
20052
+ features
20053
+ }) => {
20054
+ if (targets === void 0 && features === void 0) {
20055
+ throw new Error("Invalid config: at least one of 'targets' or 'features' must be provided.");
20056
+ }
20057
+ };
20004
20058
  var Config = class _Config {
20005
20059
  baseDirs;
20006
20060
  targets;
20007
20061
  features;
20062
+ /**
20063
+ * Cached list of validated `ToolTarget` keys for the object form of
20064
+ * `targets`. Populated in the constructor after `validateObjectFormTargetKeys`
20065
+ * so `getTargets()` does not rebuild the `ALL_TOOL_TARGETS` set on every call.
20066
+ * Undefined when `this.targets` is in array form.
20067
+ */
20068
+ objectFormTargetKeys;
20008
20069
  verbose;
20009
20070
  delete;
20010
20071
  global;
@@ -20012,6 +20073,7 @@ var Config = class _Config {
20012
20073
  simulateCommands;
20013
20074
  simulateSubagents;
20014
20075
  simulateSkills;
20076
+ gitignoreTargetsOnly;
20015
20077
  dryRun;
20016
20078
  check;
20017
20079
  sources;
@@ -20026,17 +20088,24 @@ var Config = class _Config {
20026
20088
  simulateCommands,
20027
20089
  simulateSubagents,
20028
20090
  simulateSkills,
20091
+ gitignoreTargetsOnly,
20029
20092
  dryRun,
20030
20093
  check,
20031
20094
  sources
20032
20095
  }) {
20033
- this.validateConflictingTargets(targets);
20096
+ assertTargetsFeaturesExclusive({ targets, features });
20097
+ assertTargetsOrFeaturesProvided({ targets, features });
20098
+ const resolvedTargets = targets ?? [];
20099
+ const resolvedFeatures = features ?? [];
20100
+ this.validateObjectFormTargetKeys(resolvedTargets);
20101
+ this.validateConflictingTargets(resolvedTargets);
20034
20102
  if (dryRun && check) {
20035
20103
  throw new Error("--dry-run and --check cannot be used together");
20036
20104
  }
20037
20105
  this.baseDirs = baseDirs;
20038
- this.targets = targets;
20039
- this.features = features;
20106
+ this.targets = resolvedTargets;
20107
+ this.features = resolvedFeatures;
20108
+ this.objectFormTargetKeys = isRulesyncConfigTargetsObject(resolvedTargets) ? _Config.filterValidToolTargets(Object.keys(resolvedTargets)) : void 0;
20040
20109
  this.verbose = verbose;
20041
20110
  this.delete = isDelete;
20042
20111
  this.global = global ?? false;
@@ -20044,15 +20113,42 @@ var Config = class _Config {
20044
20113
  this.simulateCommands = simulateCommands ?? false;
20045
20114
  this.simulateSubagents = simulateSubagents ?? false;
20046
20115
  this.simulateSkills = simulateSkills ?? false;
20116
+ this.gitignoreTargetsOnly = gitignoreTargetsOnly ?? true;
20047
20117
  this.dryRun = dryRun ?? false;
20048
20118
  this.check = check ?? false;
20049
20119
  this.sources = sources ?? [];
20050
20120
  }
20121
+ /**
20122
+ * Rejects unknown keys (and the special `*` key) in the object form of
20123
+ * `targets`. For the array form this is already enforced at the Zod schema
20124
+ * level via `z.enum(ALL_TOOL_TARGETS_WITH_WILDCARD)`; for the object form
20125
+ * `z.record(z.string(), ...)` intentionally accepts any string key (to work
20126
+ * around zod's `z.record(z.enum(...))` requiring ALL enum members), so
20127
+ * runtime validation lives here instead.
20128
+ */
20129
+ validateObjectFormTargetKeys(targets) {
20130
+ if (Array.isArray(targets)) return;
20131
+ const validTargets = new Set(ALL_TOOL_TARGETS);
20132
+ for (const key of Object.keys(targets)) {
20133
+ if (key === "*") {
20134
+ throw new Error(
20135
+ "Invalid target '*' in object form: wildcard is only supported in the array form `targets: ['*']`. Per-target options cannot be attached to a wildcard."
20136
+ );
20137
+ }
20138
+ if (!validTargets.has(key)) {
20139
+ throw new Error(`Unknown target '${key}'. Valid targets: ${ALL_TOOL_TARGETS.join(", ")}.`);
20140
+ }
20141
+ }
20142
+ }
20051
20143
  validateConflictingTargets(targets) {
20144
+ const has = (target) => {
20145
+ if (Array.isArray(targets)) {
20146
+ return targets.includes(target);
20147
+ }
20148
+ return Object.prototype.hasOwnProperty.call(targets, target);
20149
+ };
20052
20150
  for (const [target1, target2] of CONFLICTING_TARGET_PAIRS) {
20053
- const hasTarget1 = targets.includes(target1);
20054
- const hasTarget2 = targets.includes(target2);
20055
- if (hasTarget1 && hasTarget2) {
20151
+ if (has(target1) && has(target2)) {
20056
20152
  throw new Error(
20057
20153
  `Conflicting targets: '${target1}' and '${target2}' cannot be used together. Please choose one.`
20058
20154
  );
@@ -20062,16 +20158,45 @@ var Config = class _Config {
20062
20158
  getBaseDirs() {
20063
20159
  return this.baseDirs;
20064
20160
  }
20161
+ /**
20162
+ * Filter an arbitrary string-key list down to the known `ToolTarget` set,
20163
+ * skipping `*` (which is only meaningful as an array element, not a key).
20164
+ */
20165
+ static filterValidToolTargets(keys) {
20166
+ const validTargets = new Set(ALL_TOOL_TARGETS);
20167
+ const result = [];
20168
+ for (const key of keys) {
20169
+ if (key === "*") continue;
20170
+ if (!validTargets.has(key)) continue;
20171
+ result.push(key);
20172
+ }
20173
+ return result;
20174
+ }
20065
20175
  getTargets() {
20066
- if (this.targets.includes("*")) {
20176
+ if (this.objectFormTargetKeys !== void 0) {
20177
+ return this.objectFormTargetKeys;
20178
+ }
20179
+ const arrayTargets = Array.isArray(this.targets) ? this.targets : [];
20180
+ if (!Array.isArray(this.features)) {
20181
+ return _Config.filterValidToolTargets(Object.keys(this.features));
20182
+ }
20183
+ if (arrayTargets.includes("*")) {
20067
20184
  return ALL_TOOL_TARGETS.filter(
20068
20185
  // eslint-disable-next-line no-type-assertion/no-type-assertion
20069
20186
  (target) => !LEGACY_TARGETS.includes(target)
20070
20187
  );
20071
20188
  }
20072
- return this.targets.filter((target) => target !== "*");
20189
+ return arrayTargets.filter((target) => target !== "*");
20073
20190
  }
20074
20191
  getFeatures(target) {
20192
+ if (isRulesyncConfigTargetsObject(this.targets)) {
20193
+ if (target) {
20194
+ const value = this.targets[target];
20195
+ if (!value) return [];
20196
+ return _Config.normalizeTargetFeatures(value);
20197
+ }
20198
+ return _Config.collectAllFeatures(Object.values(this.targets));
20199
+ }
20075
20200
  if (!Array.isArray(this.features)) {
20076
20201
  const perTargetFeatures = this.features;
20077
20202
  if (target) {
@@ -20081,20 +20206,7 @@ var Config = class _Config {
20081
20206
  }
20082
20207
  return _Config.normalizeTargetFeatures(targetFeatures);
20083
20208
  }
20084
- const allFeatures = [];
20085
- for (const features of Object.values(perTargetFeatures)) {
20086
- if (!features) continue;
20087
- const normalized = _Config.normalizeTargetFeatures(features);
20088
- for (const feature of normalized) {
20089
- if (!allFeatures.includes(feature)) {
20090
- allFeatures.push(feature);
20091
- }
20092
- }
20093
- if (allFeatures.length === ALL_FEATURES.length) {
20094
- return allFeatures;
20095
- }
20096
- }
20097
- return allFeatures;
20209
+ return _Config.collectAllFeatures(Object.values(perTargetFeatures));
20098
20210
  }
20099
20211
  if (this.features.includes("*")) {
20100
20212
  return [...ALL_FEATURES];
@@ -20122,23 +20234,40 @@ var Config = class _Config {
20122
20234
  }
20123
20235
  return enabled;
20124
20236
  }
20237
+ /**
20238
+ * Collect the union of features across all per-target values.
20239
+ * Used when `getFeatures()` is called without a target in object mode.
20240
+ */
20241
+ static collectAllFeatures(values) {
20242
+ const allFeatures = [];
20243
+ for (const value of values) {
20244
+ if (!value) continue;
20245
+ const normalized = _Config.normalizeTargetFeatures(value);
20246
+ for (const feature of normalized) {
20247
+ if (!allFeatures.includes(feature)) {
20248
+ allFeatures.push(feature);
20249
+ }
20250
+ }
20251
+ if (allFeatures.length === ALL_FEATURES.length) {
20252
+ return allFeatures;
20253
+ }
20254
+ }
20255
+ return allFeatures;
20256
+ }
20125
20257
  /**
20126
20258
  * Returns the per-feature options object for a given target/feature, if any.
20127
20259
  * Returns `undefined` when no per-feature options were provided or when the
20128
20260
  * feature is not enabled for the given target.
20129
20261
  */
20130
20262
  getFeatureOptions(target, feature) {
20131
- if (Array.isArray(this.features)) {
20132
- return void 0;
20133
- }
20134
- const targetFeatures = this.features[target];
20135
- if (!targetFeatures || Array.isArray(targetFeatures)) {
20263
+ const value = isRulesyncConfigTargetsObject(this.targets) ? this.targets[target] : !Array.isArray(this.features) ? this.features[target] : void 0;
20264
+ if (!value || Array.isArray(value)) {
20136
20265
  return void 0;
20137
20266
  }
20138
- const perFeature = targetFeatures;
20139
- const value = perFeature[feature];
20140
- if (value && typeof value === "object" && isFeatureValueEnabled(value)) {
20141
- return value;
20267
+ const perFeature = value;
20268
+ const featureValue = perFeature[feature];
20269
+ if (featureValue && typeof featureValue === "object" && isFeatureValueEnabled(featureValue)) {
20270
+ return featureValue;
20142
20271
  }
20143
20272
  return void 0;
20144
20273
  }
@@ -20146,6 +20275,13 @@ var Config = class _Config {
20146
20275
  * Check if per-target features configuration is being used.
20147
20276
  */
20148
20277
  hasPerTargetFeatures() {
20278
+ return isRulesyncConfigTargetsObject(this.targets) || !Array.isArray(this.features);
20279
+ }
20280
+ /**
20281
+ * Returns true if the deprecated object form under `features` is in use.
20282
+ * Callers can use this to emit a migration warning.
20283
+ */
20284
+ hasDeprecatedFeaturesObjectForm() {
20149
20285
  return !Array.isArray(this.features);
20150
20286
  }
20151
20287
  getVerbose() {
@@ -20169,6 +20305,9 @@ var Config = class _Config {
20169
20305
  getSimulateSkills() {
20170
20306
  return this.simulateSkills;
20171
20307
  }
20308
+ getGitignoreTargetsOnly() {
20309
+ return this.gitignoreTargetsOnly;
20310
+ }
20172
20311
  getDryRun() {
20173
20312
  return this.dryRun;
20174
20313
  }
@@ -20187,6 +20326,17 @@ var Config = class _Config {
20187
20326
  }
20188
20327
  };
20189
20328
 
20329
+ // src/config/deprecation-warnings.ts
20330
+ var deprecationWarningEmitted = false;
20331
+ var emitFeaturesObjectFormDeprecationWarning = () => {
20332
+ if (deprecationWarningEmitted) return;
20333
+ if (process.env.RULESYNC_SILENT_DEPRECATION) return;
20334
+ deprecationWarningEmitted = true;
20335
+ console.warn(
20336
+ "[rulesync] DEPRECATED: 'features' object form is deprecated. Use the new 'targets' object form instead: `targets: { claudecode: { rules: true, ignore: { fileMode: 'local' } } }`. See https://github.com/dyoshikawa/rulesync/blob/main/docs/guide/configuration.md for the migration guide."
20337
+ );
20338
+ };
20339
+
20190
20340
  // src/config/config-resolver.ts
20191
20341
  var getDefaults = () => ({
20192
20342
  targets: ["agentsmd"],
@@ -20200,6 +20350,7 @@ var getDefaults = () => ({
20200
20350
  simulateCommands: false,
20201
20351
  simulateSubagents: false,
20202
20352
  simulateSkills: false,
20353
+ gitignoreTargetsOnly: true,
20203
20354
  dryRun: false,
20204
20355
  check: false,
20205
20356
  sources: []
@@ -20212,6 +20363,10 @@ var loadConfigFromFile = async (filePath) => {
20212
20363
  const jsonData = (0, import_jsonc_parser4.parse)(fileContent);
20213
20364
  const parsed = ConfigFileSchema.parse(jsonData);
20214
20365
  const { $schema: _schema, ...configParams } = parsed;
20366
+ assertTargetsFeaturesExclusive({
20367
+ targets: configParams.targets,
20368
+ features: configParams.features
20369
+ });
20215
20370
  return configParams;
20216
20371
  };
20217
20372
  var mergeConfigs = (baseConfig, localConfig) => {
@@ -20226,6 +20381,7 @@ var mergeConfigs = (baseConfig, localConfig) => {
20226
20381
  simulateCommands: localConfig.simulateCommands ?? baseConfig.simulateCommands,
20227
20382
  simulateSubagents: localConfig.simulateSubagents ?? baseConfig.simulateSubagents,
20228
20383
  simulateSkills: localConfig.simulateSkills ?? baseConfig.simulateSkills,
20384
+ gitignoreTargetsOnly: localConfig.gitignoreTargetsOnly ?? baseConfig.gitignoreTargetsOnly,
20229
20385
  dryRun: localConfig.dryRun ?? baseConfig.dryRun,
20230
20386
  check: localConfig.check ?? baseConfig.check,
20231
20387
  sources: localConfig.sources ?? baseConfig.sources
@@ -20244,6 +20400,7 @@ var ConfigResolver = class {
20244
20400
  simulateCommands,
20245
20401
  simulateSubagents,
20246
20402
  simulateSkills,
20403
+ gitignoreTargetsOnly,
20247
20404
  dryRun,
20248
20405
  check
20249
20406
  }) {
@@ -20253,13 +20410,35 @@ var ConfigResolver = class {
20253
20410
  const localConfigPath = (0, import_node_path134.join)(configDir, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH);
20254
20411
  const localConfig = await loadConfigFromFile(localConfigPath);
20255
20412
  const configByFile = mergeConfigs(baseConfig, localConfig);
20413
+ try {
20414
+ assertTargetsFeaturesExclusive({
20415
+ targets: configByFile.targets,
20416
+ features: configByFile.features
20417
+ });
20418
+ } catch (error) {
20419
+ const detail = error instanceof Error ? error.message : String(error);
20420
+ throw new Error(
20421
+ `${detail} (detected after merging '${validatedConfigPath}' with '${localConfigPath}' \u2014 the two files combined produce the invalid combination; remove the conflicting field from one of them).`,
20422
+ { cause: error }
20423
+ );
20424
+ }
20256
20425
  const resolvedGlobal = global ?? configByFile.global ?? getDefaults().global;
20257
20426
  const resolvedSimulateCommands = simulateCommands ?? configByFile.simulateCommands ?? getDefaults().simulateCommands;
20258
20427
  const resolvedSimulateSubagents = simulateSubagents ?? configByFile.simulateSubagents ?? getDefaults().simulateSubagents;
20259
20428
  const resolvedSimulateSkills = simulateSkills ?? configByFile.simulateSkills ?? getDefaults().simulateSkills;
20429
+ const resolvedGitignoreTargetsOnly = gitignoreTargetsOnly ?? configByFile.gitignoreTargetsOnly ?? getDefaults().gitignoreTargetsOnly;
20430
+ const userProvidedFeatures = features ?? configByFile.features;
20431
+ const userProvidedTargets = targets ?? configByFile.targets;
20432
+ const targetsIsObject = userProvidedTargets !== void 0 && !Array.isArray(userProvidedTargets);
20433
+ const featuresIsObject = userProvidedFeatures !== void 0 && !Array.isArray(userProvidedFeatures);
20434
+ if (featuresIsObject) {
20435
+ emitFeaturesObjectFormDeprecationWarning();
20436
+ }
20437
+ const resolvedFeatures = userProvidedFeatures ?? (targetsIsObject ? void 0 : getDefaults().features);
20438
+ const resolvedTargets = userProvidedTargets ?? (featuresIsObject ? void 0 : getDefaults().targets);
20260
20439
  const configParams = {
20261
- targets: targets ?? configByFile.targets ?? getDefaults().targets,
20262
- features: features ?? configByFile.features ?? getDefaults().features,
20440
+ targets: resolvedTargets,
20441
+ features: resolvedFeatures,
20263
20442
  verbose: verbose ?? configByFile.verbose ?? getDefaults().verbose,
20264
20443
  delete: isDelete ?? configByFile.delete ?? getDefaults().delete,
20265
20444
  baseDirs: getBaseDirsInLightOfGlobal({
@@ -20271,6 +20450,7 @@ var ConfigResolver = class {
20271
20450
  simulateCommands: resolvedSimulateCommands,
20272
20451
  simulateSubagents: resolvedSimulateSubagents,
20273
20452
  simulateSkills: resolvedSimulateSkills,
20453
+ gitignoreTargetsOnly: resolvedGitignoreTargetsOnly,
20274
20454
  dryRun: dryRun ?? configByFile.dryRun ?? getDefaults().dryRun,
20275
20455
  check: check ?? configByFile.check ?? getDefaults().check,
20276
20456
  sources: configByFile.sources ?? getDefaults().sources
@@ -21860,6 +22040,7 @@ var GITIGNORE_ENTRY_REGISTRY = [
21860
22040
  feature: "general",
21861
22041
  entry: "**/.claude/settings.local.json"
21862
22042
  },
22043
+ { target: "claudecode", feature: "general", entry: "**/.claude/*.lock" },
21863
22044
  // Cline
21864
22045
  { target: "cline", feature: "rules", entry: "**/.clinerules/" },
21865
22046
  { target: "cline", feature: "commands", entry: "**/.clinerules/workflows/" },
@@ -21968,6 +22149,11 @@ var GITIGNORE_ENTRY_REGISTRY = [
21968
22149
  { target: "opencode", feature: "skills", entry: "**/.opencode/skill/" },
21969
22150
  { target: "opencode", feature: "mcp", entry: "**/.opencode/plugins/" },
21970
22151
  { target: "opencode", feature: "general", entry: "**/.opencode/memories/" },
22152
+ {
22153
+ target: "opencode",
22154
+ feature: "general",
22155
+ entry: "**/.opencode/package-lock.json"
22156
+ },
21971
22157
  // Qwen Code
21972
22158
  { target: "qwencode", feature: "rules", entry: "**/QWEN.md" },
21973
22159
  { target: "qwencode", feature: "general", entry: "**/.qwen/memories/" },
@@ -22473,6 +22659,12 @@ async function importCommand(logger5, options) {
22473
22659
  if (!options.targets) {
22474
22660
  throw new CLIError("No tools found in --targets", ErrorCodes.IMPORT_FAILED);
22475
22661
  }
22662
+ if (!Array.isArray(options.targets)) {
22663
+ throw new CLIError(
22664
+ "--targets object form is not supported on the command line",
22665
+ ErrorCodes.IMPORT_FAILED
22666
+ );
22667
+ }
22476
22668
  if (options.targets.length > 1) {
22477
22669
  throw new CLIError("Only one tool can be imported at a time", ErrorCodes.IMPORT_FAILED);
22478
22670
  }
@@ -22541,7 +22733,8 @@ async function createConfigFile() {
22541
22733
  global: false,
22542
22734
  simulateCommands: false,
22543
22735
  simulateSubagents: false,
22544
- simulateSkills: false
22736
+ simulateSkills: false,
22737
+ gitignoreTargetsOnly: true
22545
22738
  },
22546
22739
  null,
22547
22740
  2
@@ -25152,6 +25345,35 @@ async function mcpCommand(logger5, { version }) {
25152
25345
  });
25153
25346
  }
25154
25347
 
25348
+ // src/cli/commands/resolve-gitignore-targets.ts
25349
+ var import_node_path152 = require("path");
25350
+ var resolveGitignoreTargets = async ({
25351
+ cliTargets,
25352
+ cwd = process.cwd()
25353
+ }) => {
25354
+ if (cliTargets !== void 0) {
25355
+ return cliTargets;
25356
+ }
25357
+ const baseConfigPath = (0, import_node_path152.join)(cwd, RULESYNC_CONFIG_RELATIVE_FILE_PATH);
25358
+ const localConfigPath = (0, import_node_path152.join)(cwd, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH);
25359
+ const [hasBase, hasLocal] = await Promise.all([
25360
+ fileExists(baseConfigPath),
25361
+ fileExists(localConfigPath)
25362
+ ]);
25363
+ if (!hasBase && !hasLocal) {
25364
+ return void 0;
25365
+ }
25366
+ const config = await ConfigResolver.resolve({});
25367
+ if (config.getGitignoreTargetsOnly()) {
25368
+ const targets = config.getTargets();
25369
+ if (targets.includes("agentsmd")) {
25370
+ return targets;
25371
+ }
25372
+ return [...targets, "agentsmd"];
25373
+ }
25374
+ return void 0;
25375
+ };
25376
+
25155
25377
  // src/lib/update.ts
25156
25378
  var crypto = __toESM(require("crypto"), 1);
25157
25379
  var fs = __toESM(require("fs"), 1);
@@ -25554,7 +25776,7 @@ function wrapCommand({
25554
25776
  }
25555
25777
 
25556
25778
  // src/cli/index.ts
25557
- var getVersion = () => "8.0.0";
25779
+ var getVersion = () => "8.2.0";
25558
25780
  function wrapCommand2(name, errorCode, handler) {
25559
25781
  return wrapCommand({ name, errorCode, handler, getVersion });
25560
25782
  }
@@ -25577,11 +25799,12 @@ var main = async () => {
25577
25799
  parseCommaSeparatedList
25578
25800
  ).option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(
25579
25801
  wrapCommand2("gitignore", "GITIGNORE_FAILED", async (logger5, options) => {
25802
+ const cliTargets = options.targets;
25803
+ const cliFeatures = options.features;
25804
+ const resolvedTargets = await resolveGitignoreTargets({ cliTargets });
25580
25805
  await gitignoreCommand(logger5, {
25581
- // eslint-disable-next-line no-type-assertion/no-type-assertion
25582
- targets: options.targets,
25583
- // eslint-disable-next-line no-type-assertion/no-type-assertion
25584
- features: options.features
25806
+ targets: resolvedTargets ? [...resolvedTargets] : void 0,
25807
+ features: cliFeatures
25585
25808
  });
25586
25809
  })
25587
25810
  );
package/dist/cli/index.js CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  RULESYNC_HOOKS_FILE_NAME,
25
25
  RULESYNC_HOOKS_RELATIVE_FILE_PATH,
26
26
  RULESYNC_IGNORE_RELATIVE_FILE_PATH,
27
+ RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH,
27
28
  RULESYNC_MCP_FILE_NAME,
28
29
  RULESYNC_MCP_RELATIVE_FILE_PATH,
29
30
  RULESYNC_MCP_SCHEMA_URL,
@@ -72,7 +73,7 @@ import {
72
73
  stringifyFrontmatter,
73
74
  toPosixPath,
74
75
  writeFileContent
75
- } from "../chunk-GB6XLCGB.js";
76
+ } from "../chunk-K4IN6URH.js";
76
77
 
77
78
  // src/cli/index.ts
78
79
  import { Command } from "commander";
@@ -1196,6 +1197,7 @@ var GITIGNORE_ENTRY_REGISTRY = [
1196
1197
  feature: "general",
1197
1198
  entry: "**/.claude/settings.local.json"
1198
1199
  },
1200
+ { target: "claudecode", feature: "general", entry: "**/.claude/*.lock" },
1199
1201
  // Cline
1200
1202
  { target: "cline", feature: "rules", entry: "**/.clinerules/" },
1201
1203
  { target: "cline", feature: "commands", entry: "**/.clinerules/workflows/" },
@@ -1304,6 +1306,11 @@ var GITIGNORE_ENTRY_REGISTRY = [
1304
1306
  { target: "opencode", feature: "skills", entry: "**/.opencode/skill/" },
1305
1307
  { target: "opencode", feature: "mcp", entry: "**/.opencode/plugins/" },
1306
1308
  { target: "opencode", feature: "general", entry: "**/.opencode/memories/" },
1309
+ {
1310
+ target: "opencode",
1311
+ feature: "general",
1312
+ entry: "**/.opencode/package-lock.json"
1313
+ },
1307
1314
  // Qwen Code
1308
1315
  { target: "qwencode", feature: "rules", entry: "**/QWEN.md" },
1309
1316
  { target: "qwencode", feature: "general", entry: "**/.qwen/memories/" },
@@ -1545,6 +1552,12 @@ async function importCommand(logger5, options) {
1545
1552
  if (!options.targets) {
1546
1553
  throw new CLIError("No tools found in --targets", ErrorCodes.IMPORT_FAILED);
1547
1554
  }
1555
+ if (!Array.isArray(options.targets)) {
1556
+ throw new CLIError(
1557
+ "--targets object form is not supported on the command line",
1558
+ ErrorCodes.IMPORT_FAILED
1559
+ );
1560
+ }
1548
1561
  if (options.targets.length > 1) {
1549
1562
  throw new CLIError("Only one tool can be imported at a time", ErrorCodes.IMPORT_FAILED);
1550
1563
  }
@@ -1613,7 +1626,8 @@ async function createConfigFile() {
1613
1626
  global: false,
1614
1627
  simulateCommands: false,
1615
1628
  simulateSubagents: false,
1616
- simulateSkills: false
1629
+ simulateSkills: false,
1630
+ gitignoreTargetsOnly: true
1617
1631
  },
1618
1632
  null,
1619
1633
  2
@@ -4078,6 +4092,35 @@ async function mcpCommand(logger5, { version }) {
4078
4092
  });
4079
4093
  }
4080
4094
 
4095
+ // src/cli/commands/resolve-gitignore-targets.ts
4096
+ import { join as join13 } from "path";
4097
+ var resolveGitignoreTargets = async ({
4098
+ cliTargets,
4099
+ cwd = process.cwd()
4100
+ }) => {
4101
+ if (cliTargets !== void 0) {
4102
+ return cliTargets;
4103
+ }
4104
+ const baseConfigPath = join13(cwd, RULESYNC_CONFIG_RELATIVE_FILE_PATH);
4105
+ const localConfigPath = join13(cwd, RULESYNC_LOCAL_CONFIG_RELATIVE_FILE_PATH);
4106
+ const [hasBase, hasLocal] = await Promise.all([
4107
+ fileExists(baseConfigPath),
4108
+ fileExists(localConfigPath)
4109
+ ]);
4110
+ if (!hasBase && !hasLocal) {
4111
+ return void 0;
4112
+ }
4113
+ const config = await ConfigResolver.resolve({});
4114
+ if (config.getGitignoreTargetsOnly()) {
4115
+ const targets = config.getTargets();
4116
+ if (targets.includes("agentsmd")) {
4117
+ return targets;
4118
+ }
4119
+ return [...targets, "agentsmd"];
4120
+ }
4121
+ return void 0;
4122
+ };
4123
+
4081
4124
  // src/lib/update.ts
4082
4125
  import * as crypto from "crypto";
4083
4126
  import * as fs from "fs";
@@ -4480,7 +4523,7 @@ function wrapCommand({
4480
4523
  }
4481
4524
 
4482
4525
  // src/cli/index.ts
4483
- var getVersion = () => "8.0.0";
4526
+ var getVersion = () => "8.2.0";
4484
4527
  function wrapCommand2(name, errorCode, handler) {
4485
4528
  return wrapCommand({ name, errorCode, handler, getVersion });
4486
4529
  }
@@ -4503,11 +4546,12 @@ var main = async () => {
4503
4546
  parseCommaSeparatedList
4504
4547
  ).option("-V, --verbose", "Verbose output").option("-s, --silent", "Suppress all output").action(
4505
4548
  wrapCommand2("gitignore", "GITIGNORE_FAILED", async (logger5, options) => {
4549
+ const cliTargets = options.targets;
4550
+ const cliFeatures = options.features;
4551
+ const resolvedTargets = await resolveGitignoreTargets({ cliTargets });
4506
4552
  await gitignoreCommand(logger5, {
4507
- // eslint-disable-next-line no-type-assertion/no-type-assertion
4508
- targets: options.targets,
4509
- // eslint-disable-next-line no-type-assertion/no-type-assertion
4510
- features: options.features
4553
+ targets: resolvedTargets ? [...resolvedTargets] : void 0,
4554
+ features: cliFeatures
4511
4555
  });
4512
4556
  })
4513
4557
  );