rulesync 7.15.1 → 7.16.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/index.cjs CHANGED
@@ -351,14 +351,21 @@ var ToolTargetSchema = import_mini2.z.enum(ALL_TOOL_TARGETS);
351
351
  var ToolTargetsSchema = import_mini2.z.array(ToolTargetSchema);
352
352
  var RulesyncTargetsSchema = import_mini2.z.array(import_mini2.z.enum(ALL_TOOL_TARGETS_WITH_WILDCARD));
353
353
 
354
- // src/config/config.ts
355
- function hasControlCharacters(value) {
354
+ // src/utils/validation.ts
355
+ function findControlCharacter(value) {
356
356
  for (let i = 0; i < value.length; i++) {
357
357
  const code = value.charCodeAt(i);
358
- if (code >= 0 && code <= 31 || code === 127) return true;
358
+ if (code >= 0 && code <= 31 || code === 127) {
359
+ return { position: i, hex: `0x${code.toString(16).padStart(2, "0")}` };
360
+ }
359
361
  }
360
- return false;
362
+ return null;
361
363
  }
364
+ function hasControlCharacters(value) {
365
+ return findControlCharacter(value) !== null;
366
+ }
367
+
368
+ // src/config/config.ts
362
369
  var SourceEntrySchema = import_mini3.z.object({
363
370
  source: import_mini3.z.string().check((0, import_mini3.minLength)(1, "source must be a non-empty string")),
364
371
  skills: (0, import_mini3.optional)(import_mini3.z.array(import_mini3.z.string())),
@@ -738,6 +745,7 @@ var import_node_path7 = require("path");
738
745
 
739
746
  // src/utils/frontmatter.ts
740
747
  var import_gray_matter = __toESM(require("gray-matter"), 1);
748
+ var import_js_yaml = require("js-yaml");
741
749
  function isPlainObject(value) {
742
750
  if (value === null || typeof value !== "object") return false;
743
751
  const prototype = Object.getPrototypeOf(value);
@@ -776,8 +784,55 @@ function deepRemoveNullishObject(obj) {
776
784
  }
777
785
  return result;
778
786
  }
779
- function stringifyFrontmatter(body, frontmatter) {
780
- const cleanFrontmatter = deepRemoveNullishObject(frontmatter);
787
+ function deepFlattenStringsValue(value) {
788
+ if (value === null || value === void 0) {
789
+ return void 0;
790
+ }
791
+ if (typeof value === "string") {
792
+ return value.replace(/\n+/g, " ").trim();
793
+ }
794
+ if (Array.isArray(value)) {
795
+ const cleanedArray = value.map((item) => deepFlattenStringsValue(item)).filter((item) => item !== void 0);
796
+ return cleanedArray;
797
+ }
798
+ if (isPlainObject(value)) {
799
+ const result = {};
800
+ for (const [key, val] of Object.entries(value)) {
801
+ const cleaned = deepFlattenStringsValue(val);
802
+ if (cleaned !== void 0) {
803
+ result[key] = cleaned;
804
+ }
805
+ }
806
+ return result;
807
+ }
808
+ return value;
809
+ }
810
+ function deepFlattenStringsObject(obj) {
811
+ if (!obj || typeof obj !== "object") {
812
+ return {};
813
+ }
814
+ const result = {};
815
+ for (const [key, val] of Object.entries(obj)) {
816
+ const cleaned = deepFlattenStringsValue(val);
817
+ if (cleaned !== void 0) {
818
+ result[key] = cleaned;
819
+ }
820
+ }
821
+ return result;
822
+ }
823
+ function stringifyFrontmatter(body, frontmatter, options) {
824
+ const { avoidBlockScalars = false } = options ?? {};
825
+ const cleanFrontmatter = avoidBlockScalars ? deepFlattenStringsObject(frontmatter) : deepRemoveNullishObject(frontmatter);
826
+ if (avoidBlockScalars) {
827
+ return import_gray_matter.default.stringify(body, cleanFrontmatter, {
828
+ engines: {
829
+ yaml: {
830
+ parse: (input) => (0, import_js_yaml.load)(input) ?? {},
831
+ stringify: (data) => (0, import_js_yaml.dump)(data, { lineWidth: -1 })
832
+ }
833
+ }
834
+ });
835
+ }
781
836
  return import_gray_matter.default.stringify(body, cleanFrontmatter);
782
837
  }
783
838
  function parseFrontmatter(content, filePath) {
@@ -1896,7 +1951,7 @@ var CursorCommand = class _CursorCommand extends ToolCommand {
1896
1951
  }
1897
1952
  super({
1898
1953
  ...rest,
1899
- fileContent: stringifyFrontmatter(body, frontmatter)
1954
+ fileContent: stringifyFrontmatter(body, frontmatter, { avoidBlockScalars: true })
1900
1955
  });
1901
1956
  this.frontmatter = frontmatter;
1902
1957
  this.body = body;
@@ -5813,7 +5868,10 @@ var RulesyncMcp = class _RulesyncMcp extends RulesyncFile {
5813
5868
  stripMcpServerFields(fields) {
5814
5869
  if (fields.length === 0) return this;
5815
5870
  const filteredServers = Object.fromEntries(
5816
- Object.entries(this.json.mcpServers).map(([name, config]) => [name, (0, import_object.omit)(config, fields)])
5871
+ Object.entries(this.json.mcpServers).map(([name, config]) => [
5872
+ name,
5873
+ Object.fromEntries(Object.entries(config).filter(([key]) => !fields.includes(key)))
5874
+ ])
5817
5875
  );
5818
5876
  return new _RulesyncMcp({
5819
5877
  baseDir: this.baseDir,
@@ -6319,12 +6377,26 @@ var CursorMcp = class _CursorMcp extends ToolMcp {
6319
6377
  json;
6320
6378
  constructor(params) {
6321
6379
  super(params);
6322
- this.json = this.fileContent !== void 0 ? JSON.parse(this.fileContent) : {};
6380
+ if (this.fileContent !== void 0) {
6381
+ try {
6382
+ this.json = JSON.parse(this.fileContent);
6383
+ } catch (error) {
6384
+ throw new Error(
6385
+ `Failed to parse Cursor MCP config at ${(0, import_node_path48.join)(this.relativeDirPath, this.relativeFilePath)}: ${formatError(error)}`,
6386
+ { cause: error }
6387
+ );
6388
+ }
6389
+ } else {
6390
+ this.json = {};
6391
+ }
6323
6392
  }
6324
6393
  getJson() {
6325
6394
  return this.json;
6326
6395
  }
6327
- static getSettablePaths() {
6396
+ isDeletable() {
6397
+ return !this.global;
6398
+ }
6399
+ static getSettablePaths(_options) {
6328
6400
  return {
6329
6401
  relativeDirPath: ".cursor",
6330
6402
  relativeFilePath: "mcp.json"
@@ -6332,41 +6404,62 @@ var CursorMcp = class _CursorMcp extends ToolMcp {
6332
6404
  }
6333
6405
  static async fromFile({
6334
6406
  baseDir = process.cwd(),
6335
- validate = true
6407
+ validate = true,
6408
+ global = false
6336
6409
  }) {
6337
- const fileContent = await readFileContent(
6338
- (0, import_node_path48.join)(
6339
- baseDir,
6340
- this.getSettablePaths().relativeDirPath,
6341
- this.getSettablePaths().relativeFilePath
6342
- )
6343
- );
6410
+ const paths = this.getSettablePaths({ global });
6411
+ const filePath = (0, import_node_path48.join)(baseDir, paths.relativeDirPath, paths.relativeFilePath);
6412
+ const fileContent = await readFileContentOrNull(filePath) ?? '{"mcpServers":{}}';
6413
+ let json;
6414
+ try {
6415
+ json = JSON.parse(fileContent);
6416
+ } catch (error) {
6417
+ throw new Error(
6418
+ `Failed to parse Cursor MCP config at ${(0, import_node_path48.join)(paths.relativeDirPath, paths.relativeFilePath)}: ${formatError(error)}`,
6419
+ { cause: error }
6420
+ );
6421
+ }
6422
+ const newJson = { ...json, mcpServers: json.mcpServers ?? {} };
6344
6423
  return new _CursorMcp({
6345
6424
  baseDir,
6346
- relativeDirPath: this.getSettablePaths().relativeDirPath,
6347
- relativeFilePath: this.getSettablePaths().relativeFilePath,
6348
- fileContent,
6349
- validate
6425
+ relativeDirPath: paths.relativeDirPath,
6426
+ relativeFilePath: paths.relativeFilePath,
6427
+ fileContent: JSON.stringify(newJson, null, 2),
6428
+ validate,
6429
+ global
6350
6430
  });
6351
6431
  }
6352
- static fromRulesyncMcp({
6432
+ static async fromRulesyncMcp({
6353
6433
  baseDir = process.cwd(),
6354
6434
  rulesyncMcp,
6355
- validate = true
6435
+ validate = true,
6436
+ global = false
6356
6437
  }) {
6357
- const json = rulesyncMcp.getJson();
6358
- const mcpServers = isMcpServers(json.mcpServers) ? json.mcpServers : {};
6438
+ const paths = this.getSettablePaths({ global });
6439
+ const fileContent = await readOrInitializeFileContent(
6440
+ (0, import_node_path48.join)(baseDir, paths.relativeDirPath, paths.relativeFilePath),
6441
+ JSON.stringify({ mcpServers: {} }, null, 2)
6442
+ );
6443
+ let json;
6444
+ try {
6445
+ json = JSON.parse(fileContent);
6446
+ } catch (error) {
6447
+ throw new Error(
6448
+ `Failed to parse Cursor MCP config at ${(0, import_node_path48.join)(paths.relativeDirPath, paths.relativeFilePath)}: ${formatError(error)}`,
6449
+ { cause: error }
6450
+ );
6451
+ }
6452
+ const rulesyncJson = rulesyncMcp.getJson();
6453
+ const mcpServers = isMcpServers(rulesyncJson.mcpServers) ? rulesyncJson.mcpServers : {};
6359
6454
  const transformedServers = convertEnvToCursorFormat(mcpServers);
6360
- const cursorConfig = {
6361
- mcpServers: transformedServers
6362
- };
6363
- const fileContent = JSON.stringify(cursorConfig, null, 2);
6455
+ const cursorConfig = { ...json, mcpServers: transformedServers };
6364
6456
  return new _CursorMcp({
6365
6457
  baseDir,
6366
- relativeDirPath: this.getSettablePaths().relativeDirPath,
6367
- relativeFilePath: this.getSettablePaths().relativeFilePath,
6368
- fileContent,
6369
- validate
6458
+ relativeDirPath: paths.relativeDirPath,
6459
+ relativeFilePath: paths.relativeFilePath,
6460
+ fileContent: JSON.stringify(cursorConfig, null, 2),
6461
+ validate,
6462
+ global
6370
6463
  });
6371
6464
  }
6372
6465
  toRulesyncMcp() {
@@ -6390,14 +6483,16 @@ var CursorMcp = class _CursorMcp extends ToolMcp {
6390
6483
  static forDeletion({
6391
6484
  baseDir = process.cwd(),
6392
6485
  relativeDirPath,
6393
- relativeFilePath
6486
+ relativeFilePath,
6487
+ global = false
6394
6488
  }) {
6395
6489
  return new _CursorMcp({
6396
6490
  baseDir,
6397
6491
  relativeDirPath,
6398
6492
  relativeFilePath,
6399
6493
  fileContent: "{}",
6400
- validate: false
6494
+ validate: false,
6495
+ global
6401
6496
  });
6402
6497
  }
6403
6498
  };
@@ -7226,7 +7321,7 @@ var toolMcpFactories = /* @__PURE__ */ new Map([
7226
7321
  class: CursorMcp,
7227
7322
  meta: {
7228
7323
  supportsProject: true,
7229
- supportsGlobal: false,
7324
+ supportsGlobal: true,
7230
7325
  supportsEnabledTools: false,
7231
7326
  supportsDisabledTools: false
7232
7327
  }
@@ -7939,9 +8034,15 @@ var import_node_path61 = require("path");
7939
8034
  var DirFeatureProcessor = class {
7940
8035
  baseDir;
7941
8036
  dryRun;
7942
- constructor({ baseDir = process.cwd(), dryRun = false }) {
8037
+ avoidBlockScalars;
8038
+ constructor({
8039
+ baseDir = process.cwd(),
8040
+ dryRun = false,
8041
+ avoidBlockScalars = false
8042
+ }) {
7943
8043
  this.baseDir = baseDir;
7944
8044
  this.dryRun = dryRun;
8045
+ this.avoidBlockScalars = avoidBlockScalars;
7945
8046
  }
7946
8047
  /**
7947
8048
  * Return tool targets that this feature supports.
@@ -7967,7 +8068,9 @@ var DirFeatureProcessor = class {
7967
8068
  let mainFileContent;
7968
8069
  if (mainFile) {
7969
8070
  const mainFilePath = (0, import_node_path61.join)(dirPath, mainFile.name);
7970
- const content = stringifyFrontmatter(mainFile.body, mainFile.frontmatter);
8071
+ const content = stringifyFrontmatter(mainFile.body, mainFile.frontmatter, {
8072
+ avoidBlockScalars: this.avoidBlockScalars
8073
+ });
7971
8074
  mainFileContent = addTrailingNewline(content);
7972
8075
  const existingContent = await readFileContentOrNull(mainFilePath);
7973
8076
  if (existingContent !== mainFileContent) {
@@ -10706,7 +10809,7 @@ var SkillsProcessor = class extends DirFeatureProcessor {
10706
10809
  getFactory = defaultGetFactory4,
10707
10810
  dryRun = false
10708
10811
  }) {
10709
- super({ baseDir, dryRun });
10812
+ super({ baseDir, dryRun, avoidBlockScalars: toolTarget === "cursor" });
10710
10813
  const result = SkillsProcessorToolTargetSchema.safeParse(toolTarget);
10711
10814
  if (!result.success) {
10712
10815
  throw new Error(
@@ -11787,7 +11890,7 @@ var CursorSubagent = class _CursorSubagent extends ToolSubagent {
11787
11890
  ...cursorSection
11788
11891
  };
11789
11892
  const body = rulesyncSubagent.getBody();
11790
- const fileContent = stringifyFrontmatter(body, cursorFrontmatter);
11893
+ const fileContent = stringifyFrontmatter(body, cursorFrontmatter, { avoidBlockScalars: true });
11791
11894
  const paths = this.getSettablePaths({ global });
11792
11895
  return new _CursorSubagent({
11793
11896
  baseDir,
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  generate,
7
7
  importFromTool,
8
8
  logger
9
- } from "./chunk-L5AQUWUM.js";
9
+ } from "./chunk-E5YWRHGW.js";
10
10
 
11
11
  // src/index.ts
12
12
  async function generate2(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rulesync",
3
- "version": "7.15.1",
3
+ "version": "7.16.0",
4
4
  "description": "Unified AI rules management CLI tool that generates configuration files for various AI development tools",
5
5
  "keywords": [
6
6
  "ai",
@@ -56,7 +56,7 @@
56
56
  "commander": "14.0.3",
57
57
  "consola": "3.4.2",
58
58
  "effect": "3.19.19",
59
- "es-toolkit": "1.44.0",
59
+ "es-toolkit": "1.45.1",
60
60
  "fastmcp": "3.33.0",
61
61
  "globby": "16.1.1",
62
62
  "gray-matter": "4.0.3",
@@ -67,28 +67,28 @@
67
67
  "zod": "4.3.6"
68
68
  },
69
69
  "devDependencies": {
70
- "@anthropic-ai/claude-agent-sdk": "0.2.59",
70
+ "@anthropic-ai/claude-agent-sdk": "0.2.69",
71
71
  "@eslint/js": "9.39.2",
72
72
  "@openrouter/sdk": "0.9.11",
73
73
  "@secretlint/secretlint-rule-preset-recommend": "11.3.1",
74
74
  "@tsconfig/node24": "24.0.4",
75
75
  "@types/js-yaml": "4.0.9",
76
- "@types/node": "25.3.1",
77
- "@typescript/native-preview": "7.0.0-dev.20260225.1",
76
+ "@types/node": "25.3.3",
77
+ "@typescript/native-preview": "7.0.0-dev.20260304.1",
78
78
  "@vitest/coverage-v8": "4.0.18",
79
79
  "cspell": "9.7.0",
80
80
  "eslint": "9.39.2",
81
81
  "eslint-plugin-import": "2.32.0",
82
82
  "eslint-plugin-no-type-assertion": "1.3.0",
83
- "eslint-plugin-oxlint": "1.50.0",
83
+ "eslint-plugin-oxlint": "1.51.0",
84
84
  "eslint-plugin-strict-dependencies": "1.3.31",
85
85
  "eslint-plugin-zod-import": "0.3.0",
86
86
  "knip": "5.85.0",
87
- "lint-staged": "16.2.7",
88
- "oxfmt": "0.35.0",
89
- "oxlint": "1.50.0",
90
- "repomix": "1.11.1",
91
- "resend": "6.9.2",
87
+ "lint-staged": "16.3.2",
88
+ "oxfmt": "0.36.0",
89
+ "oxlint": "1.51.0",
90
+ "repomix": "1.12.0",
91
+ "resend": "6.9.3",
92
92
  "secretlint": "11.3.1",
93
93
  "simple-git-hooks": "2.13.1",
94
94
  "sort-package-json": "3.6.1",