rtfct 0.1.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.
Files changed (39) hide show
  1. package/.project/adrs/001-use-bun-typescript.md +52 -0
  2. package/.project/guardrails.md +65 -0
  3. package/.project/kanban/backlog.md +7 -0
  4. package/.project/kanban/done.md +240 -0
  5. package/.project/kanban/in-progress.md +11 -0
  6. package/.project/kickstart.md +63 -0
  7. package/.project/protocol.md +134 -0
  8. package/.project/specs/requirements.md +152 -0
  9. package/.project/testing/strategy.md +123 -0
  10. package/.project/theology.md +125 -0
  11. package/CLAUDE.md +119 -0
  12. package/README.md +143 -0
  13. package/package.json +31 -0
  14. package/src/args.ts +104 -0
  15. package/src/commands/add.ts +78 -0
  16. package/src/commands/init.ts +128 -0
  17. package/src/commands/praise.ts +19 -0
  18. package/src/commands/regenerate.ts +122 -0
  19. package/src/commands/status.ts +163 -0
  20. package/src/help.ts +52 -0
  21. package/src/index.ts +102 -0
  22. package/src/kanban.ts +83 -0
  23. package/src/manifest.ts +67 -0
  24. package/src/presets/base.ts +195 -0
  25. package/src/presets/elixir.ts +118 -0
  26. package/src/presets/github.ts +194 -0
  27. package/src/presets/index.ts +154 -0
  28. package/src/presets/typescript.ts +589 -0
  29. package/src/presets/zig.ts +494 -0
  30. package/tests/integration/add.test.ts +104 -0
  31. package/tests/integration/init.test.ts +197 -0
  32. package/tests/integration/praise.test.ts +36 -0
  33. package/tests/integration/regenerate.test.ts +154 -0
  34. package/tests/integration/status.test.ts +165 -0
  35. package/tests/unit/args.test.ts +144 -0
  36. package/tests/unit/kanban.test.ts +162 -0
  37. package/tests/unit/manifest.test.ts +155 -0
  38. package/tests/unit/presets.test.ts +295 -0
  39. package/tsconfig.json +19 -0
@@ -0,0 +1,589 @@
1
+ /**
2
+ * The TypeScript Codex — Sacred Patterns for TypeScript Development
3
+ */
4
+
5
+ import type { Preset } from "./index";
6
+
7
+ const TESTING_STRATEGY_MD = `# TypeScript Testing Strategy
8
+
9
+ *The Rites of Verification for TypeScript*
10
+
11
+ ## The Sacred Command
12
+
13
+ \`\`\`bash
14
+ npx vitest # Watch mode — the Machine Spirit watches
15
+ npx vitest run # Single run — for CI/CD
16
+ npx vitest --ui # Visual interface — for deep investigation
17
+ \`\`\`
18
+
19
+ ## Why Vitest
20
+
21
+ Vitest is the sacred choice for TypeScript testing:
22
+ - Native ESM support
23
+ - TypeScript out of the box
24
+ - Jest-compatible API
25
+ - Blazingly fast with Vite
26
+
27
+ ## Test Organization
28
+
29
+ \`\`\`
30
+ tests/
31
+ ├── unit/ # Pure function tests
32
+ │ ├── utils.test.ts
33
+ │ └── validators.test.ts
34
+ ├── integration/ # Module interaction tests
35
+ │ ├── api.test.ts
36
+ │ └── database.test.ts
37
+ ├── e2e/ # End-to-end tests (if applicable)
38
+ └── fixtures/ # Shared test data
39
+ └── users.json
40
+ \`\`\`
41
+
42
+ ## The Sacred Pattern
43
+
44
+ \`\`\`typescript
45
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
46
+ import { UserService } from '../src/services/user';
47
+ import { Database } from '../src/database';
48
+
49
+ // Mock external dependencies
50
+ vi.mock('../src/database');
51
+
52
+ describe('UserService', () => {
53
+ let service: UserService;
54
+
55
+ beforeEach(() => {
56
+ vi.clearAllMocks();
57
+ service = new UserService();
58
+ });
59
+
60
+ describe('getUser', () => {
61
+ it('returns user when found', async () => {
62
+ const mockUser = { id: '1', name: 'Tech-Priest' };
63
+ vi.mocked(Database.findUser).mockResolvedValue(mockUser);
64
+
65
+ const result = await service.getUser('1');
66
+
67
+ expect(result).toEqual(mockUser);
68
+ expect(Database.findUser).toHaveBeenCalledWith('1');
69
+ });
70
+
71
+ it('throws when user not found', async () => {
72
+ vi.mocked(Database.findUser).mockResolvedValue(null);
73
+
74
+ await expect(service.getUser('999')).rejects.toThrow('User not found');
75
+ });
76
+ });
77
+ });
78
+ \`\`\`
79
+
80
+ ## Testing Async Code
81
+
82
+ \`\`\`typescript
83
+ import { describe, it, expect, vi } from 'vitest';
84
+
85
+ describe('async operations', () => {
86
+ it('handles promises correctly', async () => {
87
+ const result = await fetchData();
88
+ expect(result).toBeDefined();
89
+ });
90
+
91
+ it('handles promise rejection', async () => {
92
+ await expect(failingOperation()).rejects.toThrow('Expected error');
93
+ });
94
+
95
+ it('uses fake timers for delays', async () => {
96
+ vi.useFakeTimers();
97
+
98
+ const promise = delayedOperation(1000);
99
+ vi.advanceTimersByTime(1000);
100
+
101
+ await expect(promise).resolves.toBe('done');
102
+
103
+ vi.useRealTimers();
104
+ });
105
+ });
106
+ \`\`\`
107
+
108
+ ## Type Testing
109
+
110
+ \`\`\`typescript
111
+ import { describe, it, expectTypeOf } from 'vitest';
112
+ import { processData } from '../src/utils';
113
+
114
+ describe('type safety', () => {
115
+ it('returns correct type', () => {
116
+ const result = processData({ value: 42 });
117
+ expectTypeOf(result).toEqualTypeOf<{ processed: number }>();
118
+ });
119
+
120
+ it('accepts correct input type', () => {
121
+ expectTypeOf(processData).parameter(0).toMatchTypeOf<{ value: number }>();
122
+ });
123
+ });
124
+ \`\`\`
125
+
126
+ ## Vitest Configuration
127
+
128
+ \`\`\`typescript
129
+ // vitest.config.ts
130
+ import { defineConfig } from 'vitest/config';
131
+
132
+ export default defineConfig({
133
+ test: {
134
+ globals: true,
135
+ environment: 'node',
136
+ include: ['tests/**/*.test.ts'],
137
+ coverage: {
138
+ provider: 'v8',
139
+ reporter: ['text', 'json', 'html'],
140
+ exclude: ['node_modules/', 'tests/'],
141
+ },
142
+ },
143
+ });
144
+ \`\`\`
145
+
146
+ ## Coverage Doctrine
147
+
148
+ - All exported functions must have tests
149
+ - Edge cases (null, undefined, empty, boundary values) tested explicitly
150
+ - Error paths tested — verify errors are thrown correctly
151
+ - Mocks used only for external dependencies, not for internal modules
152
+
153
+ ---
154
+
155
+ *The tests do not lie. Praise the Machine Spirit.*
156
+ `;
157
+
158
+ const GUARDRAILS_MD = `# TypeScript Guardrails — Forbidden Heresies
159
+
160
+ *TypeScript-specific patterns to avoid*
161
+
162
+ ## Type Heresies
163
+
164
+ ### 1. The \`any\` Heresy
165
+ Every \`any\` is a failure of imagination. Use \`unknown\` and narrow:
166
+
167
+ \`\`\`typescript
168
+ // HERESY
169
+ function process(data: any) {
170
+ return data.value; // No safety!
171
+ }
172
+
173
+ // SACRED
174
+ function process(data: unknown) {
175
+ if (isValidData(data)) {
176
+ return data.value; // Type-safe!
177
+ }
178
+ throw new Error('Invalid data');
179
+ }
180
+
181
+ function isValidData(data: unknown): data is { value: string } {
182
+ return typeof data === 'object' && data !== null && 'value' in data;
183
+ }
184
+ \`\`\`
185
+
186
+ ### 2. Type Assertion Abuse
187
+ \`as Type\` bypasses the compiler. Prefer type guards:
188
+
189
+ \`\`\`typescript
190
+ // HERESY
191
+ const user = response.data as User; // Hope it's right!
192
+
193
+ // SACRED
194
+ function isUser(data: unknown): data is User {
195
+ return (
196
+ typeof data === 'object' &&
197
+ data !== null &&
198
+ 'id' in data &&
199
+ 'name' in data
200
+ );
201
+ }
202
+
203
+ if (isUser(response.data)) {
204
+ const user = response.data; // Compiler verified!
205
+ }
206
+ \`\`\`
207
+
208
+ ### 3. Non-null Assertion
209
+ \`foo!\` is a lie waiting to happen:
210
+
211
+ \`\`\`typescript
212
+ // HERESY
213
+ const name = user.profile!.name!; // Runtime error waiting
214
+
215
+ // SACRED
216
+ const name = user.profile?.name ?? 'Unknown';
217
+
218
+ // Or with explicit check
219
+ if (user.profile?.name) {
220
+ const name = user.profile.name; // Safe!
221
+ }
222
+ \`\`\`
223
+
224
+ ### 4. Implicit Any in Callbacks
225
+ Always type your parameters:
226
+
227
+ \`\`\`typescript
228
+ // HERESY
229
+ items.map(item => item.value); // item is implicitly any
230
+
231
+ // SACRED
232
+ items.map((item: Item) => item.value);
233
+ // Or better, ensure items is properly typed: Item[]
234
+ \`\`\`
235
+
236
+ ## Async Heresies
237
+
238
+ ### 1. Floating Promises
239
+ Every promise must be handled:
240
+
241
+ \`\`\`typescript
242
+ // HERESY
243
+ saveData(data); // Promise ignored!
244
+
245
+ // SACRED
246
+ await saveData(data);
247
+ // Or if you truly don't care about the result:
248
+ void saveData(data); // Explicit intention
249
+ \`\`\`
250
+
251
+ ### 2. Missing Error Handling
252
+
253
+ \`\`\`typescript
254
+ // HERESY
255
+ const data = await fetchData(); // What if it fails?
256
+
257
+ // SACRED
258
+ try {
259
+ const data = await fetchData();
260
+ } catch (error) {
261
+ if (error instanceof NetworkError) {
262
+ // Handle network issues
263
+ }
264
+ throw error; // Re-throw if unhandled
265
+ }
266
+ \`\`\`
267
+
268
+ ### 3. Async in Constructors
269
+
270
+ \`\`\`typescript
271
+ // HERESY
272
+ class Service {
273
+ constructor() {
274
+ this.init(); // Async operation in constructor!
275
+ }
276
+ private async init() { ... }
277
+ }
278
+
279
+ // SACRED — Use factory pattern
280
+ class Service {
281
+ private constructor() {}
282
+
283
+ static async create(): Promise<Service> {
284
+ const service = new Service();
285
+ await service.init();
286
+ return service;
287
+ }
288
+ }
289
+ \`\`\`
290
+
291
+ ## Import Heresies
292
+
293
+ ### 1. Circular Dependencies
294
+ If A imports B and B imports A, restructure:
295
+
296
+ \`\`\`typescript
297
+ // HERESY
298
+ // user.ts imports role.ts, role.ts imports user.ts
299
+
300
+ // SACRED — Extract shared types
301
+ // types.ts — shared types
302
+ // user.ts — imports types.ts
303
+ // role.ts — imports types.ts
304
+ \`\`\`
305
+
306
+ ### 2. Default Export Abuse
307
+
308
+ \`\`\`typescript
309
+ // HERESY
310
+ export default class UserService { }
311
+ import UserService from './user-service'; // Can be renamed silently
312
+
313
+ // SACRED
314
+ export class UserService { }
315
+ import { UserService } from './user-service'; // Refactor-safe
316
+ \`\`\`
317
+
318
+ ### 3. Barrel File Overuse
319
+
320
+ \`\`\`typescript
321
+ // HERESY — Re-exports everything, slows bundling
322
+ // index.ts
323
+ export * from './user';
324
+ export * from './role';
325
+ export * from './permission';
326
+
327
+ // SACRED — Import directly
328
+ import { UserService } from './services/user';
329
+ \`\`\`
330
+
331
+ ## Null Safety
332
+
333
+ ### Always Use Nullish Coalescing
334
+
335
+ \`\`\`typescript
336
+ // HERESY — falsy values cause bugs
337
+ const value = config.timeout || 3000; // 0 becomes 3000!
338
+
339
+ // SACRED
340
+ const value = config.timeout ?? 3000; // Only null/undefined trigger default
341
+ \`\`\`
342
+
343
+ ---
344
+
345
+ *The compiler is wise. Enable strict mode. Praise the Machine Spirit.*
346
+ `;
347
+
348
+ const TSCONFIG_PATTERNS_MD = `# TypeScript tsconfig Patterns
349
+
350
+ *Sacred configurations for the TypeScript compiler*
351
+
352
+ ## The Strict Foundation
353
+
354
+ Every project must start with strict mode:
355
+
356
+ \`\`\`json
357
+ {
358
+ "compilerOptions": {
359
+ "strict": true
360
+ }
361
+ }
362
+ \`\`\`
363
+
364
+ This enables all strict type-checking options:
365
+ - \`strictNullChecks\` — null and undefined are distinct types
366
+ - \`strictFunctionTypes\` — stricter function type checking
367
+ - \`strictBindCallApply\` — strict bind, call, apply methods
368
+ - \`strictPropertyInitialization\` — class properties must be initialized
369
+ - \`noImplicitAny\` — error on expressions with implied any
370
+ - \`noImplicitThis\` — error on this with implied any type
371
+
372
+ ## The Recommended Base
373
+
374
+ \`\`\`json
375
+ {
376
+ "compilerOptions": {
377
+ // Strict Type Checking
378
+ "strict": true,
379
+ "noUncheckedIndexedAccess": true,
380
+ "noImplicitReturns": true,
381
+ "noFallthroughCasesInSwitch": true,
382
+ "noImplicitOverride": true,
383
+ "exactOptionalPropertyTypes": true,
384
+
385
+ // Module Resolution
386
+ "module": "ESNext",
387
+ "moduleResolution": "bundler",
388
+ "resolveJsonModule": true,
389
+ "esModuleInterop": true,
390
+ "isolatedModules": true,
391
+
392
+ // Output
393
+ "target": "ES2022",
394
+ "lib": ["ES2022"],
395
+ "declaration": true,
396
+ "declarationMap": true,
397
+ "sourceMap": true,
398
+
399
+ // Quality
400
+ "skipLibCheck": true,
401
+ "forceConsistentCasingInFileNames": true
402
+ },
403
+ "include": ["src/**/*"],
404
+ "exclude": ["node_modules", "dist"]
405
+ }
406
+ \`\`\`
407
+
408
+ ## Node.js Application
409
+
410
+ \`\`\`json
411
+ {
412
+ "compilerOptions": {
413
+ "strict": true,
414
+ "noUncheckedIndexedAccess": true,
415
+
416
+ "module": "NodeNext",
417
+ "moduleResolution": "NodeNext",
418
+ "target": "ES2022",
419
+ "lib": ["ES2022"],
420
+
421
+ "outDir": "./dist",
422
+ "rootDir": "./src",
423
+ "declaration": true,
424
+ "sourceMap": true,
425
+
426
+ "esModuleInterop": true,
427
+ "skipLibCheck": true,
428
+ "forceConsistentCasingInFileNames": true
429
+ },
430
+ "include": ["src/**/*"],
431
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
432
+ }
433
+ \`\`\`
434
+
435
+ ## Library Configuration
436
+
437
+ \`\`\`json
438
+ {
439
+ "compilerOptions": {
440
+ "strict": true,
441
+ "noUncheckedIndexedAccess": true,
442
+ "declaration": true,
443
+ "declarationMap": true,
444
+
445
+ "module": "ESNext",
446
+ "moduleResolution": "bundler",
447
+ "target": "ES2020",
448
+ "lib": ["ES2020"],
449
+
450
+ "outDir": "./dist",
451
+ "rootDir": "./src",
452
+
453
+ // Don't emit — let bundler handle it
454
+ "noEmit": true
455
+ },
456
+ "include": ["src/**/*"]
457
+ }
458
+ \`\`\`
459
+
460
+ ## React Application
461
+
462
+ \`\`\`json
463
+ {
464
+ "compilerOptions": {
465
+ "strict": true,
466
+ "noUncheckedIndexedAccess": true,
467
+
468
+ "target": "ES2020",
469
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
470
+ "module": "ESNext",
471
+ "moduleResolution": "bundler",
472
+
473
+ "jsx": "react-jsx",
474
+ "jsxImportSource": "react",
475
+
476
+ "noEmit": true,
477
+ "isolatedModules": true,
478
+ "skipLibCheck": true,
479
+ "forceConsistentCasingInFileNames": true
480
+ },
481
+ "include": ["src"]
482
+ }
483
+ \`\`\`
484
+
485
+ ## Monorepo Base Config
486
+
487
+ \`\`\`json
488
+ // packages/tsconfig.base.json
489
+ {
490
+ "compilerOptions": {
491
+ "strict": true,
492
+ "noUncheckedIndexedAccess": true,
493
+ "noImplicitReturns": true,
494
+
495
+ "module": "ESNext",
496
+ "moduleResolution": "bundler",
497
+ "target": "ES2022",
498
+
499
+ "declaration": true,
500
+ "declarationMap": true,
501
+ "sourceMap": true,
502
+ "composite": true,
503
+
504
+ "skipLibCheck": true,
505
+ "forceConsistentCasingInFileNames": true
506
+ }
507
+ }
508
+
509
+ // packages/core/tsconfig.json
510
+ {
511
+ "extends": "../tsconfig.base.json",
512
+ "compilerOptions": {
513
+ "outDir": "./dist",
514
+ "rootDir": "./src"
515
+ },
516
+ "include": ["src/**/*"],
517
+ "references": []
518
+ }
519
+
520
+ // packages/api/tsconfig.json
521
+ {
522
+ "extends": "../tsconfig.base.json",
523
+ "compilerOptions": {
524
+ "outDir": "./dist",
525
+ "rootDir": "./src"
526
+ },
527
+ "include": ["src/**/*"],
528
+ "references": [
529
+ { "path": "../core" }
530
+ ]
531
+ }
532
+ \`\`\`
533
+
534
+ ## The Forbidden Options
535
+
536
+ Never enable these in production:
537
+
538
+ \`\`\`json
539
+ {
540
+ "compilerOptions": {
541
+ // HERESY — Disables type safety
542
+ "strict": false,
543
+ "noImplicitAny": false,
544
+ "strictNullChecks": false,
545
+
546
+ // HERESY — Ignores errors
547
+ "suppressImplicitAnyIndexErrors": true,
548
+ "suppressExcessPropertyErrors": true
549
+ }
550
+ }
551
+ \`\`\`
552
+
553
+ ## Path Aliases
554
+
555
+ \`\`\`json
556
+ {
557
+ "compilerOptions": {
558
+ "baseUrl": ".",
559
+ "paths": {
560
+ "@/*": ["src/*"],
561
+ "@utils/*": ["src/utils/*"],
562
+ "@types/*": ["src/types/*"]
563
+ }
564
+ }
565
+ }
566
+ \`\`\`
567
+
568
+ Remember to configure your bundler (Vite, webpack, etc.) to resolve these paths.
569
+
570
+ ---
571
+
572
+ *The compiler configuration is sacred. Praise the Machine Spirit.*
573
+ `;
574
+
575
+ export const TYPESCRIPT_PRESET: Preset = {
576
+ name: "typescript",
577
+ manifest: {
578
+ name: "typescript",
579
+ version: "0.1.0",
580
+ description: "The TypeScript Codex — Type-safe JavaScript",
581
+ depends: ["base"],
582
+ generated_paths: ["src/", "tests/", "dist/"],
583
+ },
584
+ files: [
585
+ { path: "testing/strategy.md", content: TESTING_STRATEGY_MD },
586
+ { path: "guardrails.md", content: GUARDRAILS_MD },
587
+ { path: "design/patterns.md", content: TSCONFIG_PATTERNS_MD },
588
+ ],
589
+ };