aboutsummaryrefslogtreecommitdiff
path: root/languages
Commit message (Collapse)AuthorAgeFilesLines
* fix(elisp): byte-compile cross-project .el edits against their own modulesCraig Jennings16 hours1-0/+5
| | | | | | The validate-el hook only put the current project's roots on the load path, so editing an .el file from another project failed Phase 1 byte-compile on free-variable warnings: the file's own sibling modules weren't reachable. I added the edited file's directory and its parent to the load path. For in-project edits both are redundant (already covered by PROJECT_ROOT and its modules dir). They only do work when the file sits outside the current project root. I left Phase 2's test runner alone. It discovers tests by stem under PROJECT_ROOT/tests, so a cross-project file's tests aren't found regardless of load path.
* docs(elisp): name eask as the build tool, not eldevCraig Jennings5 days1-1/+1
| | | | The real elisp packages drive eask in their Makefiles, but the bundle listed eldev as a build tool and left eask out, pointing a fresh session at the wrong tool.
* fix(elisp-hook): show a compact test summary in the terminal on failureCraig Jennings7 days1-3/+13
| | | | validate-el.sh dumped the full ERT batch output to the terminal on a red test, every backtrace frame, flooding the pane. It now prints a short summary there: the run tally plus the failing test names and their file:line. Claude still gets the full backtrace through additionalContext. fail_json takes an optional fourth argument for the terminal echo. Paren and byte-compile failures stay short, so they still print in full.
* feat(go): build out the full Go language bundleCraig Jennings11 days6-0/+521
| | | | | | | | | | | | | The Go bundle was coverage-slice-only. Because it shipped no rule files, sync-language-bundle.sh (which fingerprints a project's bundle by spotting one of its rule files in .claude/rules/) couldn't detect it, so the coverage slice it did ship never stayed in sync. Adding the rules is what makes the bundle sync-maintainable, which was the point. Brought Go to the full tier, matching elisp: - claude/rules/go.md and go-testing.md, the style and testing rules (table-driven tests, go test -race, errors.Is over message matching, how the coverage slice fits). These two are also the sync fingerprint. - claude/hooks/validate-go.sh, a PostToolUse hook that runs gofmt and go vet on each edited .go file. go vet type-checks, so compile and syntax errors surface at edit time. It deliberately doesn't auto-run tests, since a package's tests can be slow or integration-tagged and shouldn't fire on every keystroke. - claude/settings.json, Go permissions plus the hook wiring. - githooks/pre-commit, a secret scan and a gofmt check on staged .go. - CLAUDE.md, the seed. validate-go.sh is TDD'd by scripts/tests/validate-go.bats: a clean file passes, gofmt and vet failures both block with the JSON payload, and non-go, missing, or empty paths are ignored. I updated install-lang.bats test 7, which asserted Go installs no CLAUDE.md, to check the full bundle instead. Verified with a real install into a throwaway project and a green make test.
* feat(typescript): add coverage-summary to the TypeScript bundleCraig Jennings14 days5-0/+332
| | | | | | | | | | Last language in the coverage-summary fan-out, after Elisp, Python, and Go. Same kernel: count every source file on disk that's absent from the coverage report as 0% and weight the project number by file, so an untested file stays visible instead of being averaged away. The script at languages/typescript/claude/scripts/coverage-summary.js parses an Istanbul json-summary report (the coverage-summary.json that c8, Vitest, and Jest all emit), takes per-file statements covered over total, and reports a file-weighted number plus the missing files. It walks the source dir for .ts/.js, skipping test files, declarations, and node_modules. Node built-ins only, so it runs via node with no install, and it doesn't reimplement the per-file table nyc already prints. Tests are black-box, run with node's own test runner: a temp tree plus a json-summary report, the script invoked via node, output asserted. They cover missing-file detection, all-tracked, test-file and node_modules exclusion, and the missing-report error. make test gained a node --test discovery path for languages/*/tests, guarded so environments without Node skip it cleanly. As with Python, the TypeScript bundle had no gitignore-add.txt, which would have left the script un-gitignored on install, so I added one. This finishes the fan-out: coverage-summary now ships in all four bundles, each parsing its own tool's report behind the same file-weighted, missing-as-0% kernel. I proved the Go and TypeScript scripts by running them (Go against a live profile, TS against a synthetic report and the CLI). Python and TypeScript weren't run against a live coverage tool, since neither coverage.py nor nyc is installed here, so the first adopter of each should check against a real report.
* feat(go): add coverage-summary as a Go bundle coverage sliceCraig Jennings14 days5-0/+445
| | | | | | | | | | Third language in the coverage-summary fan-out, after Elisp and Python. Same kernel: count every source file on disk that's absent from the coverage profile as 0% and weight the project number by file, so an untested file stays visible instead of being averaged away. The script at languages/go/claude/scripts/coverage-summary.go parses a cover.out profile, maps each import-path-qualified entry back to an on-disk relative path using the module path from go.mod, and reports a file-weighted number plus the missing files. It's standard library only, so it runs anywhere via go run, and it doesn't reimplement the per-function table that go tool cover -func already prints. I proved it against a real go test -coverprofile run, not just a synthetic fixture, since the Go toolchain is installed here. Two findings to flag. Modern go test ./... already lists every module package in the profile at 0% even when untested, so for in-module code the missing-file list is usually empty. The detection earns its keep on build-tagged files and dirs outside ./.... And this is a coverage-only slice of a Go bundle that doesn't otherwise exist yet: there's no go.md rule file, so sync-language-bundle.sh can't fingerprint it (detection keys on a bundle's own .claude/rules). The script installs via make install-lang LANG=go but won't be sync-maintained until the Go bundle gets real rules and a CLAUDE.md. Building that out is the natural companion task. Tests are black-box: a Go test in its own throwaway module runs the script via go run against temp fixtures and checks output, so the shipped script dir stays test-free. They cover missing-file detection, all-tracked, _test.go exclusion, and the missing-report error. make test gained a go test discovery path for languages/*/tests, guarded so environments without Go skip it cleanly.
* feat(python): add coverage-summary to the Python bundleCraig Jennings14 days5-0/+388
| | | | | | | | | | Second language in the coverage-summary fan-out, after the Elisp pilot. Same kernel: a module no test imports never appears in coverage.py's report, so a line-weighted total skips it silently and the suite looks healthier than it is. This counts every source file on disk that's absent from the report as 0% and weights the project number by file, so untested modules stay visible. The script at languages/python/claude/scripts/coverage-summary.py parses coverage.py's JSON (files[path].summary.covered_lines / num_statements), resolves report paths against the report's directory since coverage records them relative to where it ran, and recurses the source dir for *.py. Unlike the Elisp version it doesn't print a per-file table, because coverage.py's own coverage report already does. The script adds the missing-file accounting that report lacks. It uses only the standard library, parsing the report rather than importing coverage. The Python run confirmed the plumbing from the pilot is genuinely generic. install-lang and sync deliver the script and the project-owned coverage-makefile.txt with no Python-specific code. The one gap I had to close: the Python bundle shipped without a gitignore-add.txt, so the .claude/ footprint wasn't ignored and the script would have been committable. Added one mirroring the Elisp footprint plus Python artifacts (__pycache__, .coverage, coverage.json). make test gained a languages/*/tests/test_*.py discovery path alongside the existing Elisp ERT one. Tests: 12 pytest covering the parser, the file-weighted number, and the missing-file detection including subpackage recursion, plus an install-lang check that the script lands in the gitignored footprint. I proved it against a report matching coverage.py's documented schema and the CLI end to end, but not against a live coverage json run, because coverage.py isn't installed in this repo's env. The first project to adopt it should sanity-check against a real report.
* feat(elisp): add coverage-summary to the Elisp bundle with missing-file ↵Craig Jennings14 days4-0/+407
| | | | | | | | | | | | | | detection A line-weighted coverage total has a blind spot: a module no test loads never shows up in the SimpleCov report, so it can't drag the number down. The suite looks healthier than it is. This adds a summary that counts every source file on disk against the report and treats an absent file as 0%, weighting the project number by file instead of by line so untested modules stay visible. The script ships at languages/elisp/claude/scripts/coverage-summary.el, self-contained on stock Emacs (just the built-in json). It parses the undercover SimpleCov shape directly rather than depending on the editor's coverage engine, so it runs anywhere the bundle lands. I proved it against a real 103-file report: 93 tracked, 27 untested modules surfaced, project number 66.4%. Delivery follows the bundle convention. The script lives under the gitignored .claude/ footprint and gets auto-fixed on drift by sync-language-bundle.sh, which I made generic for any claude/scripts/* rather than coverage-specific. The Makefile targets ship as a project-owned fragment (languages/elisp/coverage-makefile.txt) that install-lang.sh seeds at the project root and sync drops into .ai/inbox/ when that convention exists. The bundle never edits the project's own Makefile. Tests: 12 ERT for the kernel (Normal/Boundary/Error per function), wired into make test via a new languages/*/tests/ discovery path, plus bats for the sync auto-fix and the inbox-drop guards. This is the Elisp pilot. The pattern is proven, so fanning out to Python, Go, and TypeScript is now a follow-up. Each one needs only its own parser and fragment. The plumbing is already generic.
* fix(elisp): add themes/ to the validate-el.sh load pathCraig Jennings2026-05-251-0/+2
| | | | | | The PostToolUse hook byte-compiles each saved .el with -L for the project root, modules/, and tests/, but not themes/. A theme file that requires a sibling under themes/ then fails byte-compile with "Cannot open load file", a false VALIDATION FAILED even though the file loads fine at runtime. I added -L themes/ to both the byte-compile and test-runner blocks. Adding a load-path entry for a directory that doesn't exist is harmless to Emacs, so it stays unconditional, matching how modules/ and tests/ are already added without an existence guard.
* fix(elisp): gitignore the full Claude tooling footprintCraig Jennings2026-05-251-3/+4
| | | | | | The bundle tracked .claude/rules, CLAUDE.md, and githooks/, ignoring only the personal overrides. For a code project, especially a third-party package checkout, the whole Claude footprint should stay local: install and sync deliver it, so it shouldn't land in the project's history. gitignore-add.txt now ignores .claude/, CLAUDE.md, and githooks/ next to the elisp build artifacts. I also added install-lang.bats, which the bundle had no test for. It covers the landed footprint and the gitignore set.
* docs(languages): tighten elisp coding and testing rulesCraig Jennings2026-05-222-1/+45
| | | | Two audit fixes. elisp.md's "prefer Write over Edits" advice was tool-specific. It's now framed around intent: edit cohesively, then run paren and byte-compile checks immediately, whatever the editing mechanism. elisp-testing.md gains batch-mode reproducibility (emacs --batch as source of truth, no interactive state, no blocking prompts), state isolation (temp user-emacs-directory, explicit load-path, declared deps only), and byte-compile/native-comp warning handling, with native-comp gated on availability and kept opt-in.
* docs(languages): revise python-testing SQLite and ORM-mocking guidanceCraig Jennings2026-05-221-2/+13
| | | | Two audit fixes. The "prefer in-memory SQLite" advice is risky when prod is Postgres or MySQL — SQLite diverges on query semantics, constraints, transactions, JSON, time zones, and indexes, so a test can pass on SQLite and fail in prod. ORM/query tests now use a production-like DB, with SQLite reserved for pure unit tests. The "never mock the ORM" rule is also clarified: don't mock ORM internals, but a thin orchestration unit can inject a fake at a deliberate data-access port it owns.
* feat(languages): add typescript bundle (Vitest-canonical)Craig Jennings2026-05-061-0/+214
| | | | Mirrors the python bundle's minimal shape: one language-specific file under claude/rules/. Vitest is canonical, with brief notes for Mocha+Chai and Angular Karma legacy idioms. Covers RTL query priorities, MSW for network mocking, it.each for parametrize, async patterns, and TS-specific discipline (no any in tests, prefer satisfies, etc.).
* feat(hooks): add global hooks — PreCompact priorities + git/gh confirm modalsCraig Jennings2026-04-191-9/+24
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three new machine-wide hooks installed via `make install-hooks`: - `precompact-priorities.sh` (PreCompact) — injects a priority block into the compaction prompt so the generated summary retains information most expensive to reconstruct: unanswered questions, root causes with file:line, subagent findings as primary evidence, exact numbers/IDs, A-vs-B decisions, open TODOs, classified-data handling. - `git-commit-confirm.py` (PreToolUse/Bash) — gates `git commit` behind a confirmation modal showing parsed message, staged files, diff stats, author. Parses both HEREDOC and `-m`/`--message` forms. - `gh-pr-create-confirm.py` (PreToolUse/Bash) — gates `gh pr create` behind a modal showing title, base ← head, reviewers, labels, assignees, milestone, draft flag, body (HEREDOC or quoted). Makefile: adds `install-hooks` / `uninstall-hooks` targets and extends `list` with a Hooks section. Install prints the settings.json snippet (in `hooks/settings-snippet.json`) to merge into `~/.claude/settings.json`. Also: `languages/elisp/claude/hooks/validate-el.sh` now emits JSON with `hookSpecificOutput.additionalContext` on failure (via new `fail_json()` helper) so Claude sees a structured error in context, in addition to the existing stderr output and exit 2. Patterns synthesized clean-room from fcakyon/claude-codex-settings (Apache-2.0). Each hook is original content.
* feat: adopt pairwise-tests (PICT combinatorial) + cross-reference from ↵Craig Jennings2026-04-191-0/+16
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | existing testing skills Forked verbatim from omkamal/pypict-claude-skill (MIT). LICENSE preserved. Renamed from `pict-test-designer` to `pairwise-tests` — technique-first naming so users invoking "pairwise" or "combinatorial" find it; PICT remains the tool under the hood. Bundle (skill-runtime only): pairwise-tests/SKILL.md (renamed, description rewritten) pairwise-tests/LICENSE (MIT, preserved) pairwise-tests/references/pict_syntax.md pairwise-tests/references/examples.md pairwise-tests/scripts/pict_helper.py (Python CLI for model gen / output fmt) pairwise-tests/scripts/README.md Upstream's repo-level docs (README, QUICKSTART, CONTRIBUTING, etc.) and `examples/` dir (ATM + gearbox walkthroughs — useful as reading, not as skill-runtime) omitted from the fork. Attribution footer added. Cross-references so /add-tests naturally routes to /pairwise-tests when warranted: - add-tests/SKILL.md Phase 2 step 8: if a function in scope has 3+ parameters each taking multiple values, surface `/pairwise-tests` to the user before proposing normal category coverage. Default continues with /add-tests; user picks pairwise explicitly. - claude-rules/testing.md: new "Combinatorial Coverage" section after the Normal/Boundary/Error categories. Explains when pairwise wins, when to skip (regulated / provably exhaustive contexts, ≤2 parameters, non- parametric testing), and points at /pairwise-tests. - languages/python/claude/rules/python-testing.md: new "Pairwise / Combinatorial for Parameter-Heavy Functions" subsection under the parametrize guidance. Explains the pytest workflow: /pairwise-tests generates the matrix, paste into pytest parametrize block, or use pypict helper directly. Mechanism note: cross-references are judgment-based — Claude reads the nudges in add-tests/testing/python-testing and acts on them when appropriate, not automatic dispatch. Craig can still invoke /pairwise-tests directly when he already knows he wants combinatorial coverage. Makefile SKILLS extended; make install symlinks /pairwise-tests globally.
* feat(rules): port key testing principles from quality-engineer promptCraig Jennings2026-04-191-0/+26
| | | | | | | | | | | | | | | | | | | | | | | Additions to claude-rules/testing.md: - Testing pyramid proportions (70-80% unit / 15-25% integration / 5-10% e2e) - Integration Tests section: docstring must name 'Components integrated:' and mark real vs mocked; when-to-write heuristics - Signs of Overmocking: 'would the test pass if the function body was NotImplementedError?' plus three more sharp questions - Testing Code That Uses Frameworks: test your integration, not the framework itself - Test Real Code, Not Copies: never inline prod code into tests - Error Behavior, Not Error Text: test type + key values, not exact prose - If Tests Are Hard to Write, Refactor the Code: hard-to-test is a code signal, not a test signal; extract focused helpers - Anti-patterns list extended Addition to languages/elisp/claude/rules/elisp-testing.md: - Interactive vs Internal split pattern: cj/foo wraps cj/--foo; test the internal directly, skip UI mocks Source: ~/.emacs.d/ai-prompts/quality-engineer.org (personal reference, kept as an extended prompt separate from these rules).
* chore: remove project-specific referencesCraig Jennings2026-04-191-3/+3
| | | | | | | | - elisp-testing.md: generalized testutil description. The specific files testutil-general.el / testutil-filesystem.el / testutil-org.el only exist in one project; bundle should describe the pattern, not name specific files. - README.org: install examples use ~/code/ path to match actual layout.
* feat: add commits.md rule — no AI attribution anywhereCraig Jennings2026-04-192-3/+9
| | | | | | | | | | | | | | New generic rule at claude-rules/commits.md covers author identity, absence of Claude/Anthropic/LLM/AI attribution (messages, PRs, comments, trailers, emojis), and conventional commit format. Applies to all repos. Bundle settings.json now sets attribution.commit: "" and attribution.pr: "" so Claude Code's default attribution is suppressed belt-and-suspenders with the written rule. Elisp CLAUDE.md template trimmed to reference commits.md instead of inlining the rules.
* chore(elisp): add *.elc and *.eln to gitignore templateCraig Jennings2026-04-191-0/+4
| | | | | | The validate-el.sh hook byte-compiles .el files, which produces .elc (and on Emacs 28+ with native-comp, .eln) artifacts. Projects using the bundle almost always want these ignored.
* fix(elisp): call package-initialize so byte-compile finds depsCraig Jennings2026-04-191-0/+2
| | | | | | | | | | | | | | | Byte-compile needs external packages on the load path to resolve (require ...) forms in project files. Without this, any project using MELPA/ELPA packages (dash, s, etc.) failed Phase 1 with "Cannot open load file". package-initialize reads package-user-dir (default ~/.emacs.d/elpa) and exposes installed package autoloads. Small latency cost (~100ms) but makes the hook work on real projects. Verified: - chime.el (requires dash) now byte-compiles cleanly; was failing. - emacs.d modules still pass; no regression.
* refactor(elisp): generalize validate-el.sh test-runner for flat layoutsCraig Jennings2026-04-191-4/+10
| | | | | | | | | | | | | | | | | | Phase 2 test lookup now triggers for any .el file outside tests/, not just modules/*.el. Stem-based test matching works the same way; this just broadens the case pattern. Before: only modules/foo.el → tests/test-foo*.el triggered Phase 2. After: foo.el, lib/foo.el, modules/foo.el all do. init.el and early-init.el are still Phase-1-only (byte-compile would load the full package graph). Verified on: - emacs.d (modules/-based): modules/browser-config.el still runs its matching test, exit 0 - flat layout (scratch /tmp): source.el at project root successfully finds and runs tests/test-source.el
* refactor: generalize testing.md, split Python specifics, DRY installCraig Jennings2026-04-193-42/+104
| | | | | | | | | | | | | | | | | | | claude-rules/testing.md is now language-agnostic (TDD principles, test categories, coverage targets, anti-patterns). Scope header widened to **/*. Python-specific content (pytest, fixtures, parametrize, anyio, Django DB testing) moved to languages/python/claude/rules/python-testing.md. Added languages/python/ bundle (rules only so far; no CLAUDE.md template or hooks yet — Python validation tooling differs from Elisp). Added install-python shortcut to the Makefile. Updated scripts/install-lang.sh to copy claude-rules/*.md into each target project's .claude/rules/. Bundles no longer need to carry their own verification.md copy — deleted languages/elisp/claude/rules/verification.md. Single source of truth in claude-rules/, fans out via install. Elisp-testing.md now references testing.md as its base (matches the python-testing.md pattern).
* feat: add per-project language bundles + elisp rulesetCraig Jennings2026-04-198-0/+463
Introduces a second install mode alongside the existing global symlinks: per-project language bundles that copy a language-specific Claude Code setup (rules, hooks, settings, pre-commit) into a target project. Layout additions: languages/elisp/ - Emacs Lisp bundle (rules, hooks, settings, CLAUDE.md) scripts/install-lang.sh - shared install logic Makefile additions: make help - unified help text make install-lang LANG=<lang> PROJECT=<path> [FORCE=1] make install-elisp PROJECT=<path> [FORCE=1] (shortcut) make list-languages - show available bundles Elisp bundle contents: - CLAUDE.md template (seed on first install, preserved on update) - .claude/rules/elisp.md, elisp-testing.md, verification.md - .claude/hooks/validate-el.sh (check-parens, byte-compile, run matching tests) - .claude/settings.json (permission allowlist, hook wiring) - githooks/pre-commit (secret scan + staged-file paren check) - gitignore-add.txt (append .claude/settings.local.json) Hooks use \$CLAUDE_PROJECT_DIR with a script-relative fallback, so the same bundle works on any machine or clone path. Install activates git hooks via core.hooksPath=githooks automatically. Re-running install is idempotent; CLAUDE.md is never overwritten without FORCE=1.