@grimoire-cc/cli 0.4.1 → 0.6.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.
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "ts-pack",
3
+ "version": "1.0.0",
4
+ "agents": [],
5
+ "skills": [
6
+ {
7
+ "name": "grimoire:modern-typescript",
8
+ "path": "skills/grimoire:modern-typescript",
9
+ "description": "Modern TypeScript best practices, patterns, and type system mastery for TS 5.7+. Use when writing TypeScript, reviewing TS code, designing types, configuring tsconfig, or asking about TypeScript patterns, generics, type safety, strict mode, idiomatic TypeScript, discriminated unions, branded types, or modern TS features.",
10
+ "triggers": {
11
+ "keywords": ["typescript", "tsconfig", "satisfies", "narrowing", "readonly"],
12
+ "file_extensions": [".ts", ".tsx", ".mts", ".cts"],
13
+ "patterns": [
14
+ "type.*safe",
15
+ "strict.*mode",
16
+ "discriminated.*union",
17
+ "branded.*type",
18
+ "modern.*typescript",
19
+ "typescript.*generic",
20
+ "typescript.*pattern",
21
+ "typescript.*best",
22
+ "type.*error",
23
+ "type.*guard",
24
+ "configure.*tsconfig",
25
+ "write.*typescript",
26
+ "review.*typescript",
27
+ "refactor.*type"
28
+ ],
29
+ "file_paths": ["**/tsconfig*", "**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"]
30
+ }
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,336 @@
1
+ ---
2
+ name: grimoire:modern-typescript
3
+ description: "Modern TypeScript best practices, patterns, and type system mastery for TS 5.7+. Use when writing TypeScript, reviewing TS code, designing types, configuring tsconfig, or asking about TypeScript patterns, generics, type safety, strict mode, idiomatic TypeScript, discriminated unions, branded types, or modern TS features."
4
+ ---
5
+
6
+ # Modern TypeScript
7
+
8
+ Guidance for writing modern, idiomatic TypeScript (5.7+) following community best practices and the latest language features. Runtime-agnostic.
9
+
10
+ ## Core Principles
11
+
12
+ ### 1. Maximum Type Safety
13
+
14
+ - Enable `strict: true` — always, no exceptions
15
+ - Prefer `unknown` over `any` — force explicit narrowing
16
+ - Use `satisfies` over type assertions — preserves inference
17
+ - Annotate return types for public APIs — prevents accidental changes
18
+ - Let TypeScript infer where it can — avoid redundant annotations on locals
19
+
20
+ ### 2. Leverage the Type System
21
+
22
+ - Model your domain with discriminated unions, not class hierarchies
23
+ - Use branded types for nominal typing (IDs, currencies, units)
24
+ - Prefer `readonly` by default — mutate only when necessary
25
+ - Use `as const` for literal types and exhaustive checks
26
+ - Template literal types for string-based APIs
27
+
28
+ ### 3. Modern Language Features
29
+
30
+ - `using` / `await using` for resource management (TS 5.2+)
31
+ - `const` type parameters for literal inference (TS 5.0+)
32
+ - `satisfies` operator for constraint checking with inference (TS 5.0+)
33
+ - `NoInfer<T>` to control inference sites (TS 5.4+)
34
+ - Inferred type predicates for cleaner narrowing (TS 5.5+)
35
+
36
+ ### 4. Code Style
37
+
38
+ - Functions over classes when no state is needed
39
+ - Composition over inheritance
40
+ - Small, focused modules with explicit exports
41
+ - `import type` for type-only imports
42
+ - Named exports over default exports
43
+
44
+ ## Strict Configuration
45
+
46
+ Minimum recommended `tsconfig.json`:
47
+
48
+ ```json
49
+ {
50
+ "compilerOptions": {
51
+ "strict": true,
52
+ "target": "es2024",
53
+ "module": "nodenext",
54
+ "moduleResolution": "nodenext",
55
+ "isolatedModules": true,
56
+ "skipLibCheck": true,
57
+ "noUncheckedIndexedAccess": true,
58
+ "exactOptionalPropertyTypes": true,
59
+ "noUncheckedSideEffectImports": true,
60
+ "verbatimModuleSyntax": true
61
+ }
62
+ }
63
+ ```
64
+
65
+ Key flags explained:
66
+
67
+ - `noUncheckedIndexedAccess` — index signatures return `T | undefined`
68
+ - `exactOptionalPropertyTypes` — distinguishes `undefined` from missing
69
+ - `verbatimModuleSyntax` — enforces explicit `import type` syntax
70
+ - `noUncheckedSideEffectImports` — verifies side-effect imports resolve (TS 5.6+)
71
+
72
+ ## Type System Essentials
73
+
74
+ ### Prefer Narrow Types
75
+
76
+ ```typescript
77
+ // Bad — too wide
78
+ function processStatus(status: string): void { /* ... */ }
79
+
80
+ // Good — constrained
81
+ type Status = "pending" | "active" | "archived";
82
+ function processStatus(status: Status): void { /* ... */ }
83
+ ```
84
+
85
+ ### Discriminated Unions Over Conditionals
86
+
87
+ ```typescript
88
+ // Bad — boolean flags with optional fields
89
+ interface ApiResponse {
90
+ success: boolean;
91
+ data?: User;
92
+ error?: string;
93
+ }
94
+
95
+ // Good — each state is distinct and self-describing
96
+ type ApiResponse =
97
+ | { status: "success"; data: User }
98
+ | { status: "error"; error: string }
99
+ | { status: "loading" };
100
+ ```
101
+
102
+ ### Use `satisfies` for Validated Inference
103
+
104
+ ```typescript
105
+ // Bad — loses literal types
106
+ const config: Record<string, string> = {
107
+ host: "localhost",
108
+ port: "3000",
109
+ };
110
+ // config.host is string
111
+
112
+ // Good — validates shape, keeps literals
113
+ const config = {
114
+ host: "localhost",
115
+ port: "3000",
116
+ } satisfies Record<string, string>;
117
+ // config.host is "localhost"
118
+ ```
119
+
120
+ ### Branded Types for Nominal Safety
121
+
122
+ ```typescript
123
+ type UserId = string & { readonly __brand: unique symbol };
124
+ type OrderId = string & { readonly __brand: unique symbol };
125
+
126
+ function createUserId(id: string): UserId {
127
+ return id as UserId;
128
+ }
129
+
130
+ function getUser(id: UserId): User { /* ... */ }
131
+
132
+ const userId = createUserId("u-123");
133
+ const orderId = createOrderId("o-456");
134
+ getUser(orderId); // Error! OrderId is not UserId
135
+ ```
136
+
137
+ ### `readonly` by Default
138
+
139
+ ```typescript
140
+ // Prefer readonly for function parameters
141
+ function processItems(items: readonly Item[]): Result {
142
+ // items.push(...) would be an error
143
+ return items.map(transform);
144
+ }
145
+
146
+ // Use Readonly<T> for objects
147
+ function updateConfig(
148
+ current: Readonly<Config>,
149
+ patch: Partial<Config>,
150
+ ): Config {
151
+ return { ...current, ...patch };
152
+ }
153
+ ```
154
+
155
+ ### Const Type Parameters (TS 5.0+)
156
+
157
+ ```typescript
158
+ // Without const — infers string[]
159
+ function createRoute<T extends readonly string[]>(parts: T) { /* ... */ }
160
+ createRoute(["users", "profile"]); // T is string[]
161
+
162
+ // With const — infers literal tuple
163
+ function createRoute<const T extends readonly string[]>(parts: T) { /* ... */ }
164
+ createRoute(["users", "profile"]); // T is readonly ["users", "profile"]
165
+ ```
166
+
167
+ ### Resource Management with `using` (TS 5.2+)
168
+
169
+ ```typescript
170
+ function processFile(path: string): string {
171
+ using handle = openFile(path);
172
+ return handle.readAll();
173
+ // handle[Symbol.dispose]() called automatically
174
+ }
175
+
176
+ async function withConnection(): Promise<QueryResult> {
177
+ await using conn = await pool.getConnection();
178
+ return conn.query("SELECT ...");
179
+ // conn[Symbol.asyncDispose]() called automatically
180
+ }
181
+ ```
182
+
183
+ ### NoInfer for Controlled Inference (TS 5.4+)
184
+
185
+ ```typescript
186
+ // Without NoInfer — initial widens the union
187
+ function createFSM<S extends string>(
188
+ initial: S,
189
+ states: S[],
190
+ ): void { /* ... */ }
191
+ createFSM("typo", ["idle", "running"]); // No error — S becomes "typo" | "idle" | "running"
192
+
193
+ // With NoInfer — only states drives inference
194
+ function createFSM<S extends string>(
195
+ initial: NoInfer<S>,
196
+ states: S[],
197
+ ): void { /* ... */ }
198
+ createFSM("typo", ["idle", "running"]); // Error: "typo" not assignable
199
+ ```
200
+
201
+ ## Error Handling
202
+
203
+ ### Use Result Types Over Exceptions
204
+
205
+ ```typescript
206
+ type Result<T, E = Error> =
207
+ | { ok: true; value: T }
208
+ | { ok: false; error: E };
209
+
210
+ function parseConfig(raw: string): Result<Config, ParseError> {
211
+ try {
212
+ const data = JSON.parse(raw);
213
+ return { ok: true, value: validate(data) };
214
+ } catch (e) {
215
+ return { ok: false, error: new ParseError(e) };
216
+ }
217
+ }
218
+
219
+ // Caller must handle both cases
220
+ const result = parseConfig(input);
221
+ if (result.ok) {
222
+ useConfig(result.value);
223
+ } else {
224
+ log(result.error);
225
+ }
226
+ ```
227
+
228
+ ### Type-Safe Error Narrowing
229
+
230
+ ```typescript
231
+ // Always narrow unknown in catch
232
+ try {
233
+ riskyOperation();
234
+ } catch (error: unknown) {
235
+ if (error instanceof NetworkError) {
236
+ retry(error.url);
237
+ } else if (error instanceof ValidationError) {
238
+ showErrors(error.fields);
239
+ } else {
240
+ throw error;
241
+ }
242
+ }
243
+ ```
244
+
245
+ ## Module Patterns
246
+
247
+ ### Explicit Named Exports
248
+
249
+ ```typescript
250
+ // Prefer named exports
251
+ export function createUser(data: UserInput): User { /* ... */ }
252
+ export type { User, UserInput };
253
+
254
+ // Avoid default exports — harder to rename, worse tree-shaking
255
+ ```
256
+
257
+ ### Barrel Files — Use Sparingly
258
+
259
+ ```typescript
260
+ // index.ts — only for public API surface
261
+ export { createUser, updateUser } from "./user.js";
262
+ export type { User, UserInput } from "./types.js";
263
+
264
+ // Don't re-export everything — it defeats tree-shaking
265
+ ```
266
+
267
+ ### Type-Only Import Enforcement
268
+
269
+ ```typescript
270
+ // With verbatimModuleSyntax, this is enforced:
271
+ import type { User } from "./types.js"; // Type-only — erased
272
+ import { createUser } from "./user.js"; // Value — kept
273
+ import { type Config, loadConfig } from "./config.js"; // Mixed
274
+ ```
275
+
276
+ ## Common Anti-Patterns
277
+
278
+ | Anti-Pattern | Better Alternative |
279
+ |---|---|
280
+ | `any` | `unknown` with narrowing |
281
+ | Type assertions (`as T`) | `satisfies`, type guards |
282
+ | `enum` | Union types or `as const` objects |
283
+ | Class hierarchies for data | Discriminated unions |
284
+ | `!` non-null assertion | Proper null checks or `?.` |
285
+ | `Function` type | Specific signature `(arg: T) => R` |
286
+ | `Object` / `{}` type | `Record<string, unknown>` |
287
+ | Nested ternaries in types | Named helper types |
288
+ | `@ts-ignore` | `@ts-expect-error` (fails when fixed) |
289
+ | `interface extends` chains | Intersection types or composition |
290
+
291
+ ## When to Annotate vs. Infer
292
+
293
+ **Annotate:**
294
+
295
+ - Function return types (public API)
296
+ - Complex object parameters
297
+ - Generic constraints
298
+ - Exported constants with specific types
299
+
300
+ **Let TypeScript infer:**
301
+
302
+ - Local variables
303
+ - Array method chains (`.map`, `.filter`)
304
+ - Simple function returns (private/internal)
305
+ - `const` declarations with literal values
306
+
307
+ ## Exhaustive Checking
308
+
309
+ ```typescript
310
+ function assertNever(value: never): never {
311
+ throw new Error(`Unexpected value: ${value}`);
312
+ }
313
+
314
+ function handleStatus(status: Status): string {
315
+ switch (status) {
316
+ case "pending": return "Waiting...";
317
+ case "active": return "Running";
318
+ case "archived": return "Done";
319
+ default: return assertNever(status); // Compile error if a case is missed
320
+ }
321
+ }
322
+ ```
323
+
324
+ ## Deep Reference
325
+
326
+ For detailed guides on specific topics:
327
+
328
+ - **[Type System Patterns](reference/type-system.md)** — Generics, conditional types, mapped types, template literals, type guards
329
+ - **[Code Patterns & Idioms](reference/patterns-and-idioms.md)** — Functional patterns, builder pattern, immutable state, async patterns
330
+ - **[Modern Features Guide](reference/modern-features.md)** — Comprehensive TS 5.0-5.9 feature reference
331
+
332
+ ## Limitations
333
+
334
+ - This skill provides guidance, not enforcement — use ESLint + typescript-eslint for automated checks
335
+ - Patterns are runtime-agnostic — some may need adaptation for specific frameworks
336
+ - TypeScript evolves rapidly — check the [official release notes](https://devblogs.microsoft.com/typescript/) for the latest