claude-toolkit 0.11.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.0 (2026-08-08)
4
+
5
+ `ct-typescript-conventions` now selects a compiler as well as types: prefer the TypeScript 7 native compiler where a project already ships it, fall back cleanly where it does not, and never upgrade a project unprompted. Availability-aware in the same spirit as `ct-esnext-idioms`.
6
+
7
+ - feat: prioritise the TypeScript 7 native compiler in `ct-typescript-conventions` — TS7 reached GA on 2026-07-08 as `typescript@7.0.2`, with Microsoft reporting 8x–12x faster full builds. The skill detects the *installed* version (`require('typescript/package.json').version`) rather than the declared semver range, which `overrides`/`resolutions`/catalogs routinely defeat, and names the three detection traps: the binary is `tsc` not `tsgo` (that was the pre-GA `@typescript/native-preview` name), range-sniffing is unreliable, and an API probe gives a false negative because TS7 exposes only `version`/`versionMajorMinor`.
8
+ - feat: document when *not* to switch — TS7.0 ships no stable programmatic API, so `typescript-eslint`, `ts-jest`, `ts-node`, `vue-tsc`, `svelte-check`, `@astrojs/check` and `@angular/compiler-cli` break at install or lint time rather than at `tsc --noEmit`, which means a green type-check proves nothing. The skill prescribes the documented side-by-side alias install instead of a downgrade.
9
+ - feat: add the TS7 `tsconfig.json` deltas — the options TS7 rejects outright (`ignoreDeprecations` no longer silences them), plus the two changed defaults behind most real breakage. Verified empirically against 5.9.3 / 6.0.3 / 7.0.2: both defaults land in 6.0 and carry forward, `types` fails `TS2591` even with `@types/node` installed, `rootDir` only bites when an `outDir` is set, and `types: ["*"]` is `TS2688` on 5.x — so no literal value is safe across majors.
10
+ - feat: skill triggers now fire on typecheck, tsconfig, and compiler work — added the `tsc`, `tsgo`, `tsconfig`, `typecheck` and `compiler` keywords, the `**/tsconfig*.json` path pattern, and intents for running a typecheck or upgrading TypeScript.
11
+ - docs: TSConfig cheat sheet gains a TypeScript 7 delta section; the skill's new remit is reflected in the generated skills index, README, and docs index.
12
+
13
+ ## 0.15.0 (2026-07-04)
14
+
15
+ Two new authoring-convention assets — an opt-in `esnext` stack and an always-on `ct-code-style` core skill — plus Biome enforcement of the mechanically-checkable style rules.
16
+
17
+ - feat: add the `esnext` stack (skill `ct-esnext-idioms`) — availability-aware guidance on modern ECMAScript runtime features: `Temporal` over `Date`, `using`/`await using` for deterministic cleanup, lazy iterator helpers, `structuredClone`, `Object.groupBy`/`Map.groupBy`, `Promise.withResolvers`, immutable array ops, and ESM hygiene. Detected on tsconfig `target`/`module`/`lib` = ESNext/ES2022+, package.json `"type":"module"`, `engines.node >= 20`, or a `.mjs` file.
18
+ - feat: add the `ct-code-style` core skill (always included) — universal code-structure conventions: guard clauses over nested `else`, object lookup tables over `if/else if` chains, single-level ternaries, full ES6 destructuring, and arrow-only functions with no `class`/`this`. Carve-outs defer to stack skills: never destructure reactive SolidJS `props`, and use `class`/`this` only where a platform mandates it (e.g. Cloudflare Durable Objects).
19
+ - feat: enforce the mechanically-checkable style rules in the scaffolded Biome config — `noUselessElse`, `noNestedTernary`, and `useArrowFunction`. `noMagicNumbers` is kept as skill guidance only (too noisy to enforce).
20
+ - docs: document both assets in the README, `docs/stacks/esnext-idioms.md`, `docs/skills/code-style.md`, and the docs index; cross-link the SolidJS props rule to `ct-code-style`.
21
+
3
22
  ## 0.11.0 (2026-07-04)
4
23
 
5
24
  Remediation of the 2026-07-04 core audit (46 findings): the skill-eval hook is hardened, stack-owned commands are gated behind their stack, and the `ct-` agents, commands, skills, and reference docs are corrected and aligned.
package/README.md CHANGED
@@ -107,6 +107,7 @@ Stacks in your config that are no longer detected are reported but left unchange
107
107
  | `playwright` | Playwright E2E testing, Page Objects, fixtures, CI/CD |
108
108
  | `storybook` | Storybook interaction testing, CSF 3, visual regression |
109
109
  | `capacitor` | Capacitor 8 runtime, Capgo OTA, channels; webview UI & native feel |
110
+ | `esnext` | Modern ECMAScript idioms — Temporal, iterator helpers, `using`, structuredClone |
110
111
 
111
112
  ## Core Features (always included)
112
113
 
@@ -120,8 +121,9 @@ Stacks in your config that are no longer detected are reported but left unchange
120
121
 
121
122
  - `ct-systematic-debugging` — Four-phase debugging methodology
122
123
  - `ct-testing-patterns` — TDD workflow and patterns
123
- - `ct-typescript-conventions` — TypeScript strict mode best practices
124
+ - `ct-typescript-conventions` — TypeScript strict mode best practices, and preferring the TS7 native compiler where available
124
125
  - `ct-verification-before-completion` — Evidence-based completion claims
126
+ - `ct-code-style` — Code structure & style: guard clauses, lookup tables, no magic values
125
127
 
126
128
  ### Core Commands
127
129
 
@@ -0,0 +1,134 @@
1
+ ---
2
+ name: ct-code-style
3
+ description: Code structure & style — guard clauses, lookup tables, destructuring, no classes, no magic values
4
+ ---
5
+
6
+ # Code Style
7
+
8
+ Write small, flat, data-driven functions. These conventions apply to all JS/TS. Stack skills override where noted below.
9
+
10
+ ## Guard clauses / early returns
11
+
12
+ Return early on edge cases; never nest the happy path inside `else`.
13
+
14
+ ```ts
15
+ // Bad — nested, else-heavy
16
+ const price = (user) => {
17
+ if (user) {
18
+ if (user.active) {
19
+ return user.plan.price;
20
+ } else {
21
+ return 0;
22
+ }
23
+ } else {
24
+ return 0;
25
+ }
26
+ };
27
+
28
+ // Good — guard clauses, flat
29
+ const price = (user) => {
30
+ if (!user) return 0;
31
+ if (!user.active) return 0;
32
+ return user.plan.price;
33
+ };
34
+ ```
35
+
36
+ Rule: no `else` / `else if` after a `return`.
37
+
38
+ ## Lookup tables over chained conditionals
39
+
40
+ ```ts
41
+ // Bad
42
+ const getDiscount = (type) => {
43
+ if (type === "student") return 0.1;
44
+ else if (type === "senior") return 0.15;
45
+ else if (type === "employee") return 0.2;
46
+ else return 0;
47
+ };
48
+
49
+ // Good
50
+ const getDiscount = (type) => {
51
+ const discounts = { student: 0.1, senior: 0.15, employee: 0.2 };
52
+ return discounts[type] ?? 0;
53
+ };
54
+ ```
55
+
56
+ For an exhaustive **typed** union where every case must be handled, prefer an exhaustive `switch` — see `ct-typescript-conventions`.
57
+
58
+ ## Ternaries — one level, never nested
59
+
60
+ A ternary must not contain another ternary. Nesting → extract a lookup table or guard clauses.
61
+
62
+ ```ts
63
+ // Bad — nested ternary
64
+ const label = a ? "x" : b ? "y" : "z";
65
+
66
+ // Good — lookup or guards
67
+ const labels = { a: "x", b: "y" };
68
+ const label = labels[key] ?? "z";
69
+ ```
70
+
71
+ ## Destructuring — use it fully
72
+
73
+ ```ts
74
+ const { id, name: title, role = "guest", address: { city } = {}, ...rest } = user;
75
+ // basic rename default nested (+ default) rest
76
+ const greet = ({ name, lang = "en" }) => `${GREETING[lang]} ${name}`; // in params
77
+ ```
78
+
79
+ **⚠️ Never destructure reactive framework props.** In SolidJS, `const { value } = props` breaks reactivity — read `props.value` at the point of use. This carve-out is authoritative: see `ct-solidjs-patterns`. Destructuring is for plain data objects.
80
+
81
+ ## Functions — arrow-only, no `class`, no `this`
82
+
83
+ Model behavior as functions and closures over state. No `class`, no `this` in your own code.
84
+
85
+ ```ts
86
+ // Bad — class + this
87
+ class Counter {
88
+ count = 0;
89
+ inc() {
90
+ this.count++;
91
+ }
92
+ }
93
+
94
+ // Good — closure
95
+ const makeCounter = () => {
96
+ let count = 0;
97
+ return { inc: () => ++count, value: () => count };
98
+ };
99
+ ```
100
+
101
+ **⚠️ Exception:** use `class` / `this` only when a third-party lib or platform leaves no functional alternative — e.g. Cloudflare Durable Objects / `WorkerEntrypoint` (`export class Room extends DurableObject`), or subclassing built-ins (`class AppError extends Error`). See `ct-cloudflare-d1-kv`.
102
+
103
+ ## No magic numbers or strings
104
+
105
+ Name every literal that carries meaning. User-facing text → i18n keys, never inline strings.
106
+
107
+ ```ts
108
+ // Bad
109
+ if (retries > 3) show("Trop de tentatives");
110
+
111
+ // Good
112
+ const MAX_RETRIES = 3;
113
+ if (retries > MAX_RETRIES) show(LL.errors.tooManyRetries()); // i18n key, not a literal
114
+ ```
115
+
116
+ User-facing strings belong in the locale layer — see `ct-i18n-typesafe`.
117
+
118
+ ## Anti-Patterns
119
+
120
+ | Bad | Good |
121
+ | --- | --- |
122
+ | `else` after a `return` | guard clauses |
123
+ | `if/else if` chain on a value | object lookup table + `??` |
124
+ | nested ternary | lookup table or guards |
125
+ | `class` + `this` for your own logic | arrow functions + closures |
126
+ | destructuring SolidJS `props` | read `props.x` (see `ct-solidjs-patterns`) |
127
+ | magic number / inline UI string | named const / i18n key |
128
+
129
+ ## See Also
130
+
131
+ - `ct-typescript-conventions` — exhaustive `switch` for typed unions; typing destructured params.
132
+ - `ct-esnext-idioms` — modern features these patterns pair with (`??`, `Object.groupBy`).
133
+ - `ct-solidjs-patterns` — props are reactive; do not destructure them.
134
+ - `ct-i18n-typesafe` — where user-facing strings live.
@@ -1,12 +1,70 @@
1
1
  ---
2
2
  name: ct-typescript-conventions
3
- description: Strict TypeScript patterns for type safety, readability, and maintainable codebases. Use when writing or reviewing TypeScript, adding or tightening types, or deciding between type and interface
3
+ description: Strict TypeScript patterns and compiler selection -- prefer the TypeScript 7 native compiler when a project ships it. Use when writing or reviewing TypeScript, running a typecheck, adding or tightening types, or deciding between type and interface
4
4
  ---
5
5
 
6
6
  # TypeScript Conventions
7
7
 
8
8
  Strict mode enabled. Catch errors at compile time, not runtime.
9
9
 
10
+ ## Compiler -- prefer TypeScript 7 when the project has it
11
+
12
+ TypeScript 7 (GA 2026-07-08, `typescript@7.0.2`) is the Go-native compiler; Microsoft reports **8x--12x faster full builds**. In a project that already ships it, use it for every type-check and build. Never install or bump it unprompted.
13
+
14
+ **Detect the installed version, not the declared range:**
15
+
16
+ ```bash
17
+ node -p "require('typescript/package.json').version" # major >= 7 -> TS7
18
+ ```
19
+
20
+ Resolve from the directory you are editing -- monorepo workspaces can pin different majors. Three traps:
21
+
22
+ - The TS7 binary is **`tsc`**. `tsgo` was the pre-GA `@typescript/native-preview` name -- never put it in a script.
23
+ - `package.json` ranges lie: `overrides`, `resolutions`, catalogs, and stale lockfiles all win, and `*` / `latest` / `>=5` resolve to 7.x today.
24
+ - Don't probe the API -- on TS7 `require("typescript")` exposes only `version` and `versionMajorMinor`, so a `ts.createProgram` check gives a false negative.
25
+
26
+ **Using it** -- same flags as 6.x:
27
+
28
+ ```bash
29
+ tsc --noEmit # type-check
30
+ tsc -b # build mode / project references
31
+ tsc --checkers 1 --noEmit # CI: pin the worker count
32
+ ```
33
+
34
+ `--checkers` defaults to 4. Pin it in CI -- Microsoft warns that varying it can surface order-dependent results.
35
+
36
+ **Stay on 6.x when a tool embeds the compiler.** TS7.0 ships no stable programmatic API (7.1 targets a new one), so `typescript-eslint`, `ts-jest`, `ts-node`, `vue-tsc`, `svelte-check`, `@astrojs/check`, and `@angular/compiler-cli` all break -- at install or lint time, *not* at `tsc --noEmit`. A green type-check proves nothing here. Vite, Bun, `tsx`, Vitest, and Biome are unaffected. Run both rather than downgrading:
37
+
38
+ ```json
39
+ {
40
+ "devDependencies": {
41
+ "@typescript/native": "npm:typescript@^7.0.2",
42
+ "typescript": "npm:@typescript/typescript6@^6.0.2"
43
+ }
44
+ }
45
+ ```
46
+
47
+ Keep `typescript` pointed at the 6.0 package -- blocked tools import it by peer dependency. Where TS7 is absent, the floor is `typescript@6.0.3`, the bridge release; its readiness gate is a clean build with `"stableTypeOrdering": true` and no `ignoreDeprecations`.
48
+
49
+ **`tsconfig.json` rules TS7 enforces** -- `ignoreDeprecations` no longer silences any of them:
50
+
51
+ | Now rejected | Fix |
52
+ |---|---|
53
+ | `baseUrl` | project-root-relative `paths` |
54
+ | `outFile`, `downlevelIteration` | remove |
55
+ | `target: "es5"` | `"es2015"` or later |
56
+ | `module: "amd" \| "umd" \| "system" \| "none" \| "systemjs"` | `"esnext"`, `"preserve"`, or `"nodenext"` |
57
+ | `moduleResolution: "node" \| "node10" \| "classic"` | `"bundler"`, `"node16"`, or `"nodenext"` |
58
+ | `esModuleInterop` / `allowSyntheticDefaultImports` / `alwaysStrict` set to `false` | drop the override |
59
+ | `out`, `charset`, `keyofStringsOnly`, `importsNotUsedAsValues`, `preserveValueImports`, `suppressImplicitAnyIndexErrors`, `suppressExcessPropertyErrors`, `noStrictGenericChecks`, `noImplicitUseStrict` | gone -- `TS5023: Unknown compiler option` |
60
+
61
+ Two changed defaults cause most real-world breakage. Both arrived in 6.0 and carry forward, so they bite on either major -- verified identical on 6.0.3 and 7.0.2:
62
+
63
+ - **`types` defaults to `[]`** -- `process`, `Bun` and friends disappear (`TS2591`) *even with `@types/node` installed*. Set `"types": ["node"]` / `["bun"]` explicitly, naming only packages that are actually installed (a missing one is `TS2688`). `"types": ["*"]` restores the old catch-all on 6.x/7.x but is itself `TS2688` on 5.x.
64
+ - **`rootDir` defaults to `./`** -- with sources in `src/` and an `outDir`, emit fails `TS5011`. Set `"rootDir": "./src"`. `--noEmit` does *not* surface this, so type-check CI stays green while the build breaks.
65
+
66
+ `strict` and `stableTypeOrdering` are on by default; `stableTypeOrdering` cannot be turned off.
67
+
10
68
  ## Rules
11
69
 
12
70
  - **No `any`** -- Use `unknown` + narrowing, generics, or specific types.
package/docs/README.md CHANGED
@@ -14,8 +14,9 @@ Skills that are always included regardless of stack configuration.
14
14
  | -------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
15
15
  | [ct-systematic-debugging](skills/systematic-debugging.md) | Four-phase methodology for diagnosing and fixing bugs without guesswork |
16
16
  | [ct-testing-patterns](skills/testing-patterns.md) | Framework-agnostic TDD practices, mocking strategies, and test organization |
17
- | [ct-typescript-conventions](skills/typescript-conventions.md) | Strict TypeScript patterns for type safety and maintainability |
17
+ | [ct-typescript-conventions](skills/typescript-conventions.md) | Strict TypeScript patterns, plus TS7 native-compiler selection |
18
18
  | [ct-verification-before-completion](skills/verification-before-completion.md) | Evidence-based completion claims with structured verification checklist |
19
+ | [ct-code-style](skills/code-style.md) | Code structure & style: guard clauses, lookup tables, no magic values |
19
20
 
20
21
  ## Commands
21
22
 
@@ -55,3 +56,4 @@ Stack-specific skills activated based on your `claude-toolkit.config.ts` configu
55
56
  | [ct-playwright-patterns](stacks/playwright-patterns.md) | `playwright` | E2E testing with Page Objects, fixtures, auth, network mocking, CI/CD |
56
57
  | [ct-storybook-patterns](stacks/storybook-patterns.md) | `storybook` | Interaction testing, CSF 3, play functions, a11y, visual regression |
57
58
  | [ct-capacitor-ota](stacks/capacitor-ota.md) | `capacitor` | Capacitor 8 native runtime and Capgo OTA live updates, channels, encryption |
59
+ | [ct-esnext-idioms](stacks/esnext-idioms.md) | `esnext` | Modern ECMAScript runtime idioms: Temporal, iterator helpers, using, structuredClone |
@@ -127,3 +127,34 @@ When a bundler (Vite, esbuild, etc.) handles transpilation:
127
127
  }
128
128
  }
129
129
  ```
130
+
131
+ ## TypeScript 7 Delta
132
+
133
+ > Not from the source cheat sheet — added for TypeScript 7 (GA 2026-07-08, `typescript@7.0.2`). See [ct-typescript-conventions](../../skills/typescript-conventions.md#tsconfigjson-under-ts7) for the full compiler-selection guidance.
134
+
135
+ Everything above is TS7-compatible as written, with **two additions** required when the native compiler is in use:
136
+
137
+ ```jsonc
138
+ {
139
+ "compilerOptions": {
140
+ // `types` defaults to [] from 6.0 onward. Without this, ambient
141
+ // globals (`process`, Bun's APIs) fail TS2591 even when
142
+ // @types/node IS installed. Name only packages you actually have —
143
+ // a missing one is TS2688.
144
+ "types": ["node"],
145
+
146
+ // `rootDir` defaults to "./" from 6.0 onward. With sources in src/
147
+ // AND an outDir set, emit fails TS5011 — and `--noEmit` will not
148
+ // warn you. Without an outDir it does not fire.
149
+ "rootDir": "./src"
150
+ }
151
+ }
152
+ ```
153
+
154
+ Both defaults changed in **6.0**, not 7.0 — verified identical on `typescript@6.0.3` and `7.0.2`. `rootDir` matters only when the project emits to an `outDir`; `types` matters always.
155
+
156
+ `"types": ["*"]` restores the pre-6.0 catch-all behaviour on 6.x and 7.x, but is `TS2688` on 5.x — so it is not a safe value for a config shared across majors.
157
+
158
+ TS7 also rejects options this sheet never recommends — `baseUrl`, `outFile`, `downlevelIteration`, `target: "es5"`, `module: "amd" | "umd" | "system"`, and `moduleResolution: "node" | "node10" | "classic"`. `ignoreDeprecations` does not silence them.
159
+
160
+ The TypeScript website's [tsconfig reference](https://www.typescriptlang.org/tsconfig/) has not been updated for TS7 and still documents removed options.
@@ -0,0 +1,26 @@
1
+ # Code Style
2
+
3
+ > Code structure & style: guard clauses, lookup tables, destructuring, no classes, no magic values.
4
+
5
+ **Type:** Core Skill (always included)
6
+ **Source:** [`core/skills/ct-code-style/SKILL.md`](../../core/skills/ct-code-style/SKILL.md)
7
+
8
+ ## Overview
9
+
10
+ Universal, target-independent conventions for writing small, flat, data-driven functions. Always active — no stack required. It complements `ct-typescript-conventions` (types) and `ct-esnext-idioms` (modern features). Stack skills win where they overlap.
11
+
12
+ ## Rules
13
+
14
+ - **Guard clauses / early returns** — no `else`/`else if` after a `return`.
15
+ - **Lookup tables** over `if/else if` chains keyed on a value (`discounts[type] ?? 0`).
16
+ - **Ternaries** — one level, never nested.
17
+ - **Full ES6 destructuring** — basic, renaming, defaults, nested, rest, in parameters.
18
+ - **Arrow-only, no `class`, no `this`** — model behavior as functions + closures.
19
+ - **No magic numbers/strings** — named constants; user-facing text → i18n keys.
20
+
21
+ ## Carve-outs (stack skills win)
22
+
23
+ - **SolidJS props:** never destructure reactive `props` — it breaks reactivity. See [`ct-solidjs-patterns`](../best-practices/solidjs/props-patterns.md).
24
+ - **Platform classes:** `class`/`this` only where a third-party lib/platform requires it (Cloudflare Durable Objects, `class X extends Error`).
25
+
26
+ Three of these rules are additionally enforced by Biome in the scaffolded `biome.json` (`noUselessElse`, `noNestedTernary`, `useArrowFunction`). "No magic numbers" stays guidance-only — it is too noisy to enforce mechanically.
@@ -1,6 +1,6 @@
1
1
  # TypeScript Conventions
2
2
 
3
- > Strict TypeScript patterns for type safety, readability, and maintainable codebases. Use when writing or reviewing TypeScript, adding or tightening types, or deciding between type and interface
3
+ > Strict TypeScript patterns and compiler selection — prefer the TypeScript 7 native compiler when a project ships it. Use when writing or reviewing TypeScript, running a typecheck, adding or tightening types, or deciding between type and interface
4
4
 
5
5
  **Type:** Core Skill (always included)
6
6
  **Source:** [`core/skills/ct-typescript-conventions/SKILL.md`](../core/skills/ct-typescript-conventions/SKILL.md)
@@ -9,6 +9,98 @@
9
9
 
10
10
  Rules for writing TypeScript that leverages the type system fully. These assume strict mode is enabled and prioritize catching errors at compile time over runtime.
11
11
 
12
+ The skill also covers **which compiler to run**. TypeScript 7 — the Go-native port — reached GA on 2026-07-08 as `typescript@7.0.2`, with Microsoft reporting 8x–12x faster full builds. The guidance is availability-aware in the same spirit as [ct-esnext-idioms](../stacks/esnext-idioms.md): prefer TS7 where a project already has it, fall back cleanly where it doesn't, and never upgrade a project unprompted.
13
+
14
+ ## Compiler Selection
15
+
16
+ ### Detect before you assume
17
+
18
+ Read the **installed** version, resolved from the directory being edited (monorepo workspaces can pin different majors):
19
+
20
+ ```bash
21
+ node -p "require('typescript/package.json').version" # major >= 7 -> TS7
22
+ ```
23
+
24
+ `typescript@7.0.2` whitelists `./package.json` in its `exports` map, and 5.x/6.x have no `exports` map at all, so this subpath resolves on every major.
25
+
26
+ Three detection traps:
27
+
28
+ | Trap | Why it misleads |
29
+ |---|---|
30
+ | Looking for a `tsgo` binary | TS7's only CLI binary is `tsc`. `tsgo` belonged to the pre-GA `@typescript/native-preview` package, which is frozen at a `7.0.0-dev` nightly. |
31
+ | Reading the `package.json` semver range | `overrides`, `resolutions`, pnpm/bun catalogs and stale lockfiles all override it — and `*`, `latest`, `>=5` all resolve to 7.x today. |
32
+ | Probing the API (`ts.createProgram`) | On TS7 `require("typescript")` exposes only `version` and `versionMajorMinor`, so an API probe reports a false negative. |
33
+
34
+ ### When TS7 is available
35
+
36
+ Flags are unchanged from 6.x — `tsc --noEmit`, `tsc -p <path>`, `tsc -b`, `tsc --watch`. New in TS7:
37
+
38
+ | Flag | Meaning |
39
+ |---|---|
40
+ | `--checkers <n>` | Type-checking workers per project (default 4). Microsoft warns that varying it "may surface order-dependent results" — pin it in CI. |
41
+ | `--builders <n>` | Projects built concurrently in build mode (default 4). |
42
+ | `--singleThreaded` | Most deterministic setting. |
43
+ | `--lsp` | Language server over LSP (there is no `tsserver` binary). |
44
+
45
+ Note that watch mode is labelled a prototype at the GA tag: it rebuilds but does not incrementally recheck.
46
+
47
+ ### When a tool embeds the compiler — do not switch
48
+
49
+ TypeScript 7.0 ships **no stable programmatic API**; Microsoft targets 7.1 for "a new (and different) API" with no committed date. Anything that embeds the compiler is blocked:
50
+
51
+ | Tool | Status on TS7 |
52
+ |---|---|
53
+ | `typescript-eslint` | Peer range `<6.1.0`; install fails `ERESOLVE`, and a forced install crashes during lint |
54
+ | `vue-tsc`, `svelte-check`, `@astrojs/check` | Crash on `ERR_PACKAGE_PATH_NOT_EXPORTED` — TS7 has no `./lib/*` export |
55
+ | `ts-node` | Crashes in config load |
56
+ | `ts-jest` | Peer range `>=4.3 <7` |
57
+ | `@angular/compiler-cli` | Peer range `>=6.0 <6.1` (Microsoft suggests a hybrid: TS7 at the CLI, TS6 for editor support) |
58
+
59
+ Unaffected — these carry no `typescript` dependency at all: Vite, Bun, `tsx`, Vitest, Jest, Biome.
60
+
61
+ **A type-check smoke test will not catch these failures.** typescript-eslint's breakage surfaces at `npm ci` and at lint time while `tsc --noEmit` still passes. When a blocked tool is present, install both compilers side by side rather than downgrading — this is Microsoft's documented escape hatch:
62
+
63
+ ```json
64
+ {
65
+ "devDependencies": {
66
+ "@typescript/native": "npm:typescript@^7.0.2",
67
+ "typescript": "npm:@typescript/typescript6@^6.0.2"
68
+ }
69
+ }
70
+ ```
71
+
72
+ The `typescript` key must stay pointed at the 6.0 compatibility package, because blocked tools import from `typescript` by peer dependency.
73
+
74
+ ### When TS7 is absent
75
+
76
+ The floor is `typescript@6.0.3` — the last 6.x release, and the bridge Microsoft designates between 5.9 and 7.0. It also sits inside every current ecosystem peer range. The readiness gate before moving a project to 7 is a clean 6.0 build with `"stableTypeOrdering": true` and no `ignoreDeprecations` set. That flag is a diagnostic aid only — Microsoft notes it can add up to 25% to type-checking time and is not meant to be left on.
77
+
78
+ ### `tsconfig.json` under TS7
79
+
80
+ These are rejected outright, and `ignoreDeprecations` no longer silences them (in 6.0 they were silenceable errors; in 7.0 there is no escape hatch):
81
+
82
+ | Rejected | Replacement |
83
+ |---|---|
84
+ | `baseUrl` | project-root-relative `paths` |
85
+ | `outFile`, `downlevelIteration` | remove |
86
+ | `target: "es5"` | `"es2015"` or later |
87
+ | `module: "amd" \| "umd" \| "system" \| "none" \| "systemjs"` | `"esnext"`, `"preserve"`, or `"nodenext"` |
88
+ | `moduleResolution: "node" \| "node10" \| "classic"` | `"bundler"`, `"node16"`, or `"nodenext"` |
89
+ | `esModuleInterop` / `allowSyntheticDefaultImports` / `alwaysStrict` set to `false` | drop the override |
90
+
91
+ These are gone entirely — `TS5023: Unknown compiler option`: `out`, `charset`, `keyofStringsOnly`, `suppressImplicitAnyIndexErrors`, `noImplicitUseStrict`, `importsNotUsedAsValues`, `preserveValueImports`, `suppressExcessPropertyErrors`, `noStrictGenericChecks`.
92
+
93
+ Two changed defaults account for most real-world breakage:
94
+
95
+ - **`types` defaults to `[]`.** Ambient globals like `process` vanish with `TS2591`. Set it explicitly: `"types": ["node"]`, or `["bun"]` for Bun projects.
96
+ - **`rootDir` defaults to `./`.** With sources under `src/` and an `outDir`, emit fails `TS5011`. Set `"rootDir": "./src"`. This fires **only when the program emits** — `tsc --noEmit` stays silent, so a type-check-only CI job can stay green while the build breaks.
97
+
98
+ `strict` defaults to `true`, and `stableTypeOrdering` is on and cannot be turned off. `experimentalDecorators` and `emitDecoratorMetadata` both survive.
99
+
100
+ Project references, `--build` mode, incremental builds, declaration emit, and CommonJS emit all work.
101
+
102
+ > Reference note: `typescriptlang.org/tsconfig` has not been updated for TS7 and still documents removed options, and there is no TS 7.0 handbook release-notes page. The [GA announcement](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/) is the authoritative what's-new source.
103
+
12
104
  ## Key Patterns
13
105
 
14
106
  ### Strict Mode
@@ -0,0 +1,25 @@
1
+ # ESNext Idioms
2
+
3
+ > Modern ECMAScript runtime idioms — Temporal, iterator helpers, `using`, structuredClone.
4
+
5
+ **Type:** Stack Skill (requires `esnext` stack)
6
+ **Source:** [`stacks/esnext/skills/ct-esnext-idioms/SKILL.md`](../../stacks/esnext/skills/ct-esnext-idioms/SKILL.md)
7
+ **Detected by:** tsconfig `target`/`module`/`lib` = `ESNext`/`ES2022+`, package.json `"type":"module"`, `engines.node >= 20`, or a `.mjs` file
8
+ **File Extensions:** `.mjs`, `.cjs`
9
+
10
+ ## Overview
11
+
12
+ Guidance for preferring current ECMAScript **runtime features** over legacy patterns and hand-rolled polyfills. It is availability-aware: each newer feature notes the runtime/polyfill it needs, so the recommendation never generates code the target can't run. Types are covered by `ct-typescript-conventions`; general code structure by `ct-code-style`.
13
+
14
+ ## Topics
15
+
16
+ - **Dates → `Temporal`** — immutable, timezone-correct calendar/clock math (polyfill until baseline).
17
+ - **Resource management → `using` / `await using`** — deterministic cleanup via `Symbol.dispose`/`asyncDispose`.
18
+ - **Iterator helpers** — lazy `map`/`filter`/`take`, `Iterator.concat`, `Array.fromAsync`.
19
+ - **Clone & group** — `structuredClone`, `Object.groupBy` / `Map.groupBy`.
20
+ - **Async ergonomics** — `Promise.withResolvers`, `AbortSignal.timeout`/`any`, top-level await.
21
+ - **Immutable array ops** — `at`, `findLast`, `toSorted`/`toReversed`/`with`.
22
+ - **Errors** — `Error({ cause })`, `AggregateError`.
23
+ - **ESM hygiene** — `import.meta.dirname` over `__dirname`, dynamic `import()`.
24
+
25
+ See the [skill source](../../stacks/esnext/skills/ct-esnext-idioms/SKILL.md) for the full patterns, availability notes, and anti-patterns table.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-toolkit",
3
- "version": "0.11.0",
3
+ "version": "0.16.0",
4
4
  "description": "Reusable Claude Code configuration toolkit with stack-specific connectors",
5
5
  "type": "module",
6
6
  "bin": {
package/src/detect.ts CHANGED
@@ -12,6 +12,8 @@ interface PackageJson {
12
12
  dependencies?: Record<string, string>;
13
13
  devDependencies?: Record<string, string>;
14
14
  workspaces?: string[] | { packages?: string[] };
15
+ type?: string;
16
+ engines?: { node?: string };
15
17
  }
16
18
 
17
19
  /** A directory to inspect for stack markers, with its project-relative label */
@@ -54,6 +56,9 @@ const IGNORE_DIRS = new Set([
54
56
  "test-results",
55
57
  ]);
56
58
 
59
+ /** Node major version at/above which modern ESNext runtime features are assumed available. */
60
+ const MIN_MODERN_NODE_MAJOR = 20;
61
+
57
62
  function readPackageJson(dir: string): PackageJson | null {
58
63
  const pkgPath = join(dir, "package.json");
59
64
  if (!existsSync(pkgPath)) return null;
@@ -174,6 +179,57 @@ function findProtoFiles(ctx: DetectContext): string | null {
174
179
  return null;
175
180
  }
176
181
 
182
+ /** Read a file's text, or null if missing/unreadable (tolerates comments/trailing commas). */
183
+ function readTextFile(path: string): string | null {
184
+ try {
185
+ return readFileSync(path, "utf-8");
186
+ } catch {
187
+ return null;
188
+ }
189
+ }
190
+
191
+ /**
192
+ * First scan root whose tsconfig.json sets target/module/lib to ESNext or ES2022+.
193
+ * Uses a lenient text scan because tsconfig files routinely carry comments and
194
+ * trailing commas that would break JSON.parse.
195
+ */
196
+ function tsconfigTargetsEsnext(ctx: DetectContext): string | null {
197
+ const modern = /es(?:next|20(?:2[2-9]|[3-9]\d))/i; // ESNext or ES2022..ES2099
198
+ const keyValue = /"(?:target|module|lib)"\s*:\s*(?:"([^"]*)"|\[([^\]]*)\])/gi;
199
+ for (const { rel, dir } of ctx.scanRoots) {
200
+ const text = readTextFile(join(dir, "tsconfig.json"));
201
+ if (!text) continue;
202
+ for (const m of text.matchAll(keyValue)) {
203
+ if (modern.test(m[1] ?? m[2] ?? "")) return relPath(rel, "tsconfig.json");
204
+ }
205
+ }
206
+ return null;
207
+ }
208
+
209
+ /** Whether a package.json's engines.node floor is >= `major` (first integer in the range). */
210
+ function enginesNodeAtLeast(pkg: PackageJson, major: number): boolean {
211
+ const range = pkg.engines?.node;
212
+ if (!range) return false;
213
+ const m = range.match(/(\d+)/);
214
+ return m ? Number(m[1]) >= major : false;
215
+ }
216
+
217
+ /** First `.mjs` file in a bounded search: each scan root's top level + bin/, src/, scripts/. */
218
+ function findMjs(ctx: DetectContext): string | null {
219
+ const subs = ["", "bin", "src", "scripts"];
220
+ for (const { rel, dir } of ctx.scanRoots) {
221
+ for (const sub of subs) {
222
+ const searchDir = sub ? join(dir, sub) : dir;
223
+ if (!existsSync(searchDir)) continue;
224
+ const glob = new Bun.Glob("*.mjs"); // non-recursive: never descends into node_modules
225
+ for (const match of glob.scanSync({ cwd: searchDir, onlyFiles: true })) {
226
+ return relPath(rel, sub ? `${sub}/${match}` : match);
227
+ }
228
+ }
229
+ }
230
+ return null;
231
+ }
232
+
177
233
  const DETECTORS: StackDetector[] = [
178
234
  {
179
235
  name: "solidjs",
@@ -271,6 +327,24 @@ const DETECTORS: StackDetector[] = [
271
327
  return config ? { name: "capacitor", reason: `found ${config}` } : null;
272
328
  },
273
329
  },
330
+ {
331
+ name: "esnext",
332
+ detect: (ctx) => {
333
+ const ts = tsconfigTargetsEsnext(ctx);
334
+ if (ts) return { name: "esnext", reason: `found ESNext target in ${ts}` };
335
+ const mod = ctx.packages.find(({ pkg }) => pkg.type === "module");
336
+ if (mod) return { name: "esnext", reason: `found "type":"module" in ${pkgLabel(mod.rel)}` };
337
+ const eng = ctx.packages.find(({ pkg }) => enginesNodeAtLeast(pkg, MIN_MODERN_NODE_MAJOR));
338
+ if (eng)
339
+ return {
340
+ name: "esnext",
341
+ reason: `found engines.node >=${MIN_MODERN_NODE_MAJOR} in ${pkgLabel(eng.rel)}`,
342
+ };
343
+ const mjs = findMjs(ctx);
344
+ if (mjs) return { name: "esnext", reason: `found ${mjs}` };
345
+ return null;
346
+ },
347
+ },
274
348
  ];
275
349
 
276
350
  /** Scan a project directory (root + subdirs + workspace packages) and detect which stacks are present */
package/src/generator.ts CHANGED
@@ -42,7 +42,7 @@ async function resolveConfig(config: ClaudeToolkitConfig): Promise<ResolvedConfi
42
42
  "ct-testing-patterns",
43
43
  "ct-typescript-conventions",
44
44
  "ct-verification-before-completion",
45
- ...(config.excludeSkills ? [] : []),
45
+ "ct-code-style",
46
46
  ].filter((s) => !config.excludeSkills?.includes(s));
47
47
 
48
48
  return { config, skills, directoryMappings: allMappings, hooks, stacks };
@@ -373,8 +373,11 @@ async function generateSkillsReadme(claudeDir: string, resolved: ResolvedConfig)
373
373
  content +=
374
374
  "| ct-systematic-debugging | Four-phase debugging methodology, root cause analysis |\n";
375
375
  content += "| ct-testing-patterns | Test-driven development workflow and patterns |\n";
376
- content += "| ct-typescript-conventions | TypeScript strict mode and conventions |\n";
376
+ content +=
377
+ "| ct-typescript-conventions | TypeScript strict mode, conventions, and TS7 compiler selection |\n";
377
378
  content += "| ct-verification-before-completion | Evidence-based completion claims protocol |\n";
379
+ content +=
380
+ "| ct-code-style | Code structure & style: guard clauses, lookup tables, no magic values |\n";
378
381
  content += "\n## Stack Skills\n\n";
379
382
  content += "| Skill | Stack | Description |\n";
380
383
  content += "|-------|-------|-------------|\n";
package/src/types.ts CHANGED
@@ -10,6 +10,7 @@ export type StackName =
10
10
  | "playwright"
11
11
  | "storybook"
12
12
  | "capacitor"
13
+ | "esnext"
13
14
  | (string & {});
14
15
 
15
16
  /** Hook configuration for post-tool-use automation */
@@ -0,0 +1,130 @@
1
+ ---
2
+ name: ct-esnext-idioms
3
+ description: Modern ECMAScript runtime idioms — Temporal, iterator helpers, using, structuredClone
4
+ ---
5
+
6
+ # Modern ESNext Idioms
7
+
8
+ Prefer current ECMAScript runtime features over legacy patterns and hand-rolled polyfills. Types live in `ct-typescript-conventions`; code structure lives in `ct-code-style`; this skill is about *runtime features*.
9
+
10
+ > Some features below are newer than the Node/browser baseline. Each is tagged with what it needs. When a target can't guarantee it, use the noted polyfill/flag rather than dropping back to the legacy pattern.
11
+
12
+ ## Dates & time → `Temporal`
13
+
14
+ `Date` is mutable, timezone-naive, and error-prone for math. Use `Temporal` for calendar/clock work.
15
+
16
+ ```ts
17
+ const today = Temporal.Now.plainDateISO(); // no time, no tz confusion
18
+ const due = today.add({ days: 30 }); // immutable — returns a new value
19
+ const start = Temporal.ZonedDateTime.from("2026-07-04T09:00[Europe/Paris]");
20
+ const mins = start.until(Temporal.Now.zonedDateTimeISO()).total("minutes");
21
+ ```
22
+
23
+ _Availability: Stage 3 — not yet baseline in Node. Add `temporal-polyfill` (or `@js-temporal/polyfill`) until it ships._ Avoid `Date` mutation, manual `getTime()` ms math, and moment.js.
24
+
25
+ ## Deterministic cleanup → `using` / `await using`
26
+
27
+ `Symbol.dispose` / `Symbol.asyncDispose` free resources when scope exits — no `try/finally` ladder.
28
+
29
+ ```ts
30
+ const openFile = (path: string) => {
31
+ const handle = fs.openSync(path, "r");
32
+ return { handle, [Symbol.dispose]: () => fs.closeSync(handle) };
33
+ };
34
+
35
+ const readHead = (path: string) => {
36
+ using file = openFile(path); // closed automatically at end of block
37
+ return fs.readSync(file.handle, Buffer.alloc(16), 0, 16, 0);
38
+ }; // ← dispose runs here, even on throw
39
+ ```
40
+
41
+ `await using` awaits `Symbol.asyncDispose` (db connections, locks, spans). _Availability: TS 5.2+ (downlevels) or Node 24+._
42
+
43
+ ## Iterator helpers → lazy, no intermediate arrays
44
+
45
+ ```ts
46
+ const firstThree = users.values()
47
+ .filter((u) => u.active)
48
+ .map((u) => u.name)
49
+ .take(3)
50
+ .toArray(); // only 3 names ever materialised
51
+
52
+ const merged = Iterator.concat(pageA.values(), pageB.values()); // Iterator Sequencing — Stage 4 (2025); polyfill on older runtimes
53
+ const rows = await Array.fromAsync(streamRows()); // async iterable → array
54
+ ```
55
+
56
+ _Availability: standard iterator helpers (`.filter`/`.map`/`.take`) and `Array.fromAsync` are Node 22+ / modern browsers; `Iterator.concat` is newer (Iterator Sequencing, 2025) — polyfill until it lands._ Replaces `[...a, ...b].filter().map().slice()`, which allocates at every step.
57
+
58
+ ## Clone & group
59
+
60
+ ```ts
61
+ const copy = structuredClone(state); // deep clone — Map/Set/Date/typed arrays
62
+ const byRole = Object.groupBy(users, (u) => u.role); // { admin: [...], guest: [...] }
63
+ const byId = Map.groupBy(users, (u) => u.id); // Map keys (any type)
64
+ ```
65
+
66
+ Avoid `JSON.parse(JSON.stringify(x))` (drops `undefined`, turns `Date` into a string, throws on cycles) and manual `reduce` grouping.
67
+
68
+ ## Promise & async ergonomics
69
+
70
+ ```ts
71
+ const { promise, resolve, reject } = Promise.withResolvers<Data>(); // no deferred-hack closure
72
+ const res = await fetch(url, { signal: AbortSignal.timeout(5_000) }); // auto-abort
73
+ const signal = AbortSignal.any([userAbort, AbortSignal.timeout(30_000)]);
74
+ ```
75
+
76
+ Top-level `await` is available in ESM modules — no IIFE wrapper.
77
+
78
+ ## Immutable array ops
79
+
80
+ ```ts
81
+ const last = items.at(-1); // negative index
82
+ const newest = orders.findLast((o) => o.settled);
83
+ const sorted = scores.toSorted((a, b) => b - a); // non-mutating; also toReversed/toSpliced/with
84
+ const patched = row.with(2, "x"); // copy with index 2 replaced
85
+ ```
86
+
87
+ ## Errors with cause
88
+
89
+ ```ts
90
+ try {
91
+ await save();
92
+ } catch (err) {
93
+ throw new Error("Checkout failed", { cause: err }); // preserve the chain
94
+ }
95
+ throw new AggregateError(errors, "Some uploads failed"); // aggregate independent failures
96
+ ```
97
+
98
+ ## ESM hygiene
99
+
100
+ ```ts
101
+ const here = import.meta.dirname; // replaces __dirname (Node 20.11+/21.2+)
102
+ const url = import.meta.url;
103
+ const mod = await import("./plugin.js"); // dynamic import, not require()
104
+ ```
105
+
106
+ ## Performance
107
+
108
+ > _Verified against ECMAScript 2024 / Node 22 (2026-07)._
109
+
110
+ - **Iterator helpers are lazy** — `take(3)` stops the pipeline after 3 items; the array form processes everything first. Use them for early-exit and large/infinite sequences.
111
+ - **`structuredClone` is a native deep copy** — faster and more correct than `JSON.parse(JSON.stringify())`, with no string round-trip.
112
+ - **`AbortSignal.timeout`** removes `setTimeout` bookkeeping and leaks; the runtime clears the timer when the signal settles.
113
+
114
+ ## Anti-Patterns
115
+
116
+ | Legacy | Modern |
117
+ | --- | --- |
118
+ | `new Date()` math / moment.js | `Temporal` (via polyfill) |
119
+ | `try { } finally { close() }` | `using` / `await using` |
120
+ | `[...a, ...b].filter().map().slice()` | iterator helpers + `Iterator.concat` |
121
+ | `JSON.parse(JSON.stringify(x))` | `structuredClone(x)` |
122
+ | manual `reduce` grouping | `Object.groupBy` / `Map.groupBy` |
123
+ | deferred `let resolve; new Promise(...)` | `Promise.withResolvers()` |
124
+ | `arr[arr.length - 1]` | `arr.at(-1)` |
125
+ | `__dirname` / `require()` | `import.meta.dirname` / `import()` |
126
+
127
+ ## See Also
128
+
129
+ - `ct-typescript-conventions` — typing these values (`satisfies`, discriminated unions for async state).
130
+ - `ct-code-style` — structure around these features (guard clauses, no magic numbers, `??`).
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "ct-esnext-idioms",
3
+ "description": "Modern ECMAScript runtime idioms — Temporal, iterator helpers, using, structuredClone",
4
+ "defaultMappings": {},
5
+ "fileExtensions": ["mjs", "cjs"],
6
+ "skillRules": {
7
+ "ct-esnext-idioms": {
8
+ "description": "Modern ECMAScript runtime idioms and features",
9
+ "priority": 5,
10
+ "triggers": {
11
+ "keywords": [
12
+ "Temporal",
13
+ "structuredClone",
14
+ "iterator",
15
+ "using",
16
+ "dispose",
17
+ "groupBy",
18
+ "withResolvers",
19
+ "AbortSignal",
20
+ "fromAsync",
21
+ "esnext",
22
+ "moment",
23
+ "date"
24
+ ],
25
+ "keywordPatterns": ["\\bTemporal\\b", "\\busing\\b", "\\bstructuredClone\\b"],
26
+ "contentPatterns": [
27
+ "new Date\\(",
28
+ "JSON\\.parse\\(JSON\\.stringify",
29
+ "__dirname",
30
+ "\\.groupBy\\("
31
+ ],
32
+ "intentPatterns": ["(?:format|parse|manipulate).*date", "(?:clone|copy).*(?:object|deep)"]
33
+ },
34
+ "relatedSkills": ["ct-typescript-conventions", "ct-code-style"]
35
+ }
36
+ }
37
+ }
@@ -29,7 +29,7 @@ const [data] = createResource(userId, fetchUser); // async: data(), data.loading
29
29
 
30
30
  ## Props
31
31
 
32
- **CRITICAL: Never destructure props.** Destructuring breaks reactivity.
32
+ **CRITICAL: Never destructure props.** Destructuring breaks reactivity. (This overrides the general "use destructuring" guidance in `ct-code-style` — destructure plain data, never reactive `props`.)
33
33
 
34
34
  ```tsx
35
35
  // WRONG -- reads once, never updates
@@ -16,7 +16,14 @@
16
16
  "linter": {
17
17
  "enabled": true,
18
18
  "rules": {
19
- "recommended": true
19
+ "recommended": true,
20
+ "style": {
21
+ "noUselessElse": "error",
22
+ "noNestedTernary": "error"
23
+ },
24
+ "complexity": {
25
+ "useArrowFunction": "error"
26
+ }
20
27
  }
21
28
  },
22
29
  "javascript": {
@@ -43,14 +43,37 @@
43
43
  }
44
44
  },
45
45
  "ct-typescript-conventions": {
46
- "description": "TypeScript strict mode and conventions",
46
+ "description": "TypeScript strict mode, conventions, and TS7 compiler selection",
47
47
  "priority": 5,
48
48
  "triggers": {
49
- "keywords": ["typescript", "type", "interface", "generic", "strict mode"],
50
- "keywordPatterns": ["\\btype\\b", "\\binterface\\b", "\\bgeneric\\b"],
51
- "pathPatterns": ["**/*.d.ts", "**/types/**", "**/types.ts"],
52
- "intentPatterns": ["(?:define|create).*(?:type|interface)", "(?:type).*(?:error|issue)"],
53
- "contentPatterns": ["interface ", "type ", ": unknown", "as const"]
49
+ "keywords": [
50
+ "typescript",
51
+ "type",
52
+ "interface",
53
+ "generic",
54
+ "strict mode",
55
+ "tsc",
56
+ "tsgo",
57
+ "tsconfig",
58
+ "typecheck",
59
+ "compiler"
60
+ ],
61
+ "keywordPatterns": [
62
+ "\\btype\\b",
63
+ "\\binterface\\b",
64
+ "\\bgeneric\\b",
65
+ "\\btsc\\b",
66
+ "\\btsconfig\\b",
67
+ "\\bts\\s?7\\b"
68
+ ],
69
+ "pathPatterns": ["**/*.d.ts", "**/types/**", "**/types.ts", "**/tsconfig*.json"],
70
+ "intentPatterns": [
71
+ "(?:define|create).*(?:type|interface)",
72
+ "(?:type).*(?:error|issue)",
73
+ "(?:run|fix).*type.?check",
74
+ "(?:upgrade|migrate|update).*typescript"
75
+ ],
76
+ "contentPatterns": ["interface ", "type ", ": unknown", "as const", "compilerOptions"]
54
77
  },
55
78
  "excludePatterns": ["issue type", "type of bug"]
56
79
  },
@@ -65,6 +88,29 @@
65
88
  ],
66
89
  "contextPatterns": ["before claiming", "make sure", "double check"]
67
90
  }
91
+ },
92
+ "ct-code-style": {
93
+ "description": "Code structure & style — guard clauses, lookup tables, destructuring, no magic values",
94
+ "priority": 6,
95
+ "triggers": {
96
+ "keywords": [
97
+ "refactor",
98
+ "clean up",
99
+ "guard clause",
100
+ "early return",
101
+ "destructure",
102
+ "ternary",
103
+ "magic number",
104
+ "magic string",
105
+ "lookup table",
106
+ "arrow function"
107
+ ],
108
+ "contentPatterns": ["else if", "else\\s*\\{", "\\bclass\\b", "this\\.", "\\bfunction\\b"],
109
+ "intentPatterns": [
110
+ "(?:refactor|clean up|simplify|tidy).*(?:code|function|logic)",
111
+ "(?:extract|avoid).*(?:magic|constant|conditional)"
112
+ ]
113
+ }
68
114
  }
69
115
  }
70
116
  }