add Claude Code setup - #59
Merged
Merged
Conversation
Adapted from the sibling scripture-editors repo, adjusted for this repo being npm + a single-package library rather than pnpm + an Nx monorepo. Config ------ - .claude/settings.json: shared permissions. Every Bash(...) deny/ask rule has a PowerShell(...) twin, since Bash patterns don't match PowerShell calls and PowerShell is the primary shell on Windows. Covers the Remove-Item aliases (del/rd/ri/erase), sudo, and Invoke-Expression/iex. These raise the bar rather than making the guardrails sound. Matching is prefix-based over tokens, so a rule only fires on the spelling it names: verified that `git push --force origin main` is denied while `git push origin main --force` -- same operation, flag moved -- is not. Treat the deny list as defence in depth, not a boundary. A PreToolUse hook is the only form immune to reordering. Editing this file is in `ask` rather than `deny`: denying it locks out routine maintenance, while `ask` still prevents Claude silently widening its own permissions. - .claude/skills/typescript-lsp-volta/: committed skills-directory plugin giving go-to-definition, find-references and diagnostics. It replaces the official typescript-lsp plugin (explicitly set to false), which requires a global install and, on Windows, spawns the binary shell-free so it cannot launch Volta's .cmd shim and dies with ENOENT: uv_spawn. This one runs the server via `node node_modules/typescript-language-server/lib/cli.mjs --stdio`, which works under Volta on every OS. - package.json: adds typescript-language-server as a devDependency, required by the above. Dev-only -- `files` publishes dist and src, so it is never shipped. No build tooling or existing scripts changed. - .mcp.json + scripts/mcp-launcher.mjs: Context7, as the single team-wide server. settings.json sets context7@claude-plugins-official to false so a personal copy of that plugin is disabled for this project only, leaving other repos alone -- no post-merge cleanup to remember, and it reverts with the branch. The launcher is ESM rather than the sibling's CJS so it passes this repo's type-checked ESLint, and spawns cmd.exe explicitly instead of using `shell: true`, which Node 24 (pinned here) deprecates via DEP0190. That is not argument-hardening: both forms are equally injectable, which is acceptable only because .mcp.json already names the command to execute. - tsconfig.lint.json: include scripts/**/*.mjs, matching how the other root tool configs are already listed, so `eslint .` can type-check the launcher. - .gitignore: switch the Claude block to `.claude/*` plus negations so settings.json and skills/ stay tracked while local-only files don't. Documentation ------------- CLAUDE.md records commands, CI-matching verification, the public API map, and the port-fidelity rule from README's Contributing section, plus three things learned the hard way: - There is no typecheck script, and lint is not a substitute. ESLint's type-checked rules consume type information but never report compile errors, so only tsc catches those. Test files are type-checked by nothing CI runs; `npx tsc -p tsconfig.test.json --noEmit` is the command for that. - Dependency updates: this repo takes security Dependabot PRs only, and there are no runtime dependencies, so advisories never reach consumers. Dependabot's lockfiles are not idempotent -- the baseline is that `npm install --package-lock-only` on main is a no-op, so re-resolve and diff before accepting one. One residual brace-expansion advisory is known and accepted; pinning minimatch@^10 through overrides silently breaks lint, and the reason is narrower than it looks -- it is v10's ESM build that lacks a default export, while its CommonJS build has one. - Known porting gaps: members that exist but don't behave like the C#, so a ported test can fail for reasons that aren't visible in the source -- internalValid()'s commented-out range check, isExcluded hardcoded false, set verseNum missing the negative guard and `verse = null`, BBBCCCVVVS declared not implemented, the numeric constructor bypassing the setters, and the BookSet stub. Verified: lint, prettier:ci, build, test:ci (43/43) and `npx tsc -p tsconfig.test.json --noEmit` all exit 0. Context7 completes an MCP handshake through the launcher, and the language server completes an LSP initialize handshake advertising definitionProvider and referencesProvider. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #59 +/- ##
=======================================
Coverage 83.93% 83.93%
=======================================
Files 4 4
Lines 330 330
Branches 77 77
=======================================
Hits 277 277
Misses 33 33
Partials 20 20 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds Claude Code configuration to this repo, adapted from the sibling
scripture-editorsrepo and adjusted for this one being npm + a single-packagelibrary rather than pnpm + an Nx monorepo.
No runtime impact: no
dependencieschange, no build tooling or existing scriptchanged, and the one new devDependency is never published (
filesshipsdistand
srconly).What's here
CLAUDE.md— commands, CI-matching verification steps, the public API map,and the port-fidelity rule from the README's Contributing section. It also
records three things that are easy to get wrong:
typecheckscript, and lint is not a substitute. ESLint'stype-checked rules consume type information but never report compile errors.
Test files are type-checked by nothing CI runs —
npx tsc -p tsconfig.test.json --noEmitis the command for that.since there are no runtime dependencies, advisories never reach consumers.
Dependabot's lockfiles are not idempotent, so re-resolve and diff before
accepting one. One residual
brace-expansionadvisory is known and accepted;pinning
minimatch@^10viaoverridessilently breaks lint.ported test can fail for reasons not visible in the source (
internalValid()'scommented-out range check,
isExcludedhardcodedfalse,set verseNummissing the negative guard,
BBBCCCVVVSunimplemented, the numeric constructorbypassing setters, the
BookSetstub)..claude/settings.json— shared permissions. EveryBash(...)deny/ask rulehas a
PowerShell(...)twin, since Bash patterns don't match PowerShell calls andPowerShell is the primary shell on Windows.
.claude/skills/typescript-lsp-volta/— a committed skills-directory pluginproviding go-to-definition, find-references and diagnostics, plus
typescript-language-serveras a devDependency. It replaces the officialtypescript-lspplugin, which needs a global install and, on Windows, spawns thebinary shell-free so it cannot launch Volta's
.cmdshim (ENOENT: uv_spawn).This one runs the server through
node, which works under Volta on every OS..mcp.json+scripts/mcp-launcher.mjs— Context7 as a single team-wideserver.
settings.jsondisables the equivalent personal plugin for this projectonly, so contributors who have it keep it in their other repos and there's no
post-merge cleanup to remember.
.gitignore,tsconfig.lint.json— tracksettings.jsonandskills/whilekeeping local-only files out; let
eslint .type-check the launcher.A caveat worth reading
The permission deny list is defence in depth, not a boundary. Matching is
prefix-based over tokens, so a rule only fires on the exact spelling it names.
Verified during review:
git push --force origin mainis denied, whilegit push origin main --force— the same operation with the flag moved — is not.A
PreToolUsehook is the only form immune to reordering, and is the naturalfollow-up if we want a real boundary.
Verification
lint,prettier:ci,build,test:ci(43/43) andnpx tsc -p tsconfig.test.json --noEmitall exit 0. Context7 completes an MCPhandshake through the launcher, and the language server completes an LSP
initializehandshake advertisingdefinitionProviderandreferencesProvider.🤖 Generated with Claude Code