Commit Graph
80 Commits
Author SHA1 Message Date
tmu b7d1dbdf06 📝 Track setup-phase review findings in the backlog
Record the tooling, CI and docs gaps found reviewing the setup phase
(src/ is placeholder code and was excluded from the review).

The entries cover pi-lsp install reproducibility, gating the emit path
in verify, .ts extensions left in emitted .d.ts, pi-lsp false-positive
diagnostics, CI packaging checks and artifact reuse, pre-push gate
strength, plus low-impact cleanups (clean target, redundant fields,
knip false positive, sourcemap sources, doc drift).
2026-09-10 22:37:59 +00:00
tmu 99bda289a6 📝 Document .editorconfig as oxfmt fallback
Recommend `EditorConfig.EditorConfig` in .vscode/extensions.json so editors
without native `.editorconfig` support pick it up, and add an "Editor
configuration" section to CONTRIBUTING.md: `.editorconfig` is an
editor-compatibility fallback for the file types oxfmt does not format
(shell scripts, dotfiles, the commit-message template, git's
`COMMIT_EDITMSG` buffer), with `.oxfmtrc.json` authoritative where both
apply. `.editorconfig` itself is unchanged.
2026-09-10 21:47:08 +00:00
tmu 0c6d1483de ♻️ Port CI workflow from GitHub Actions to Gitea Actions
The runner now lives on Gitea; move the workflow to .gitea/workflows/ and
delete the stale .github copy (Gitea ignores .github/, so it's drift bait).
Use the native gitea.ref context for the tag gate; keep actions pinned to
their GitHub sources (the runner has internet + caches them). Drop the
upload-artifact coverage step in favour of the self-hosted webserver plan
tracked in the backlog. Add an empty-secret gate as publish's first step so
a tag push without NPM_TOKEN fails loudly instead of silently no-oppping.
2026-09-10 19:41:09 +02:00
tmu 9a08262076 📝 Track self-hosted coverage hosting plan in backlog
Record the decision to host CI coverage from a tiny dir-listing server in
the Gitea docker setup (shared volume keyed by repo/tag) instead of the
GitHub upload-artifact zip. This unblocks dropping the coverage artifact
from the Gitea CI port. Adds the lipanski docker-image author to cspell.
2026-09-10 19:40:35 +02:00
tmu 672fd1f7e4 ♻️ Adopt setup: prefix and retire the stray use: script
Introduce a setup: prefix for one-time clone configuration (mutates the
local environment, never a hook or CI step) with setup as its umbrella
aggregator. Move use:git-commit-message to setup:git-commit-message as
its first member, retiring use: — the undocumented prefix tracked in the
backlog. Update both CONTRIBUTING.md prefix lists, the bare-command
inventory, and the stale use: reference in branch.sh.
2026-09-08 23:01:49 +02:00
tmu 844e80d650 ♻️ Group workflow scripts under a create: prefix
`branch` and `release` were bare commands, but bare in this repo means
"how you invoke a tier" or "runs one tool" — neither fits a command that
opens or closes a unit of work. They get their own prefix now, since a
prefix is how this repo records _when_ a script runs.

`create:` because both members genuinely create something (a branch, a
release) and it is a plain verb rather than VCS slang. No bare `create`
aggregator: running "all the workflows" describes nothing anyone wants,
and `publish:*` already precedents a prefix without one.

The rule "reuse an existing prefix, never invent one" now says what it
actually means: a new prefix is allowed when the scripts belong in the
pipeline, provided it enters both lists in the same commit as its first
member. That is the lesson from `use:`, which is referenced by prose yet
in none of the lists — already tracked in backlog.tasks.

The bare-command sentence shrinks to `build`, `clean`, `verify`.
2026-09-08 22:51:35 +02:00
tmu 8c2855aa92 📝 Track the undocumented use: prefix in the backlog
`use:git-commit-message` is referenced by CONTRIBUTING.md but appears in
none of the lists that define the script vocabulary, so the rule "reuse
an existing prefix, never invent one" is currently violated by its own
exception.

Recorded as a task rather than fixed here: the prefix wants a name that
says what it is for, and that is the same decision as naming a prefix
for the other lifecycle scripts, so the two should be chosen together.
2026-09-08 22:32:05 +02:00
tmu 8efd13b2f4 ✨ Add branch front-door command
The branching model assumed a clean, current `main` and a green baseline
before any edit, but both were prose, and prose nobody checks silently
becomes a suggestion. `npm run branch -- <prefix>/<desc>` asserts the
precondition and only then creates the branch, so a later failure is
attributable to the change that caused it.

Checks run cheap-first (`--porcelain` deliberately catches untracked
files, which would otherwise ride onto the new branch) and the suite
runs last, so an ineligible tree never pays for it. `main`'s remote is
derived from its upstream rather than hardcoded: this repo has both
`origin` and `origin_https`, and `main` tracks the latter, so a
`git fetch origin main` currency check would compare against a ref that
is never updated here. The baseline runs after switching to `main`, so a
red `main` restores the starting branch instead of stranding the caller
on it.

The prefix stays a judgment call: the script validates it against the
documented vocabulary instead of inferring it.

Prose kept as index only — AGENTS.md points agents at the command from
the task workflow, CONTRIBUTING.md owns the model and the bare-script
tier (dropping its stale hardcoded count of "two" conveniences).
2026-09-08 22:14:34 +02:00
tmu 93cb37441d 📝 Document pi-lsp tooling and agent usage
README § Tooling and § Tooling decisions record the extension and the
reasoning behind it: read-only by design, auto-detects TypeScript 7,
and never a correctness gate — verify is. AGENTS.md § First action tells
agents the lsp_* tools exist, to prefer lsp_references over grep -w for
colliding identifiers, and to treat empty LSP output as inconclusive.

Close the backlog evaluation task with a resolved note, and allowlist
the tsgo/tsserver tool names for cspell.
2026-09-08 20:47:40 +02:00
tmu 863198d472 ✨ Adopt @spences10/pi-lsp project-local
Declare the read-only LSP extension in .pi/settings.json so it is
shared with the team and auto-installed on project trust.

Pin 0.0.46 explicitly: a bare `pi install` writes `^0.0.10`, and in
semver a caret on 0.0.x pins to exactly 0.0.10, which hard-wires
typescript-language-server and predates TypeScript 7. 0.0.46 detects
the repo's own typescript@7 (no lib/tsserver.js) and spawns
tsc --lsp --stdio against it — no extra server package required.

Commit pi's own .pi/npm/.gitignore sentinel rather than adding a rule
to the project root .gitignore: pi put the file there deliberately, so
leaving it in place (and tracked) is the least-surprise option for a
human reading the tree.
2026-09-08 20:47:22 +02:00
tmu 15aef06e32 🐛 Require @done on every ✔ backlog line
The sandy081.todotasks extension marks a line done from the ✔ glyph
alone, then unconditionally decorates its @done tag via
lineText.indexOf("@done"). On a bare ✔ that returns -1, so the
decorator builds a Range with a negative character offset, VS Code
throws, and all decoration/highlighting for the document dies.

The committed backlog.tasks contained two such lines (the branching-
model subtasks), so re-opening the file reproducibly broke
highlighting. Add @done to them and document the required ✔/@done
(and ✘/@cancelled) coupling in AGENTS.md.
2026-09-08 14:56:47 +02:00
tmu b50149aad6 📝 Document 'release' as a bare script in prefix convention
'release' (./scripts/release.sh) runs no tool and aggregates no
prefix:* family, so it fits neither a tool entry point nor a tier
aggregator. List it among the bare conveniences alongside 'verify'.
2026-09-08 14:07:36 +02:00
tmu 06bc6bc43e 📝 Document GitHub Flow branching model and agent task workflow
Adopt single-developer GitHub Flow: branches off main with a
feature/fix/chore prefix, merged back via 'git merge --no-ff' (local
PR). Releases are not triggered by pushes; only the maintainer runs
'npm run release', which tags and pushes; CI publishes to npm on the
tag. Gitea is the lab; GitHub is reserved for later promotion.

Replace the 'not yet settled' backlog note in AGENTS.md with concrete
agent instructions: a task with subtasks gets a branch, a leaf task is
worked on the current branch, and a fixed handover template (Implemented
/ Judgement calls / Known problems) frames the pre-merge review. Drop
the stale 'MR' reference in 'Never do' (no MR workflow). Update the
feedback-tier table and publishing workflow to gate CI on push to main,
not PRs. Check off the branching-model backlog group and open a task
for the release CI workflow (blocked on a missing Gitea runner).
2026-09-08 14:07:33 +02:00
tmu 75d605fabe 📝 Add backlog.tasks template and format rules
Introduce backlog.tasks, a project backlog in vscode-todotasks format
(not Markdown), seeded as a reusable template: Setup, v1.0, Bugs,
Enhancements, Documentation and Maintenance projects, with the
implementation tasks to be filled in later.

Document the format in AGENTS.md so agents can read and update the file,
recommend sandy081.todotasks in .vscode/extensions.json, and whitelist
"todotasks" for cspell, which both new mentions otherwise fail.

The branch, review and release policy is deliberately not asserted here:
it is unsettled, so it is tracked as a task-group under Setup and AGENTS.md
tells agents to take branch and merge instructions from the user until that
work is documented.
2026-09-08 11:42:46 +02:00
tmu 837203c29e 📝 Rename test-loop "blue" phase to "type"; cite TDD
The type-first spec step was labelled "Blue", which collides with the
Red-Green-Blue convention where blue means the refactor phase (and our
loop already has an explicit Refactor step). Rename phase 1 to Type and
describe the loop as type -> red -> green -> refactor.

Tie the discipline to its established name: Type-Driven Development
(Edwin Brady), quoting the Idris framing -- treat the type as the plan,
let the compiler/type-checker drive you to a program that satisfies it.
Section heading becomes "Testing discipline (type-driven)"; update the
AGENTS.md pointer + anchor. Add "idris" to the cspell dictionary.

De-duplicate: CONTRIBUTING.md no longer re-lists the banned directives
(@ts-ignore, as casts, ...); it links to AGENTS.md "Never do", the
single home for that rule, so the lists can't drift.
2026-09-07 23:44:35 +02:00
tmu c7f566500e ⬆️ Upgrade dependencies to latest versions 2026-09-07 23:01:33 +02:00
tmu 09c5b129bb ✨ Add pubv-based release.sh and npm run release
Release front-door: pubv runs preflight + the interactive semver
heuristic and graduates CHANGELOG.md [Unreleased] -> dated; npm version
syncs package.json + lockfile; git commit --amend folds them into
pubv's single release commit; the tag is created AFTER the amend so it
marks the exact commit on main that gets published; then push. Tags are
v-less (--tag-prefix=none) to match the changelog compare refs.

@runwisp/pubv added as a devDependency. The short "how we got here /
what was rejected" note lives as a comment atop the script; the docs/
exploration was removed as too much to read.
2026-09-07 18:17:28 +02:00
tmu b9fb054176 🔧 Point repo metadata at Gitea; drop redundant .npmignore
The repository.url shipped github.com/tmueller/... but the real remote
is gitea.e1nsnull.de/tmu/..., so the npm page's Repository link 404'd.
Add homepage + bugs, fix the URL, and rename the CONTRIBUTING "GitHub"
reference to the forge. Delete .npmignore: when a package.json `files`
allowlist is present npm never consults .npmignore, so it was dead
weight that would only drift. Add "gitea" to the cspell dictionary.
2026-09-06 14:42:35 +02:00
tmu 464789b385 📝 Document type-first (red/green/blue) testing discipline
New behavior is written type-first: the expectTypeOf (blue) comes
before the assert (red), since for a pattern-matching library the
types are the feature. Adds a CONTRIBUTING.md section and a
discoverability pointer in AGENTS.md's "Read these" index.

Human-facing rationale lives in CONTRIBUTING.md; AGENTS.md only links
it so agents and reviewers don't diverge.
2026-09-06 01:08:09 +02:00
tmu 49a78f3683 📝 Deduplicate docs into one home per fact
The same facts were restated in up to three files, and the
restatements had already drifted: two claimed timings were 2-5x
off, and the maintain rationale existed in four copies with
different numbers each. Duplicated prose is a liability, not
redundancy.

Each fact now has exactly one owner, with links from the others:

- tool inventory: README § Tooling
- configuration rationale: README § Tooling decisions
- prefix taxonomy: CONTRIBUTING § Script prefix convention
- what runs when and at what cost: CONTRIBUTING § Feedback tiers
- publish procedure: CONTRIBUTING § Publishing workflow

README loses its whole Script prefix convention section and the
Workflows section; CONTRIBUTING loses the Why-these-splits bullets
that repeated the tier table and the rules list. Prose is 23KB to
17KB, with no rule or rationale dropped.
2026-09-06 00:45:15 +02:00
tmu 892aead383 🐛 Fix commit template setup with git config
Copying the template into .git/COMMIT_EDITMSG has no lasting
effect: git pre-fills that file from the commit.template config,
so the script registered nothing and the template never appeared.
Use git's own mechanism, which is what git reads for every
interactive commit.

Name, prefix and call site are unchanged, so README's setup
bullet stays correct; only the sentence describing the old copy
in CONTRIBUTING needed rewording. Note the config holds a path
relative to the working directory, so it applies to commits made
from the repo root.
2026-09-06 00:26:22 +02:00
tmu 682ecf1163 ✨ Enforce pinned Node via engine-strict
The `engines` field is advisory by default: an install run under an
unpinned Node only emits a notice and still succeeds, so it rewrites
package-lock.json with that older npm's resolution rules. That is not
hypothetical — it happened while dropping the redundant platform
bindings, and the resulting lockfile was rejected by `npm ci`.

With engine-strict the mismatch is a hard failure instead, so the pin
in .node-version is actually load-bearing for lockfile integrity.
Verified: node 24 install now aborts with EBADENGINE, node 26 stays
green (`npm run verify`, `npm ci` in sync), and npm auto-excludes
.npmrc from the published tarball.
2026-09-06 00:13:12 +02:00
tmu 8b4722da26 🔥 Drop redundant optionalDependencies block
oxlint, oxfmt and oxlint-tsgolint each declare their own platform
bindings as optionalDependencies gated by os/cpu, so npm already
installs exactly the one matching the host. Listing all 20 at the
root duplicated that: the lockfile diff shows no package added or
removed, only re-tagged as a dev transitive.

The root list was also the sole reason maintain:outdated carried a
20-package --ignore-packages hack, since check-outdated scans root
deps. Without it the script is one flag and reports clean.

Verified on the pinned node 26: fresh `npm ci` in sync, `npm run
verify` green, type-aware oxlint still fires (no-floating-promises),
and `npm install --os/--cpu` dry-runs resolve the darwin-arm64 and
win32-x64 bindings from the lockfile.
2026-09-06 00:09:33 +02:00
tmu da5ab79f9b ♻️ Replace verify skill with an npm verify script
A skill duplicated AGENTS.md policy and only worked in Agent-Skills harnesses.
A bare top-level script is the repo-native affordance: every harness (and CI,
and a human) reads package.json#scripts as the source of truth. Add
`npm run verify` = `npm run check` + `test:unit` (tsc runs once, since check
already type-checks) as the one-shot whole-project correctness gate.

Delete .agents/skills/verify/SKILL.md. Document verify in README (Development +
a Tooling-decisions bullet + bare-command note in the prefix list), CONTRIBUTING
(feedback-tier table, "Before pushing", the bare-command paragraph, a "why these
splits" bullet), and AGENTS.md (test = fast iterating gate, verify = definition
of done; skill pointer removed).
2026-09-05 21:46:59 +02:00
tmu 3d0fe18d6a ✨ Add verify skill for the agent loop
A project-local Agent Skill (.agents/skills/verify/SKILL.md) wraps the
correctness ladder as an on-demand procedure: run npm run test, recover by
fixing the root cause in the types (not by suppressing them), optionally run
whole-project npm run check, and commit through the hook. Exposed as
/skill:verify for pi, and read by any Agent-Skills harness from .agents/skills/.

It is a procedure, not a doc fork: it links AGENTS.md / CONTRIBUTING.md /
package.json instead of restating the rules, so there is no drift.

Agents must not start `npm run watch` -- it never returns and blocks the turn.
The skill's ladder drops watch and calls it out under "What not to run here";
AGENTS.md gains a matching guardrail.
2026-09-05 21:28:44 +02:00
tmu 103cf04f8d ♻️ Run both maintain scans independently (;)
The maintain aggregator used &&, so if maintain:knip exited non-zero (e.g. an
unused export) maintain:outdated never ran and the report was partial. Switch
to ; so both scans always run and every finding surfaces. CI still treats the
job as non-blocking, so the trailing exit code is moot there. Zero new dep.
2026-09-05 18:49:59 +02:00
tmu 7973bf0d0b 📝 Move knip + outdated to a maintain: prefix
`npm run check` should be the fast, offline correctness ladder only (tsc +
oxlint + oxfmt + cspell, ~3s), so an agent can run it as a confirmation gate
during feature work. check:knip / check:outdated were advisory whole-project /
network scans, and check-outdated exits non-zero whenever any dep is behind.
Keeping them in check made `npm run check` (and the CI build gate) fail on
dependency freshness, which must not block an unrelated feature PR.

Rename them to maintain:knip / maintain:outdated, aggregate under `npm run
maintain`, and run it in CI as a dedicated non-blocking job (continue-on-error)
that surfaces findings without ever gating a merge. Update the script-prefix
convention, the feedback-tier table, and AGENTS.md: the agent may now run
`npm run check`; only `maintain` stays out of the feature loop.
2026-09-05 16:47:55 +02:00
tmu 8940537fc0 📝 Add hook-discipline rule to AGENTS.md
The agent must not use `git commit --no-verify` (or skip any hook) to get a
green run — bypassing a check to silence it is the same anti-pattern as
@ts-nocheck or an oxlint-disable. The pre-commit checks are fast and offline,
so a redundant run costs nothing. Records the "redo through the hook" recovery
command.
2026-09-05 16:35:14 +02:00
tmu 436b49e3be 📝 Add AGENTS.md as the agent entry point
AI coding agents read AGENTS.md automatically, not README. Make AGENTS.md
a thin pointer: the stable first-action facts plus links to CONTRIBUTING.md,
README.md, and package.json#scripts. Keep the rules prose in CONTRIBUTING.md
so humans and agents don't diverge (same anti-drift move as dropping
project-specs.md).

Clarify the agent's verification loop: npm run test is the mandatory gate;
the pre-commit hook already runs the fast offline checks (tsc, oxlint, oxfmt,
cspell) on staged files, and npm run check (knip + outdated) is repo-maintenance,
not part of the feature loop. Forbid type-system escape hatches (@ts-nocheck,
oxlint-disable, as-casts) as agent-only rules; a human may still add a
review-visible disable as a last resort, so the location convention stays in
CONTRIBUTING.md with a forward reference.
2026-09-05 16:32:15 +02:00
tmu 8924dd67d3 ✨ Add watch tier with watch:test child
Adds the earliest feedback tier to the ladder: `npm run watch`
re-runs tests on file save, started manually in a dedicated
terminal pane. Built on Node 26's native `node --test --watch`
(no new dependency).

Structure follows the existing prefix convention:
- `watch:test` — runs the test suite in watch mode
- `watch` — umbrella that aggregates `watch:*` children
  (currently just `watch:test`; future `watch:oxlint` etc.
  would aggregate here, switching to concurrent execution)

CONTRIBUTING.md documents the new tier in three places: the
prefix convention list, the feedback tiers table (new top row),
and a 'Why these splits?' bullet explaining why watch is a
manual tier rather than a hook. README's Development section
gains a one-line pointer.
2026-09-05 00:40:41 +02:00
tmu 538272c222 📝 Restore unique maintainer content as CONTRIBUTING.md
After dropping project-specs.md last commit, three categories of
content were lost that have no other home in the repo:

1. The script prefix convention with its 'pick the right prefix,
   don't invent one' rule
2. The feedback-tier system (table + 'why these splits?')
3. The publishing workflow (tagged-release flow)

These are maintainer/contributor-facing material, not user-facing.
The standard OSS location for this kind of doc is CONTRIBUTING.md,
which keeps it separate from README.md (user docs) so the two
don't drift. README.md gains a pointer at the bottom.

The content is condensed: no config dumps, no restatements of
package.json, no historical commit-message examples. Only the
rationale that isn't already in the actual config files.
2026-09-05 00:35:00 +02:00
tmu e78f624cbb 🔥 Drop project-specs.md; absorb unique rationale into README
project-specs.md was a parallel document that restated most of what was
already in package.json, README.md, and the config files themselves.
The only content that wasn't already captured elsewhere was the
'why' behind a handful of non-obvious tooling choices, which now
lives in README.md as a new 'Tooling decisions' subsection.

This eliminates the drift problem between the two docs (the source
of drift in the previous commit) by having one source of truth for
'what' (the config files) and one source for 'why' (README).
2026-09-05 00:21:56 +02:00
tmu 1a749b13d6 📝 Reconcile project-specs.md and cspell.json with the feature/setup branch 2026-09-05 00:13:10 +02:00
tmu 1fe3dbe28b ✨ Add npm run fix aggregator for the fix:* scripts
Override the prior design choice: there is now an npm run fix
script that runs fix:oxlint && fix:oxfmt. Rationale: the friction
of having to remember and run two separate fix commands after
npm run check outweighs the 'intentional fixes' argument once
check has already told you which fixers are needed. The diff
after running fix remains the review surface.

- package.json: new 'fix' script (npm run fix:oxlint && npm run fix:oxfmt)
- project-specs.md: updated the FIX section to document the new
  aggregator, removed the 'no fix aggregator by design' text in
  two places (FIX section and PREFIX CONVENTION section)
- README.md: same updates, plus the Development section header
  changed from 'Format/Fix' to 'Individual fixes' to reflect
  the new hierarchy
2026-09-04 23:58:18 +02:00
tmu 8a8d57172c ⬆️ Update cspell 2026-09-04 23:53:26 +02:00
tmu 7e9401590e ✨ Add pre-push hook that runs the test suite
The pre-commit hook is file-scoped (LEFTHOOK_FILES), so it can't
naturally run the test suite. Pre-push is the right tier for it:
- Runs after all commits are made but before the push leaves
  the machine, catching regressions that span multiple commits
- ~3.5s including the tsc step (negligible vs the typical push
  round-trip to CI)
- Offline and deterministic, same philosophy as pre-commit

lefthook.yml: new pre-push section, sequential (parallel: false
since there's only one command, but the explicit value documents
the intent that this hook runs commands in order rather than
racing).

project-specs.md: updated the CHECK TIERS table to add the
pre-push column with  in it. Updated the rule-of-thumb
list to include the pre-push tier. Updated the 'why' notes to
explain why  lives in pre-push rather than pre-commit
(LEFTHOOK_FILES doesn't apply to the test runner).
2026-09-04 23:41:59 +02:00
tmu 74e4538094 📝 Document the three-tier check system (pre-commit / check / CI)
Adds a 'CHECK TIERS' subsection under the existing HOOKS section
in project-specs.md that explicitly documents:

- Where each check runs (pre-commit, npm run check, CI build, CI publish)
- A summary table mapping scripts to execution contexts
- The rationale for each split (speed, scope, side effects)
  - Why check:knip is not in pre-commit (~4s, whole project)
  - Why check:outdated is not in pre-commit (network dep, advisory)
  - Why publish:* is not in check (validates dist/, needs build)
- Cross-reference from the CI/CD section back to the tiers table

The split is a deliberate design choice: pre-commit is the fast
safety net for what you just changed, npm run check is the full
local audit, CI is authoritative. Documenting it makes the
rationale explicit and stops anyone from re-adding the slower
checks to the hook.
2026-09-04 23:36:32 +02:00
tmu 34e9b52ee8 ♻️ Move type-aware config to .oxlintrc.json; use source-level disable directives
Cleaner separation of concerns:
- options.typeAware: true in .oxlintrc.json activates type-aware
  rules declaratively (equivalent to --type-aware CLI flag, but
  the script command stays clean: just 'oxlint ...')
- Remove the 3 type-aware rule disables from .oxlintrc.json
- Add source-level oxlint-disable directives instead:
  - 4x typescript/no-unsafe-type-assertion (pattern.ts: keysMatch
    Object.keys() cast, candidate[] cast; structuralMatcher value
    as S cast; match.ts: handler as ... cast in nextCases)
  - 1x typescript/no-unnecessary-type-parameters (pattern.ts:
    keysMatch <S extends object>)
  - 1x file-level typescript/no-floating-promises in index.test.ts
    (expectTypeOf() is a sync type-assertion library that the
    type-aware linter misidentifies)

Disabling rules at the source (next to the line that needs the
exemption) documents intent more clearly than a global config
override, and makes the trade-off visible to anyone reading the
code. Re-enabling a rule in the future only requires removing the
inline comment, not editing a central config.
2026-09-04 23:27:58 +02:00
tmu 2b3ef0f721 ✨ Add oxlint-tsgolint for type-aware linting
Activates type-aware rules via oxlint --type-aware, backed by
oxlint-tsgolint (TypeScript-Go). Catches unsafe type assertions,
unnecessary type parameters, and other issues regular oxlint
cannot see.

- Add oxlint-tsgolint devDep + 6 platform-specific native bindings
  as optionalDependencies (same pattern as oxlint)
- Add --type-aware flag to check:oxlint
- Add 6 @oxlint-tsgolint/* platforms to check:outdated ignore list
- Disable 3 type-aware rules in .oxlintrc.json with rationale:
  - typescript/no-unsafe-type-assertion, typescript/no-unnecessary-type-parameters:
    fire on legitimate generic type machinery in keysMatch/MatchBuilder
    that needs type-system restructuring (deferred to a follow-up)
  - typescript/no-floating-promises in test files: expectTypeOf() is
    a sync type-assertion library that oxlint-tsgolint misidentifies

Code simplifications enabled by the new strict checks:
- src/match.ts: drop value as unknown casts (T is already assignable
  to unknown) and the redundant run(value) as R cast
- src/index.test.ts: drop unnecessary 'X' as 'X | Y' assertions in
  match<...>(...) calls (literals are already assignable to the union)

Documentation updates in project-specs.md and README.md. Add
'tsgolint' to cspell word list.
2026-09-04 23:20:02 +02:00
tmu ec98223f8d ✨ Add knip for unused dependency and dead code detection
- Add check:knip using --include dependencies,exports,files
  (skips the noisy 'types' category, which produces false positives
  for libraries whose exported types are part of the public API)
- Remove tslib and type-fest (both caught as unused by knip)
- Add 'knip' to cspell word list
- No knip config file: the --include flag keeps the scope targeted
  without boilerplate, matching the 'keep it simple' principle
- Document in project-specs.md (CHECK section) and README.md
2026-09-04 23:08:56 +02:00
tmu 3fa72820df ✨ Add @arethetypeswrong/cli (attw) as publish:attw
Validates the emitted .d.ts declarations against multiple TypeScript
module-resolution scenarios. Same rationale as publint: it validates
the publishable artifact, not the source, so it belongs in the
publish: prefix, not check:.

- Add publish:attw script using --profile esm-only (the package is
  intentionally ESM-only; CJS resolution is out of scope by design)
- Add step to CI publish job, after publish:publint and before
  npm publish
- Document the script and the esm-only rationale in project-specs.md
  and README.md
- Add 'attw' and 'arethetypeswrong' to cspell word list
- Remove unused 'stricter' word that was added speculatively before
2026-09-04 18:08:23 +02:00
tmu 01d1084e1f 🚚 Rename publint script to publish:publint and document prefix convention
publint validates the publishable artifact (dist/ vs package.json),
not the source. It should run only at publish time, in the CI publish
job, not on every commit.

- Rename check:publint -> publish:publint
- Remove from 'check' chain
- Add 'publish:publint' as a step in the CI publish job, right
  before 'npm publish'
- Introduce a 'publish:' script prefix for publish-time-only scripts
- Document the prefix convention (check:, fix:, test:, publish:)
  in project-specs.md (as a top-level subsection under Scripts)
  and in the README
- Add 'publint' and 'stricter' to cspell word list

A new script should pick the prefix that matches its lifecycle, not
invent a new one. The 'publish:' prefix has no 'npm run publish'
aggregator by design (publishing is CI-only).
2026-09-04 18:01:01 +02:00
tmu 24a3880aad 🐛 Fix test scripts: Node 26 doesn't auto-discover tests in directory args
node --test src/ on Node 26+ treats src/ as a module path, not as a
directory to scan for test files, producing a 'Cannot find module'
error. Switch the test scripts to an explicit glob that lists all
*.test.ts files under src/.

Affects test, test:ci, test:unit.
2026-09-04 17:47:30 +02:00
tmu 7de4604ab4 ♻️ Move emit-only options from tsconfig.json to tsconfig.build.json
- Build-only options (target, declaration*, sourceMap, outDir) live in the
  build config where they belong
- Drop redundant options (composite: false is the default;
  allowSyntheticDefaultImports is implied by esModuleInterop; resolveJsonModule
  is unused)
- Drop lib override to inherit es2025+ libs from @tsconfig/node26
- Add explicit include to tsconfig.build.json (include is not inherited via
  extends, only exclude was carrying the file selection by accident)
2026-09-04 17:46:03 +02:00
tmu b649013d1c ✨ Extend tsconfig from @tsconfig/node26 and @tsconfig/strictest
- Replace hand-rolled strict flags with @tsconfig/strictest
- Pick up Node 26 module/lib/target from @tsconfig/node26
- Add exactOptionalPropertyTypes (the main strictness gain)
- Pin target to es2024 to remain conservative for the published build
2026-09-04 17:33:43 +02:00
tmu c6f7a5d1b0 🔧 Reconcile config/doc contradictions and remove stale entries 2026-09-03 22:08:01 +00:00
tmu 99fd85ca7e 🔧 Resolve oxlint warnings; defer stylistic rules to oxfmt 2026-09-03 21:33:04 +00:00
tmu b3a4e14f45 🔥 Drop typescript.tsdk references; TS 7 is provided by the typescriptteam extension 2026-09-03 21:09:38 +00:00
tmu c3f913fae9 🔧 Fix small issues with the project setup 2026-09-03 21:01:28 +00:00
tmu 0598a9eb4a 🔧 Expand oxfmt to format project config and docs files 2026-09-03 20:58:33 +00:00