Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 63 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,30 @@ jobs:
- name: Shell scripts parse
run: bash -n install.sh tools/doctor.sh bench/run.sh bench/task/verify.sh

- name: PowerShell scripts parse
shell: pwsh
run: |
$bad = @()
foreach ($f in 'install.ps1', 'tools/doctor.ps1') {
$tokens = $null; $errors = $null
[System.Management.Automation.Language.Parser]::ParseFile((Resolve-Path $f), [ref]$tokens, [ref]$errors) | Out-Null
if ($errors) { $bad += "${f}:"; $bad += $errors }
}
if ($bad) { $bad | Write-Host; exit 1 }
Write-Host 'powershell scripts parse'

- name: Python components compile
run: python3 -m py_compile claude/hooks/*.py claude/cli/*.py bench/score.py bench/task/loglib/*.py

- name: Settings snippet is valid JSON
run: python3 -c "import json; json.load(open('claude/settings/settings-snippet.json'))"
- name: Settings snippets are valid JSON (all four)
run: |
python3 - <<'EOF'
import json
for name in ("settings-snippet.json", "settings-snippet-small.json",
"settings-snippet-windows.json", "settings-snippet-windows-small.json"):
json.load(open(f"claude/settings/{name}"))
print("snippets ok")
EOF

- name: Workflow scripts compile
run: node tools/check-workflows.mjs
Expand Down Expand Up @@ -58,3 +77,45 @@ jobs:
total=$(/tmp/bench-venv/bin/python bench/score.py /tmp/pristine-inst /tmp/bench-venv/bin/python \
| python3 -c "import json,sys; print(json.load(sys.stdin)['total'])")
test "$total" = "1" || { echo "pristine anchor drifted: scored $total/15, expected 1/15"; exit 1; }

windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- name: Python components compile
shell: pwsh
run: |
$files = Get-ChildItem claude/hooks/*.py, claude/cli/*.py | ForEach-Object FullName
python -m py_compile @files
if ($LASTEXITCODE -ne 0) { exit 1 }

- name: Install script end-to-end (twice, idempotent)
shell: pwsh
run: |
$env:CLAUDE_DIR = Join-Path $env:RUNNER_TEMP 'fake-claude'
./install.ps1 *>&1 | Out-Null
# install.ps1 reports via Write-Host (information stream) — merge all
# streams or the capture is empty and this check can never pass.
$second = ./install.ps1 *>&1 | Out-String
if ($second -notmatch 'unchanged') { Write-Host 'second run not idempotent'; exit 1 }
foreach ($rel in 'skills/fable/SKILL.md', 'skills/webdesign/references/german-market.md', 'cli/mem.py') {
if (-not (Test-Path (Join-Path $env:CLAUDE_DIR $rel))) { Write-Host "missing: $rel"; exit 1 }
}
# backups must never create loadable duplicates inside skills/
$installed = (Get-ChildItem (Join-Path $env:CLAUDE_DIR 'skills')).Count
$shipped = (Get-ChildItem claude/skills).Count
if ($installed -ne $shipped) { Write-Host "skills dir has $installed entries, expected $shipped"; exit 1 }

- name: Doctor verifies the merged install
shell: pwsh
run: |
$env:CLAUDE_DIR = Join-Path $env:RUNNER_TEMP 'fake-claude'
Copy-Item claude/settings/settings-snippet-windows.json (Join-Path $env:CLAUDE_DIR 'settings.json')
./tools/doctor.ps1

- name: Unit tests (POSIX-installer tests self-skip)
shell: pwsh
run: |
python -m pip -q install pytest
python -m pytest tests/ -q
51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,56 @@
# Changelog

## v1.9 — 2026-07-09

Windows pass + README redesign. The kit's discipline layer was always OS-portable — the
hooks and the mem CLI are stdlib Python with `expanduser`/`os.path` throughout, and the
privacy guard already probed for case-insensitive filesystems — but the delivery layer
(bash installer, bash doctor, `python3 ~/...` hook commands) was POSIX-only, so on native
Windows the kit was silently uninstallable. This release makes Windows a first-class
install target with the same no-silently-inert guarantees, and restructures the README
around the reader (install first, story second, inventory collapsible).

### Added
- **`install.ps1` — native Windows installer, full parity with `install.sh`.** Same
out-of-tree backups, same hash-based idempotency, same `.fable-manifest` skill
tracking with stale-file pruning, same `-Tier small` / `-StrongModel <m>` flags,
same never-edit-settings posture. Parity is enforced, not asserted: tests pin the
skill manifests byte-for-byte across both installers and require that running
`install.ps1` over a bash-installed tree reports everything unchanged (a dual-boot /
WSL+native machine must never churn backups). Python launcher discovery tries
`py -3`, `python`, `python3` in order and soft-fails like the bash bootstrap.
- **`tools/doctor.ps1` — native Windows doctor, same checks and exit codes as
`doctor.sh`**, plus two Windows-only diagnoses: Git Bash present (on native Windows
it is the hook command shell — absent means every hook is inert, a FAIL), and a
warning when `settings.json` wires hooks through `python3` (a Unix snippet merged on
Windows never fires — the exact silently-inert failure the doctor exists to catch).
- **Windows settings snippets** (`settings-snippet-windows.json`,
`-windows-small.json`): identical to the Unix snippets except hook commands invoke
`python` (Windows Pythons ship no `python3` launcher). `tests/test_windows_port.py`
keeps them in lockstep structurally — any drift from the tested Unix configuration
fails CI.
- **`tests/test_windows_port.py`** — snippet-mirror guards (run everywhere) plus
pwsh-gated end-to-end tests mirroring `test_install_doctor.py`: install twice
(idempotent, no backup churn), user files in skill dirs preserved, formerly-shipped
files pruned, strong-model pin byte-identical with bash, doctor pass/fail/unwired
scenarios. The POSIX-installer tests now self-skip on Windows instead of driving
bash scripts through Git Bash.
- **CI `windows` job** (`windows-latest`): compiles every Python component, runs
`install.ps1` end-to-end twice, verifies the merged install with `doctor.ps1`, and
runs the unit suite. The Ubuntu job additionally parse-checks both `.ps1` scripts
and validates all four settings snippets.

### Changed
- **README redesigned.** Install (macOS/Linux and Windows side by side, with a
collapsible Windows-notes block) now leads; the origin story is two paragraphs, not
a wall; the full annotated file tree is collapsible behind a six-row component-layer
table; badges + section nav on top. Every honest-limits paragraph survives, plus a
new one: the Windows port is CI-verified end-to-end but has not yet had a live
Claude Code session pass on native Windows.
- Shipped components that name the mem CLI (`claude/CLAUDE.md` doctrine line,
`memory-search` skill, `/memory-gc` agent prompts) now note the Windows spelling:
`python` wherever a command says `python3`.

## v1.8 — 2026-07-08

Memory pass: the kit stops forgetting across projects. Native auto-memory is per-git-repo,
Expand Down
Loading
Loading