rulesync 8.4.0 → 8.6.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
@@ -10,6 +10,7 @@ import {
10
10
  ConsoleLogger,
11
11
  ErrorCodes,
12
12
  FETCH_CONCURRENCY_LIMIT,
13
+ GITIGNORE_DESTINATION_KEY,
13
14
  HooksProcessor,
14
15
  IgnoreProcessor,
15
16
  JsonLogger,
@@ -73,7 +74,7 @@ import {
73
74
  stringifyFrontmatter,
74
75
  toPosixPath,
75
76
  writeFileContent
76
- } from "../chunk-MMBSR2RA.js";
77
+ } from "../chunk-ILMHM7BF.js";
77
78
 
78
79
  // src/cli/index.ts
79
80
  import { Command } from "commander";
@@ -1336,6 +1337,9 @@ var GITIGNORE_ENTRY_REGISTRY = [
1336
1337
  entry: "**/.rovodev/.rulesync/"
1337
1338
  },
1338
1339
  { target: "rovodev", feature: "skills", entry: "**/.agents/skills/" },
1340
+ // Windsurf
1341
+ { target: "windsurf", feature: "skills", entry: "**/.windsurf/skills/" },
1342
+ { target: "windsurf", feature: "skills", entry: "**/.codeium/windsurf/skills/" },
1339
1343
  // Warp
1340
1344
  { target: "warp", feature: "rules", entry: "**/.warp/" },
1341
1345
  { target: "warp", feature: "rules", entry: "**/WARP.md" }
@@ -1415,13 +1419,14 @@ var warnInvalidFeatures = (features, logger5) => {
1415
1419
  }
1416
1420
  } else {
1417
1421
  for (const feature of Object.keys(targetFeatures)) {
1422
+ if (feature === GITIGNORE_DESTINATION_KEY) continue;
1418
1423
  warnOnce(feature);
1419
1424
  }
1420
1425
  }
1421
1426
  }
1422
1427
  }
1423
1428
  };
1424
- var filterGitignoreEntries = (params) => {
1429
+ var resolveGitignoreEntries = (params) => {
1425
1430
  const { targets, features, logger: logger5 } = params ?? {};
1426
1431
  if (targets && targets.length > 0) {
1427
1432
  warnInvalidTargets(targets, logger5);
@@ -1437,7 +1442,11 @@ var filterGitignoreEntries = (params) => {
1437
1442
  if (!isFeatureSelected(tag.feature, selectedTagTargets, features)) continue;
1438
1443
  if (seen.has(tag.entry)) continue;
1439
1444
  seen.add(tag.entry);
1440
- result.push(tag.entry);
1445
+ result.push({
1446
+ entry: tag.entry,
1447
+ target: selectedTagTargets,
1448
+ feature: tag.feature
1449
+ });
1441
1450
  }
1442
1451
  return result;
1443
1452
  };
@@ -1494,48 +1503,129 @@ var removeExistingRulesyncEntries = (content) => {
1494
1503
  }
1495
1504
  return result;
1496
1505
  };
1506
+ var groupEntriesByDestination = ({
1507
+ entries,
1508
+ resolveDestination
1509
+ }) => {
1510
+ const gitignore = /* @__PURE__ */ new Set();
1511
+ const gitattributes = /* @__PURE__ */ new Set();
1512
+ for (const entry of entries) {
1513
+ const selectedToolTargets = entry.target.filter(
1514
+ (target) => target !== "common"
1515
+ );
1516
+ const destinations = /* @__PURE__ */ new Set();
1517
+ for (const target of selectedToolTargets) {
1518
+ if (entry.feature === "general") {
1519
+ destinations.add(resolveDestination(target));
1520
+ } else {
1521
+ destinations.add(resolveDestination(target, entry.feature));
1522
+ }
1523
+ }
1524
+ if (destinations.has("gitattributes")) {
1525
+ gitattributes.add(entry.entry);
1526
+ }
1527
+ if (destinations.size === 0 || destinations.has("gitignore")) {
1528
+ gitignore.add(entry.entry);
1529
+ }
1530
+ }
1531
+ return {
1532
+ gitignore: [...gitignore],
1533
+ gitattributes: [...gitattributes]
1534
+ };
1535
+ };
1497
1536
  var gitignoreCommand = async (logger5, options) => {
1498
1537
  const gitignorePath = join2(process.cwd(), ".gitignore");
1499
- let gitignoreContent = "";
1500
- if (await fileExists(gitignorePath)) {
1501
- gitignoreContent = await readFileContent(gitignorePath);
1502
- }
1503
- const cleanedContent = removeExistingRulesyncEntries(gitignoreContent);
1504
- const filteredEntries = filterGitignoreEntries({
1538
+ const gitattributesPath = join2(process.cwd(), ".gitattributes");
1539
+ const config = await ConfigResolver.resolve({});
1540
+ const resolvedEntries = resolveGitignoreEntries({
1505
1541
  targets: options?.targets,
1506
1542
  features: options?.features,
1507
1543
  logger: logger5
1508
1544
  });
1509
- const existingEntries = new Set(
1510
- gitignoreContent.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !isRulesyncHeader(line))
1511
- );
1512
- const alreadyExistedEntries = filteredEntries.filter((entry) => existingEntries.has(entry));
1513
- const entriesToAdd = filteredEntries.filter((entry) => !existingEntries.has(entry));
1514
- const rulesyncBlock = [RULESYNC_HEADER, ...filteredEntries].join("\n");
1515
- const newContent = cleanedContent.trim() ? `${cleanedContent.trimEnd()}
1545
+ const { gitignore: gitignoreEntries, gitattributes: gitattributesEntries } = groupEntriesByDestination({
1546
+ entries: resolvedEntries,
1547
+ resolveDestination: (target, feature) => {
1548
+ if (feature === void 0 || feature === "general") {
1549
+ return config.getGitignoreDestination(target);
1550
+ }
1551
+ return config.getGitignoreDestination(target, feature);
1552
+ }
1553
+ });
1554
+ const updateRulesyncFile = async ({
1555
+ filePath,
1556
+ entries
1557
+ }) => {
1558
+ let content = "";
1559
+ if (await fileExists(filePath)) {
1560
+ content = await readFileContent(filePath);
1561
+ }
1562
+ const cleanedContent = removeExistingRulesyncEntries(content);
1563
+ const existingEntries = new Set(
1564
+ content.split("\n").map((line) => line.trim()).filter((line) => line !== "" && !isRulesyncHeader(line))
1565
+ );
1566
+ const alreadyExistedEntries = entries.filter((entry) => existingEntries.has(entry));
1567
+ const entriesToAdd = entries.filter((entry) => !existingEntries.has(entry));
1568
+ const rulesyncBlock = [RULESYNC_HEADER, ...entries].join("\n");
1569
+ const newContent = entries.length === 0 ? cleanedContent.trim() ? `${cleanedContent.trimEnd()}
1570
+ ` : "" : cleanedContent.trim() ? `${cleanedContent.trimEnd()}
1516
1571
 
1517
1572
  ${rulesyncBlock}
1518
1573
  ` : `${rulesyncBlock}
1519
1574
  `;
1520
- if (gitignoreContent === newContent) {
1575
+ if (content === newContent) {
1576
+ return { updated: false, alreadyExistedEntries, entriesToAdd: [] };
1577
+ }
1578
+ await writeFileContent(filePath, newContent);
1579
+ return { updated: true, alreadyExistedEntries, entriesToAdd };
1580
+ };
1581
+ const gitignoreResult = await updateRulesyncFile({
1582
+ filePath: gitignorePath,
1583
+ entries: gitignoreEntries
1584
+ });
1585
+ const gitattributesResult = await updateRulesyncFile({
1586
+ filePath: gitattributesPath,
1587
+ entries: gitattributesEntries
1588
+ });
1589
+ if (!gitignoreResult.updated && !gitattributesResult.updated) {
1521
1590
  if (logger5.jsonMode) {
1522
1591
  logger5.captureData("entriesAdded", []);
1523
1592
  logger5.captureData("gitignorePath", gitignorePath);
1524
- logger5.captureData("alreadyExisted", filteredEntries);
1593
+ logger5.captureData("gitattributesPath", gitattributesPath);
1594
+ logger5.captureData("alreadyExisted", [...gitignoreEntries, ...gitattributesEntries]);
1525
1595
  }
1526
- logger5.success(".gitignore is already up to date");
1596
+ logger5.success(".gitignore / .gitattributes are already up to date");
1527
1597
  return;
1528
1598
  }
1529
- await writeFileContent(gitignorePath, newContent);
1530
1599
  if (logger5.jsonMode) {
1531
- logger5.captureData("entriesAdded", entriesToAdd);
1600
+ logger5.captureData("entriesAdded", [
1601
+ ...gitignoreResult.entriesToAdd,
1602
+ ...gitattributesResult.entriesToAdd
1603
+ ]);
1532
1604
  logger5.captureData("gitignorePath", gitignorePath);
1533
- logger5.captureData("alreadyExisted", alreadyExistedEntries);
1605
+ logger5.captureData("gitattributesPath", gitattributesPath);
1606
+ logger5.captureData("alreadyExisted", [
1607
+ ...gitignoreResult.alreadyExistedEntries,
1608
+ ...gitattributesResult.alreadyExistedEntries
1609
+ ]);
1610
+ }
1611
+ if (gitignoreResult.updated) {
1612
+ logger5.success("Updated .gitignore with rulesync entries:");
1613
+ } else {
1614
+ logger5.success(".gitignore is already up to date");
1534
1615
  }
1535
- logger5.success("Updated .gitignore with rulesync entries:");
1536
- for (const entry of filteredEntries) {
1616
+ for (const entry of gitignoreEntries) {
1537
1617
  logger5.info(` ${entry}`);
1538
1618
  }
1619
+ if (gitattributesEntries.length > 0) {
1620
+ if (gitattributesResult.updated) {
1621
+ logger5.success("Updated .gitattributes with rulesync entries:");
1622
+ } else {
1623
+ logger5.success(".gitattributes is already up to date");
1624
+ }
1625
+ for (const entry of gitattributesEntries) {
1626
+ logger5.info(` ${entry}`);
1627
+ }
1628
+ }
1539
1629
  logger5.info("");
1540
1630
  logger5.info(
1541
1631
  "\u{1F4A1} If you're using Google Antigravity, note that rules, workflows, and skills won't load if they're gitignored."
@@ -2580,6 +2670,18 @@ async function fetchSourceViaGit(params) {
2580
2670
  arr.push({ relativePath: inner, content: file.content });
2581
2671
  skillFileMap.set(name, arr);
2582
2672
  }
2673
+ if (skillFileMap.size === 0 && !isWildcard && skillFilter.length === 1) {
2674
+ const [singleSkillName] = skillFilter;
2675
+ if (singleSkillName !== void 0) {
2676
+ const rootFiles = remoteFiles.filter((f) => f.relativePath.indexOf("/") === -1);
2677
+ if (rootFiles.length > 0) {
2678
+ skillFileMap.set(
2679
+ singleSkillName,
2680
+ rootFiles.map((f) => ({ relativePath: f.relativePath, content: f.content }))
2681
+ );
2682
+ }
2683
+ }
2684
+ }
2583
2685
  const allNames = [...skillFileMap.keys()];
2584
2686
  const filteredNames = isWildcard ? allNames : allNames.filter((n) => skillFilter.includes(n));
2585
2687
  if (locked) {
@@ -4523,7 +4625,7 @@ function wrapCommand({
4523
4625
  }
4524
4626
 
4525
4627
  // src/cli/index.ts
4526
- var getVersion = () => "8.4.0";
4628
+ var getVersion = () => "8.6.0";
4527
4629
  function wrapCommand2(name, errorCode, handler) {
4528
4630
  return wrapCommand({ name, errorCode, handler, getVersion });
4529
4631
  }