Skip to content

fix(windows): preserve a file's existing line endings on rewrite - #1

Merged
GQAdonis merged 1 commit into
mainfrom
fix/windows-line-ending-preservation
Sep 21, 2026
Merged

GQAdonis merged 1 commit into
mainfrom
fix/windows-line-ending-preservation

Conversation

@GQAdonis

Copy link
Copy Markdown

Brings the Windows line-ending fix into our fork's main.

Also submitted upstream as Fission-AI#1936 (tracking issue Fission-AI#1935). Merging here does not depend on upstream accepting it; if they do, this branch is the same commit and will reconcile cleanly.

What

The parsers normalize CRLF to LF on read, but nothing restored the file's convention on write. On a Windows checkout (core.autocrlf=true) that turned every rewrite into a whole-file change.

Archiving a change that adds one requirement to a CRLF spec, before and after:

before:  21	14	openspec/specs/demo/spec.md
after:    7	 0	openspec/specs/demo/spec.md

The file also stays CRLF instead of being silently converted to LF.

Changes

  • specs-apply — writeUpdatedSpec writes an updated spec back with the convention the file already used. A spec that does not exist yet stays LF.
  • file-system — same fix for updateFileWithMarkers, so installing or updating shell completions in a CRLF .bashrc/.zshrc no longer leaves mixed endings, which bash reports as $'\r': command not found.
  • pack-version-check — spawns npm through cross-spawn, since execFile cannot resolve npm.cmd on Windows.

New src/utils/line-endings.ts holds the detect/restore pair. The internal LF-only invariant is unchanged — only the write boundary differs.

Tests

29 new tests covering the CRLF round trip through the real write paths, the utility itself (including the CRLF/LF tie and a lone CR), marker insert/replace, and path containment under Windows case variance.

pnpm build, pnpm test, pnpm exec tsc --noEmit, pnpm lint all pass locally.

Two tests fail on main before this branch (artifact-workflow › Cursor skills, config-profile › in-process apply) and still fail after — pre-existing and unrelated.

Verified, not assumed

openspec validate was not broken: a CRLF copy of this repo's own 62-item openspec/ tree produces byte-identical validate output. isPathWithin already handles Windows case variance correctly. Both were left alone; the containment tests exist to keep it that way.

Written with Claude Code (Claude Opus 5), verified by reproduction — the 21/14 → 7/0 numbers are measured from an actual openspec archive run.

The parsers normalize CRLF to LF on read, but nothing restored it on
write. On a Windows checkout (core.autocrlf=true) that turned every
rewrite into a whole-file change: applying a delta that added one
requirement produced a diff of 21 insertions and 14 deletions, burying
the real change. Archiving the same spec now writes 7 insertions and 0
deletions.

- specs-apply: write an updated spec back with the convention the file
  already used; a spec that does not exist yet stays LF.
- file-system: same fix for updateFileWithMarkers, so installing shell
  completions into a CRLF .bashrc/.zshrc no longer leaves mixed endings,
  which bash reports as "$'\r': command not found".
- pack-version-check: spawn npm through cross-spawn, since execFile
  cannot resolve npm.cmd on Windows.

Adds src/utils/line-endings.ts for the detect/restore pair, plus tests
pinning the CRLF round trip through the real write paths. Also adds
regression tests for path containment under Windows case variance:
path.win32.relative already folds case, and those tests pin both halves
of the contract so a future "case-insensitive" change cannot quietly
loosen the traversal guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@GQAdonis
GQAdonis merged commit 441f613 into main Sep 21, 2026
12 of 13 checks passed
@GQAdonis
GQAdonis deleted the fix/windows-line-ending-preservation branch September 21, 2026 10:37
@GQAdonis
GQAdonis restored the fix/windows-line-ending-preservation branch September 21, 2026 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant