-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplugin.toml
More file actions
33 lines (28 loc) · 12.3 KB
/
Copy pathplugin.toml
File metadata and controls
33 lines (28 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Source of truth for plugins/perfect-typescripter/.claude-plugin/plugin.json
# AND plugins/perfect-typescripter/.codex-plugin/plugin.json. Both files are
# generated from this one — edit plugin.toml, then run:
# ./lab-tools/sync-plugin-manifests --plugin perfect-typescripter
name = "perfect-typescripter"
version = "1.38.3"
description = """\
1.38.0 fixes two framework-boundary false positives (issues #4, #5) at the root, with no allowlist or comment-tag escape an AI could silence the guard through. `positionalArgs` no longer fires on a `name: (...) => ...` callback property whose object literal is passed as a call argument — that callback's signature is contextually typed by the callee (e.g. `useJakutaResultQuery({ endpointFn: (client, args) => ... })`), so it is not an API the author declares; a standalone object's method still fires. `nullUndefined` and `optionalChaining` generalize the former react-hook-form-only boundary allowance: any file that imports an external package (a bare module specifier, not a relative or alias path) may carry `| undefined` / `| null` in function signatures and value annotations and may use `?.`, because those are forwarding upstream library shapes. The allowance is import-gated and structural — it cannot be turned on without a real import of a real package — and `nullUndefined` still fires on `type X = ... | undefined` / `interface X { foo: ... | undefined }` declarations inside such files, because those ARE user-declared shapes. The hardcoded `react-hook-form` / `react-day-picker` list is gone; there is no per-library list and no per-file allowlist to maintain. \
\
1.37.0 narrows `optionalChaining` and `nullUndefined` for the react-hook-form boundary. Files that import `react-hook-form` may use `?.` (file-scoped allowance — `errors[field]?.message` is the library's only documented read pattern) and may carry `| undefined` / `| null` in function signatures and value annotations (the upstream library types are unavoidable: FieldError | undefined etc.). Inside such files, `nullUndefined` continues to fire on `type X = ... | undefined` and `interface X { foo: ... | undefined }` — those are still user-declared shapes. Files without the react-hook-form import retain the strict ban. The invariant ("ban DU-collapsing shapes YOU DECLARE") is preserved; only consumption of upstream library shapes is permitted. \
\
1.36.0 adds PRODUCT SWITCH MATRIX enforcement in typescript_guard.js. Nested discriminant switches over previous/next-style DUs now fire when an inner switch repeats the same returned `{ Kind: "..." }` from 3+ case bodies, because the code is hand-writing a product matrix that should be classified once into a relationship DU. Configurable via `rules.productSwitchMatrix.enabled` and `minRepeatedReturnKind`. test/typescript_guard.test.js adds the awakening snapshot transition fixture that motivated the rule. \
\
1.35.5 kills two `@why <tag>` comment-based escape hatches in typescript_guard.js (`@why widen` on `LiteralUnion | string` declarations and `@why phantom` on declarations with unused type parameters) per the why-tag canonical spec which already says "Not a bypass-pass" — the hatches were violating their own spec. The `phantomTypeParams.exemptions.typeParamNames` config list remains as the project-wide naming-convention opt-out (separate mechanism, not a per-site comment bypass). test/phantom_type_params.test.js case 14 was flipped from asserting suppression to asserting the violation fires on the same fixture. Plus routes why_tag_guard.js through the new shared `hook-lib/lib/why-tag-grammar.js` (vendored into `lib/`) for grammar constants, reason validation, compactor, and denial-message template. The JSDoc parser and the exported-function detector stay in this plugin. The denial message now uses the canonical grouped layout (Missing / Invalid sections, plugin-tag header, line+signature+compacted-reason per failure, SKILL.md pointer, example) so both per-language guards emit identical structure. test/why-tag-guard.test.js updated to match the new wording; positive-case behavior (valid reasons accepted, weasel / WHAT-only / short reasons rejected) is unchanged. \
\
1.35.4 stamps agent `color` frontmatter per the ai-lab agent color schema documented in `.claude/CLAUDE.md`. Cosmetic only: Claude Code renders the color in the agent picker, task list, and transcript so operational categories (audit, prevention, exploration, testing, generation, adversarial, planning, polish) are visible at a glance. \
\
1.35.3 rejects WHAT-only @why restatements by requiring an explicit purpose connector such as so/because/prevents/preserves/without/instead-of.
1.35.2 fixes the MultiEdit blind spot in typescript_guard.js: Claude sends multi-hunk edits as `edits[].new_string`, not `content`, so the hook now uses the shared hook-input extractor and blocks null/undefined/other TypeScript violations inside every replacement. 1.35.1 compacts ts-rules-inject.js SessionStart output to a short active-rules pointer with the full rulebook left in the typescript-rules skill. 1.35.0 adds STATE BRANCHING enforcement: else-if ladders, consecutive discriminant if/return ladders, nullish-combination parser ladders, and equality ternaries must become discriminated unions plus exhaustive switches. Narrow exemptions live under `rules.stateBranching.exemptions.allowedFiles` / `allowedDirectories`. 1.34.3 adds a config-write guard that blocks AI write tools from editing `.claude/ai-lab/perfect-typescripter/config.json` or `.perfect-typescripter.json`, closing the bypass where an agent gets blocked, silently adds an ignorePath exemption, then retries the bad write. 1.34.2 compiles the Codex PreToolUse wrapper to a cwd-independent launcher and emits native `permissionDecision: deny` JSON, so `apply_patch` denial is enforced in live Codex instead of surfacing as a failed-open hook process. 1.34.1 trims the setup-eslint-integration skill router description: drops the per-file-vs-cross-file explanation prose (belongs in the skill body), keeps every trigger phrase and the trigger condition. Saves ~164 tokens per session that loads the skill index. 1.34.0 makes ESLint setup proactive instead of user-triggered. New auto-loading skill `setup-eslint-integration` detects when a TypeScript project has perfect-typescripter installed but no ESLint wiring, then spawns the new `eslint-installer` agent which dry-runs and applies the orchestrator from 1.33.0. The slash command `/setup-eslint` stays as a manual escape hatch but is no longer the expected entry point. Together this closes the gap where users would not know the slash command existed and would ship duplicate envelopes / shared variant literals / prefix drift undetected for months. The agent verifies the wiring with `npx eslint .` after applying, surfaces existing violations to the user, and prints (does not write) a CI snippet because CI host varies. 1.33.0 adds PHANTOM TYPE PARAMETER detection AND ships a bundled-local ESLint plugin (`eslint-plugin-perfect-typescripter`) with four cross-file rules that the per-file PreToolUse hook cannot see (cross-DU duplication, cross-DU naming drift). PreToolUse rule: `phantomTypeParams` blocks Write/Edit on .ts/.tsx where a generic declares a type parameter that is never referenced in the body of the type / interface / class / function — phantom params look like they constrain but don't (`WorldStateRegistry<TKind, X, Y>` with TKind unused lets callers pass any value for TKind). Detector strips comments and string literals before counting `\bNAME\b` occurrences across the declaration extent (header through closing brace / semicolon for type aliases, with `=>` skipped as a token so arrow function types do not prematurely close the alias). Configurable via `rules.phantomTypeParams.exemptions.typeParamNames` and the `// @why phantom` comment escape on the declaration line. ESLint plugin lives at `plugins/perfect-typescripter/eslint-plugin/`, ships rules `no-phantom-type-param` (mirror of the hook for human edits), `no-duplicate-envelope-shape` (N+ type aliases / interfaces with identical field sets — extract a generic), `no-shared-variant-literal-across-discriminated-unions` (the same string literal appears in N+ separate DUs — extract a shared cause), `no-variant-prefix-drift` (sibling DUs disagree on the prefix for the same operational suffix, e.g. `PlayerLeft` vs `OwnerLeft`). Wire into a project with the new `/setup-eslint` slash command, which detects project root and package manager (npm / pnpm / yarn / bun), detects existing config style (flat / legacy / none), installs deps via `file:` protocol pointing to the bundled plugin (no npm publish pipeline, no ts-morph dep), merges rules into legacy `.eslintrc.json` or writes a fresh / sibling flat `eslint.config.js`, and scaffolds husky + lint-staged pre-commit by default so cross-file rules are systematic and not advisory. 1.32.0 adds FALLTHROUGH GROUPING: stacked case labels with no intervening body (`case 'A':\n case 'B':\n break;`) are blocked. Per the branch-fan-out rule from CLAUDE.md, every case body must be explicit (one body per case, even if it is just `break;`). Comments between case labels do not mask the grouping. Block-bodied cases (`case A: { ...; break; }`) and cases with their own statement-then-break are unaffected. If multiple cases truly do the same thing, that is a hint they should collapse into one DU variant upstream; if they differ in field access, telemetry, or next-state, fan them out. Configurable via rules.fallthroughGrouping.enabled in config/default.json. 1.31.0 switches typescript_guard's positional-args detector to the canonical parse-function-signature lib in hook-lib (vendored under lib/). Local extractBalancedParenBody and the inline depth-tracking comma counter are gone; both replaced by countTopLevelCommas + extractBalancedParenBody from the shared lib. Same behavior, single source of truth for every plugin that parses TS function signatures. Enforces discriminated unions everywhere. Blocks null, undefined, optionals, ?., !. Forces upstream DU creation with switch statements. 1.30.0 adds STRING WIDENING: type aliases unioning a literal-string union (or any RHS containing a string literal) with bare `string` are blocked. TypeScript silently collapses `LiteralUnion | string` to plain `string` and breaks every exhaustive switch downstream with no compile warning. The fix is to widen the upstream wire / source type to carry the real typed kind and keep the consumer alias narrow. Escape hatch `// @why widen` for genuine freeform-text-with-hint-literals (autocomplete APIs); configurable via rules.stringWidening.exemptions.commentTag. Detector uses balanced-bracket walking so `Map<A | B, string>` and other nested-pipe shapes don't false-fire. Primitive widening like `string | number` (no literal in the union) is intentionally allowed. 1.29.0 adds a SessionStart hook (ts-rules-inject.js) that emits a short headline summary of the bans and mandates on startup/resume/clear/compact when the project has a tsconfig.json, so a fresh session opens with the rule shape already in context instead of waiting for the first .ts edit to trigger skill auto-load. 1.28.1 makes the setup-typescripter-config skill's verify step MANDATORY: after editing the config, the skill must validate the JSON parses and run npm run type-check (or tsc --noEmit) before reporting done, so a malformed config or a regression cannot ship green. 1.28.0 adds DEGENERATE COLLAPSE: comparator-named functions (compare*, diff*, equals*, isSame*, isEqual*, matches*, isMatch*, areSame*, areEqual*, same*) returning boolean or a 2-tag string-literal union over typed (non-primitive, non-branded) inputs are blocked. The fix is a relationship DU whose variants enumerate the real outcomes (e.g. SlotTransition with NoTransition / ActivateOnly / DeactivateOnly / SwapSlots) the caller switches on flat. Configurable exemptions via rules.degenerateCollapse.exemptFunctions in .claude/ai-lab/perfect-typescripter/config.json. 1.27.2 fixes config-path drift: the hook now reads .claude/ai-lab/perfect-typescripter/config.json (matching the README and skill) instead of the legacy flat .claude/perfect-typescripter/config.json. 1.27.0 replaces the 1.26.0 naming-based cluster rules with SAME-TYPE CLUSTER (3+ fields of identical type). 1.25.2 vendored the hook-lib expanded helpers. Enforces the @why tag on every exported function per the plugins/why-tag spec.\
"""
[author]
name = "CheckPickerUpper"
[targets]
claude = true
codex = true
opencode = true
[codex]
skills = "./skills/"