* fix(windows): preserve a file's existing line endings on rewrite
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>
* fix(windows): keep removeMarkerBlock on the file's own newline
Addresses the two review points and one more instance of the same bug.
`removeMarkerBlock` collapses a run of blank lines, and rebuilt the
separator as a bare '\n' regardless of the file it came from. Removing a
managed block from a CRLF CLAUDE.md or rc file therefore left a lone LF
behind - the mixed ending this PR exists to prevent. It now uses the
newline it already detects for the trailing ending.
Test fixes:
- `marker-updates.test.ts`: close `describe('line endings')` so
`removeMarkerBlock` is no longer nested inside `updateFileWithMarkers`.
- `path-containment.test.ts`: exercise `FileSystemUtils.assertPathWithin`
and `resolveProjectArtifactPath` instead of a private copy of the
containment logic, which passed whatever the production guard did. The
guard had no coverage at all; a prefix-comparison regression now fails
the sibling case.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(windows): read the file's convention consistently, and only ENOENT as absent
Three follow-ups from CodeRabbit's pass on the superseding PR.
`writeUpdatedSpec` turned every read error into "no previous file", so an
existing but unreadable spec was treated as absent and rewritten as LF.
Only ENOENT means absent now; everything else propagates.
`removeMarkerBlock` chose CRLF whenever the content held one anywhere, so
a single stray CRLF in an otherwise-LF file pulled the whole rewrite to
CRLF. It now uses detectLineEnding, the same dominant-ending reading
matchLineEnding uses, so both write paths agree.
Added the alias-path case the containment suite was missing: a directory
link inside the root that resolves outside it. That exercises the
canonicalization half of the guard, which a lexical check cannot do - the
link's own path looks contained. Skipped where creating a directory link
needs a privilege the runner lacks.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Travis James <travis@tribehealthsolutions.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix: correct CLI version mismatch and add release guard\n\n- Add patch changeset for 0.8.1\n- Add pack-version check to validate tarball version\n- Update release script to include versioning and guard
* chore(release): simplify release script and harden pack-version-check\n\n- Run pack-version check before publish only\n- Remove redundant changeset version + explicit build in release script\n- Always cleanup temp dir and tgz\n- Quieter, faster npm install during guard
* chore(release): clarify CI vs local release scripts; refine pack guard\n\n- Add scripts: release:ci (no version), release:local (runs changeset version)\n- Workflow uses release:ci to ensure version PR bump precedes publish\n- Pack guard: document npm vs pnpm choice; improve JSON fallback handling