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
86 changes: 86 additions & 0 deletions .claude/skills/bench/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
name: bench
description: >
Run the search benchmarks and regenerate the tables in docs/performance.md
with fresh, real numbers. Use when the user asks to "run the benchmarks",
"update performance numbers", or after a change that should affect speed.
---

# bench

`docs/performance.md` must only ever contain **measured** numbers. This skill
produces them; never hand-edit timings.

## 1. Environment

```bash
pip install -e ".[dev]"
python -c "import platform, os; print(platform.processor() or platform.machine(), os.cpu_count(), 'cores'); print(platform.python_version())"
```

Record the machine and Python version - the tables in `docs/performance.md`
are labelled with them and results are machine-specific.

## 2. Fixed-depth single-process search (start position)

Regenerates the "After" column of the "Single process, fixed-depth `search()`"
table. A fresh `Negamax` per depth (cold TT), un-armed clock so it never aborts.
"Nodes" is `searcher.nodes` (main search + quiescence).

```python
import time
from pychess.clock import Clock
from pychess.constants import INF
from pychess.eval_board import EvalBoard
from pychess.evaluation import PestoEvaluator
from pychess.move_ordering import MoveOrderer
from pychess.negamax import Negamax
from pychess.transposition import TranspositionTable

for depth in range(2, 8):
s = Negamax(PestoEvaluator(), MoveOrderer(), TranspositionTable(), Clock())
t = time.time()
s.search(EvalBoard(), -INF, INF, depth)
print(f"| {depth} | {time.time() - t:.3f}s / {s.nodes:,} |")
```

## 3. Lazy SMP (start position)

Regenerates the "After" column of the "What `go` runs today: Lazy SMP" table.
Use a generous `movetime` so the run isn't deadline-capped.

```python
import time
from pychess.eval_board import EvalBoard
from pychess.engine import Engine

for depth in (6, 7, 8):
t = time.time()
r = Engine().search(EvalBoard(), {"depth": depth, "movetime": 120000})
print(f"| {depth} | reached d{r.depth} | {time.time() - t:.2f}s | {r.nodes:,} nodes |")
```

Run each script with `.venv/bin/python` (or the active env). Steps 2 and 3
together take roughly a minute.

## 4. Update `docs/performance.md`

- Replace the "After (time / nodes)" cells in the fixed-depth table and the
Lazy SMP table with the new numbers.
- Update the machine / Python-version sentence at the top of the file.
- **Leave alone:** the "Before" columns (pre-review engine, not reproducible),
the M1 reference paragraph, and the "Single-process iterative deepening
(removed)" table.
- Recompute the "speed-up" column from the new numbers.

## 5. Cross-check

If speed changed materially, check whether these still hold and flag (don't
silently rewrite) any that drifted:

- the `~45-50k nps` figure and the "depth ~6-8 in blitz" notes in
`docs/engine-strength.md`.

## 6. Report

Show the old vs new table rows and the environment they were measured on.
54 changes: 54 additions & 0 deletions .claude/skills/pr-check/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
name: pr-check
description: >
Run the full CI gate locally and check the softer PR requirements (tests,
changelog, docs) against the diff. Use before opening or updating a pull
request, or when the user asks to "check my PR" / "am I ready to push".
---

# pr-check

Mirror what CI and review will check, and report a pass/fail list matching
`.github/PULL_REQUEST_TEMPLATE.md`. Stop at the first hard failure and show its
output.

## 1. Hard gate (same as CI, in order)

```bash
pip install -e ".[dev]"
ruff check .
ruff format --check .
mypy
pytest
```

- `ruff check` / `ruff format --check` - style. If it fails, `ruff check --fix`
and `ruff format` fix most of it.
- `mypy` - strict on `src/`. New public functions need full annotations.
- `pytest` - all tests pass and coverage stays >= 85% (the run fails the build
otherwise). `pytest` also runs with `filterwarnings = ["error"]`, so a new
warning is a failure.

## 2. Soft checks against the diff

```bash
git fetch origin main
git diff --stat origin/main...HEAD
git diff origin/main...HEAD
```

Then verify:

| Check | How |
|---|---|
| Tests for the change | `src/` behaviour changed -> a matching `tests/test_<module>.py` change is in the diff. New module -> new test file. |
| Changelog | `CHANGELOG.md` has a new bullet under `## [Unreleased]`. |
| Docs | the diff touches something `README.md` or `docs/` describes (a module, a UCI command, the layout, an architecture claim, a feature list). If so, those files are updated - run `/update-docs` if not. |
| No stray debug | no leftover `print(...)` in `src/` outside `__main__.py`, no commented-out code, no `# type: ignore` without a reason. |
| Public API typed | any new function/method in `src/` has argument and return annotations (mypy strict enforces this, but confirm). |

## 3. Report

Produce the checklist from the PR template with each item marked pass / fail /
n/a, plus a one-line note on anything that needs the author's attention. If
everything passes, say so and give the branch name for the PR.
85 changes: 85 additions & 0 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
name: release
description: >
Cut a new release: bump the version, roll the changelog, run the full gate,
commit, and tag. Use when the user asks to "cut a release", "tag a version",
or "release vX.Y.Z".
---

# release

The version is single-sourced from `src/pychess/__init__.py` (`__version__`);
`pyproject.toml` reads it via `[tool.hatch.version]`. Do **not** edit the
version anywhere else.

## 1. Preconditions

```bash
git status # working tree must be clean
git fetch && git status # branch must not be behind its upstream
git log -1 --format='%H %s' # note the commit being released
```

- Be on `main` (or a `release/*` branch the user names).
- The latest `main` CI run (`ci-ok`) must be green. If you can't verify it, ask.
- If the tree is dirty or the branch is behind, stop and tell the user.

## 2. Choose the version

Read `## [Unreleased]` in `CHANGELOG.md` and the current `__version__`. Pick the
next version by semver from what's in Unreleased:

- breaking API / CLI change -> major
- new user-facing capability -> minor
- fixes / internal only -> patch

Confirm the number with the user before writing anything. (For the very first
release, `__version__` may already equal the target.)

## 3. Apply the changes

1. Set `__version__` in `src/pychess/__init__.py` to `X.Y.Z`.
2. In `CHANGELOG.md`:
- rename `## [Unreleased]` to `## [X.Y.Z] - <today's date, YYYY-MM-DD>`
- add a fresh empty `## [Unreleased]` section above it
- if Unreleased was empty, stop - there's nothing to release.

Get today's date with `date +%F` (do not guess it).

## 4. Verify

```bash
pip install -e ".[dev]"
ruff check . && ruff format --check . && mypy && pytest
python -c "import pychess; print(pychess.__version__)" # == X.Y.Z
pip install . # throwaway build sanity check (in a temp venv if possible)
```

## 5. Commit and tag

```bash
git add src/pychess/__init__.py CHANGELOG.md
git commit -m "chore(release): vX.Y.Z"
git tag -a vX.Y.Z -m "vX.Y.Z"
```

## 6. Hand off (do not push without the user's OK)

Pushing and publishing are outward-facing. Print these for the user to run:

```bash
git push origin <branch> --follow-tags
```

Then draft the GitHub Release from the `CHANGELOG.md` section for this version:

```bash
gh release create vX.Y.Z --title "vX.Y.Z" --notes-file <(sed -n '/## \[X.Y.Z\]/,/## \[/p' CHANGELOG.md | sed '$d')
```

or paste that changelog section into the Release UI.

## 7. Report

State the new version, the files changed, the tag name, and the exact push /
release commands the user still needs to run.
79 changes: 79 additions & 0 deletions .claude/skills/update-docs/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
name: update-docs
description: >
Review the current changes and update README.md and every markdown file under
docs/ so the documentation matches the code. Use after implementing a feature,
refactor, or structural change, or when the user asks to "update the docs".
---

# update-docs

Bring `README.md` and `docs/*.md` back in sync with the code. Only reflect
changes that are actually in the diff - never invent features or numbers.

## 1. Find what changed

Determine the review range:

- If the user named a base (branch, tag, commit), diff against that.
- Else if the branch is not `main`, use `git diff main...HEAD` plus any
uncommitted changes (`git diff` / `git status`).
- Else review uncommitted changes only.

```bash
git status
git diff <base>...HEAD
git diff # unstaged
```

Summarise, for yourself, what changed: new/removed/renamed modules, changed
public APIs or class names, new/changed UCI commands or `go` options, new CLI
entry points, dependency or Python-version changes, tooling changes, and any
behaviour change a user or contributor would notice.

## 2. Read the current docs

Read `README.md` and each file in `docs/` (currently `design.md`,
`engine-strength.md`, `performance.md`, `tasks.md`). Note their structure and
tone so edits blend in.

## 3. Update each file where the diff touches it

Match the existing style; make the smallest edit that makes the doc correct.

| File | Update when the change affects... |
|---|---|
| `README.md` | the one-paragraph description, badges, quick-start commands, UCI usage, the repository-layout tree, or the development commands |
| `docs/design.md` | architecture (collaborators, coordinator, layers), the "Implemented" list, or the "Backlog" list |
| `docs/engine-strength.md` | evaluation/search features that move the estimate, or the calibration/time-control notes. **Do not change Elo numbers** unless the diff contains a measured result. |
| `docs/performance.md` | only when the diff includes fresh benchmark numbers. Never fabricate timings. |
| `docs/tasks.md` | a roadmap item was implemented (move it into "Recently completed" and delete it from the numbered list), or a housekeeping item was done |

Also check: the repo-layout tree in `README.md` lists every `src/pychess/*.py`
module with a one-line description - add/rename/remove rows to match.

Cross-references: if you rename or move a doc, fix every link to it in the other
docs and the README.

## 4. Adjacent files (mention, don't silently skip)

- `CLAUDE.md` - update the architecture map / gotchas if a module moved or a
convention changed.
- `CHANGELOG.md` - add a bullet under `## [Unreleased]`. This is outside the
skill's core scope; do it if it's clearly missing, otherwise flag it.

## 5. Verify

- Every relative link in the edited files resolves (`ls` the targets).
- Every shell command / code snippet shown in the docs still runs or matches
current config (`pyproject.toml`, `.github/workflows/ci.yml`).
- No stale references to removed names:
```bash
git grep -nE 'removed_name_1|removed_name_2' -- '*.md'
```

## 6. Report

List each file you changed and the one-line reason, and each doc you
deliberately left alone. If a change needs numbers you can't derive (Elo,
benchmarks), say so and leave a clear TODO rather than guessing.
18 changes: 18 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space

[*.py]
indent_size = 4
max_line_length = 100

[*.{yml,yaml,toml,json}]
indent_size = 2

[*.md]
trim_trailing_whitespace = false
8 changes: 8 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
* text=auto eol=lf

*.bin binary
*.png binary
*.jpg binary

docs/** linguist-documentation
opening_book/bookfish.bin linguist-vendored
35 changes: 35 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Bug report
description: Something the engine does wrong or a crash.
labels: [bug]
body:
- type: textarea
id: what-happened
attributes:
label: What happened
description: What you expected vs. what actually happened.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Steps to reproduce
description: >
The UCI commands you sent, or a FEN and `go` limits. A full transcript
(`position ... / go ...` and the engine's output) is ideal.
render: text
validations:
required: true
- type: input
id: version
attributes:
label: Version / commit
description: Output of `pip show pychess` or the commit SHA.
validations:
required: true
- type: input
id: python
attributes:
label: Python version and OS
placeholder: "3.12 on macOS 14 / 3.13 on Ubuntu 24.04"
validations:
required: true
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Question or discussion
url: https://github.com/cjunius/pyChess/discussions
about: Ask usage questions or propose ideas here.
- name: Security vulnerability
url: https://github.com/cjunius/pyChess/security/advisories/new
about: Report security issues privately, not as a public issue.
Loading