Skip to content

fix: enforce one blank line between concatenated rules#28

Merged
Jercik merged 4 commits intomainfrom
fix/blank-line-between-rules
Feb 6, 2026
Merged

fix: enforce one blank line between concatenated rules#28
Jercik merged 4 commits intomainfrom
fix/blank-line-between-rules

Conversation

@Jercik
Copy link
Owner

@Jercik Jercik commented Feb 6, 2026

Summary

  • enforce exactly one blank line between adjacent rules when generating combined output
  • normalize boundary newlines so extra/missing spacing is corrected consistently
  • update sync-related tests to assert the new formatting behavior

Testing

  • pnpm test -- src/core/concatenate-rules.test.ts src/core/sync.test.ts src/core/sync-global.test.ts

Copilot AI review requested due to automatic review settings February 6, 2026 16:06
github-actions[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

Copy link
Owner Author

@Jercik Jercik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approach Review: This solves the spacing issue, but there’s a simpler/less stateful way to guarantee exactly one blank line between all adjacent rules: normalize each rule’s content (strip leading + trailing newlines) and then join("\n\n"). That avoids boundary-specific logic and makes the invariant obvious regardless of which rule happens to be first/middle/last.


Approach review by Copilot (gpt-5.2)

}

let combinedContent = firstRule.content;
let combinedContent = firstRule.content.replace(/\n+$/u, "");
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Alternative approach: Consider normalizing each rule independently and then joining: e.g. return rules.map(r => r.content.replace(/^\n+|\n+$/gu, "")).join("\n\n"); This makes the "exactly one blank line" invariant explicit and avoids per-boundary edge cases.

github-actions[bot]

This comment was marked as resolved.

Jercik

This comment was marked as resolved.

github-actions[bot]

This comment was marked as resolved.

Jercik

This comment was marked as resolved.

github-actions[bot]

This comment was marked as resolved.

This comment was marked as resolved.

Jercik

This comment was marked as resolved.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary: No bugs or security issues found. 1 low observation, 2 info-level notes.


Code review by Claude (opus)

}

combinedContent = `${combinedContent}\n${rule.content}`;
combinedContent = `${normalizedCombinedContent}\n\n${normalizedRuleContent}`;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Info: Clean implementation. The strip-then-rejoin approach handles all the boundary normalization cases well—CRLF, multiple trailing newlines, empty rules. The empty-content guards on lines 23-31 correctly prevent orphan \n\n separators when rules are whitespace-only.

@@ -13,12 +14,23 @@ export function concatenateRules(rules: Rule[]): string {
let combinedContent = firstRule.content;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Low: The first rule's content is assigned directly without normalization (line 14), which means a single whitespace-only rule like {content: "\n\n"} returns "\n\n" verbatim, while multiple whitespace-only rules collapse to "". The single-rule test on line 12 of the test file confirms this is intentional ("preserves content exactly when only one rule is present"), and in practice rule files won't be pure whitespace, so this is fine. Just noting the asymmetry for awareness—author's discretion on whether to normalize the single-rule path too.

expect(result).toBe("# A\n\n# B\n\n# C");
});

it("normalizes CRLF boundaries to one LF blank line", () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Info: Good addition of the CRLF test case. Verified that the regex \r?\n correctly strips CRLF at boundaries while preserving CRLF within rule content (e.g., "# A\r\nfoo\r\n" joined with "# B" keeps the internal \r\n intact and only normalizes the boundary).

Copy link
Owner Author

@Jercik Jercik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary: Found 1 medium issue and 1 info-level observation.


Code review by Copilot (gpt-5.2)

/(?:\r?\n)+$/u,
"",
);
const normalizedRuleContent = rule.content.replace(/^(?:\r?\n)+/u, "");
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium: The empty-rule check only strips leading newlines (rule.content.replace(/^(?:\r?\n)+/u, "")), so a rule file containing only whitespace (e.g., spaces/tabs + newlines) won’t be treated as empty and may introduce stray whitespace into generated AGENTS.md (which can create unintended Markdown hard-breaks). Consider treating rules as empty when normalizedRuleContent.trim() === "" (or equivalent whitespace-only check) before concatenation.

if (combinedContent.endsWith("\n") || rule.content.startsWith("\n")) {
combinedContent = `${combinedContent}${rule.content}`;
const normalizedCombinedContent = combinedContent.replace(
/(?:\r?\n)+$/u,
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ Info: Good boundary normalization: trimming trailing/leading newline runs with /(?:\r?\n)+$/u and joining with \n\n makes spacing deterministic and also normalizes CRLF input to LF at the join points (covered by the new CRLF test in src/core/concatenate-rules.test.ts).

@Jercik Jercik merged commit a209f97 into main Feb 6, 2026
8 of 9 checks passed
@Jercik Jercik deleted the fix/blank-line-between-rules branch February 6, 2026 16:31
@github-actions
Copy link

github-actions bot commented Feb 6, 2026

🎉 This PR is included in version 5.9.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants