tlc-claude-code 1.8.1 → 1.8.3

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.
@@ -16,10 +16,185 @@ See `/tlc:build` for the complete engineering standards checklist.
16
16
 
17
17
  ## What This Does
18
18
 
19
- Launches the TLC dashboard - a visual interface showing project state, phases, tests, and next actions.
19
+ Detects project state, checks for TLC version upgrades, and launches the dashboard. If a newer TLC version is installed than the project has configured, it automatically upgrades config and commands before proceeding.
20
20
 
21
21
  ## Process
22
22
 
23
+ ### Step 0: Check TLC Version (Upgrade Detection)
24
+
25
+ **Run this BEFORE anything else.** Compare installed TLC version against the project's `.tlc.json` version.
26
+
27
+ ```bash
28
+ # Get installed TLC version
29
+ installedVersion=$(node -e "
30
+ try {
31
+ const p = require('tlc-claude-code/package.json');
32
+ console.log(p.version);
33
+ } catch(e) {
34
+ // Try global
35
+ const { execSync } = require('child_process');
36
+ try {
37
+ const v = execSync('npm list -g tlc-claude-code --json 2>/dev/null', { encoding: 'utf-8' });
38
+ console.log(JSON.parse(v).dependencies['tlc-claude-code'].version);
39
+ } catch(e2) { console.log('unknown'); }
40
+ }
41
+ " 2>/dev/null)
42
+
43
+ # Get project TLC version from .tlc.json
44
+ projectVersion=$(node -e "
45
+ try {
46
+ const c = require('./.tlc.json');
47
+ console.log(c.tlcVersion || '0.0.0');
48
+ } catch(e) { console.log('0.0.0'); }
49
+ " 2>/dev/null)
50
+ ```
51
+
52
+ **If `installedVersion` > `projectVersion`:**
53
+
54
+ Show upgrade notice and apply automatically:
55
+
56
+ ```
57
+ ─────────────────────────────────────────────────────
58
+ TLC UPGRADE DETECTED
59
+ ─────────────────────────────────────────────────────
60
+
61
+ Installed: v{installedVersion}
62
+ Project: v{projectVersion}
63
+
64
+ New in this version:
65
+ {list new features — see Version Features Map below}
66
+
67
+ Applying upgrade...
68
+ ```
69
+
70
+ Then execute the upgrade steps:
71
+
72
+ #### Step 0.1: Merge New Config Sections into .tlc.json
73
+
74
+ Read the project's `.tlc.json`. For each new config section introduced since `projectVersion`, **add it ONLY if it doesn't already exist**. Never overwrite existing user settings.
75
+
76
+ ```javascript
77
+ // Pseudo-code for config merge
78
+ const config = JSON.parse(fs.readFileSync('.tlc.json'));
79
+
80
+ // Add new sections only if missing
81
+ if (!config.router) {
82
+ config.router = {
83
+ providers: {},
84
+ capabilities: {}
85
+ };
86
+ console.log(' + Added: router (LLM provider routing)');
87
+ }
88
+
89
+ if (!config.dashboard) {
90
+ config.dashboard = {
91
+ port: 3147,
92
+ auth: false
93
+ };
94
+ console.log(' + Added: dashboard config');
95
+ }
96
+
97
+ if (!config.docs) {
98
+ config.docs = {
99
+ autoSync: false,
100
+ screenshots: []
101
+ };
102
+ console.log(' + Added: docs automation config');
103
+ }
104
+
105
+ // Update version stamp
106
+ config.tlcVersion = installedVersion;
107
+
108
+ fs.writeFileSync('.tlc.json', JSON.stringify(config, null, 2));
109
+ ```
110
+
111
+ #### Step 0.2: Update CLAUDE.md Command Dispatch
112
+
113
+ Read the project's `CLAUDE.md`. Check if the command dispatch table exists. If new commands were added since `projectVersion`, append them to the table.
114
+
115
+ **How to detect:** Read `.claude/commands/tlc/*.md` directory listing from the installed TLC package. Compare against the dispatch table entries in the project's `CLAUDE.md`. Add any missing rows.
116
+
117
+ **Never remove or reorder existing entries.** Only append new ones.
118
+
119
+ #### Step 0.3: Sync Command Files
120
+
121
+ The `.claude/commands/tlc/` directory should already be up-to-date if the user ran `tlc` (the CLI installer). But verify:
122
+
123
+ ```bash
124
+ # Check if commands are current
125
+ installedCommandCount=$(ls node_modules/tlc-claude-code/.claude/commands/tlc/*.md 2>/dev/null | wc -l)
126
+ projectCommandCount=$(ls .claude/commands/tlc/*.md 2>/dev/null | wc -l)
127
+
128
+ if [ "$installedCommandCount" -gt "$projectCommandCount" ]; then
129
+ echo " + Syncing new commands to .claude/commands/tlc/"
130
+ cp node_modules/tlc-claude-code/.claude/commands/tlc/*.md .claude/commands/tlc/
131
+ fi
132
+ ```
133
+
134
+ #### Step 0.4: Detect New LLM Providers
135
+
136
+ If the `router` section was just added (or is empty), scan for available CLI tools:
137
+
138
+ ```bash
139
+ which claude >/dev/null 2>&1 && echo "claude detected"
140
+ which codex >/dev/null 2>&1 && echo "codex detected"
141
+ which gemini >/dev/null 2>&1 && echo "gemini detected"
142
+ ```
143
+
144
+ If providers are detected but `router.providers` is empty, offer to configure:
145
+
146
+ ```
147
+ LLM providers detected: claude, codex
148
+
149
+ Configure multi-model routing? (Y/n)
150
+ ```
151
+
152
+ If yes, populate `router.providers` with detected CLIs and default capability mappings.
153
+
154
+ If no, skip — user can run `/tlc:llm config` later.
155
+
156
+ #### Step 0.5: Report and Continue
157
+
158
+ ```
159
+ ─────────────────────────────────────────────────────
160
+ UPGRADE COMPLETE
161
+ ─────────────────────────────────────────────────────
162
+
163
+ Updated .tlc.json:
164
+ + router (LLM provider routing)
165
+ + dashboard config
166
+ + docs automation config
167
+ ~ tlcVersion: 0.0.0 → 1.8.2
168
+
169
+ Synced:
170
+ + 3 new commands added
171
+ + CLAUDE.md dispatch table updated
172
+
173
+ Configure LLM routing now? → /tlc:llm config
174
+ Configure dashboard? → /tlc:dashboard
175
+
176
+ Continuing to dashboard...
177
+ ─────────────────────────────────────────────────────
178
+ ```
179
+
180
+ Then continue to Step 1 (launch dashboard) as normal.
181
+
182
+ **If versions match:** Skip this step silently. No output.
183
+
184
+ #### Version Features Map
185
+
186
+ This map tells the upgrade which config sections to add based on version ranges. Update this when new features ship.
187
+
188
+ | Since Version | Config Section | Default Value | Description |
189
+ |---|---|---|---|
190
+ | 1.5.0 | `router` | `{ providers: {}, capabilities: {} }` | LLM multi-model routing |
191
+ | 1.5.0 | `enterprise` | `{ enabled: false }` | Enterprise features toggle |
192
+ | 1.7.0 | `docs` | `{ autoSync: false, screenshots: [] }` | Documentation automation |
193
+ | 1.8.0 | `dashboard` | `{ port: 3147, auth: false }` | Dashboard configuration |
194
+ | 1.8.2 | `dashboard.compose` | `"docker-compose.tlc.yml"` | Standalone dashboard compose file |
195
+
196
+ ---
197
+
23
198
  ### Step 1: Launch Dashboard
24
199
 
25
200
  Run the TLC dashboard:
package/CLAUDE.md CHANGED
@@ -96,6 +96,7 @@ Every TLC command is defined in `.claude/commands/tlc/*.md`. When you invoke a c
96
96
  | "issues", "import issues" | `/tlc:issues` | Manual issue tracking |
97
97
  | "checklist", "full check" | `/tlc:checklist` | Ad-hoc project review |
98
98
  | "quick task", "small fix" | `/tlc:quick` | Coding without tests |
99
+ | "dashboard", "start dashboard", "dashboard container" | `/tlc:dashboard` | Running docker-compose manually |
99
100
 
100
101
  ### Before ANY work — run `/tlc`
101
102
 
@@ -588,6 +588,141 @@ Refs: #456
588
588
 
589
589
  ---
590
590
 
591
+ ## 18. File Size Limits
592
+
593
+ Large files are a code smell. They indicate a class or module is doing too much.
594
+
595
+ | Threshold | Action |
596
+ |---|---|
597
+ | **< 300 lines** | Ideal. No action needed. |
598
+ | **300-500 lines** | Acceptable. Review if it can be split. |
599
+ | **500-1000 lines** | Warning. Should be split into focused sub-modules. |
600
+ | **> 1000 lines** | Violation. MUST be split before merging. |
601
+
602
+ ### How to Split
603
+
604
+ **Controllers with many routes:**
605
+ ```
606
+ # Bad: csp.controller.ts (2,000+ lines)
607
+ # Good: Split by resource/feature:
608
+ modules/csp/
609
+ controllers/
610
+ policy.controller.ts # CRUD for policies
611
+ report.controller.ts # CSP violation reports
612
+ directive.controller.ts # Directive management
613
+ csp.routes.ts # Route registration (thin)
614
+ ```
615
+
616
+ **Services with many methods:**
617
+ ```
618
+ # Bad: user.service.ts (1,500 lines)
619
+ # Good: Split by responsibility:
620
+ modules/user/
621
+ services/
622
+ user-auth.service.ts # Login, register, password reset
623
+ user-profile.service.ts # Profile CRUD, avatar, preferences
624
+ user-admin.service.ts # Admin operations, ban, role changes
625
+ user.service.ts # Thin facade re-exporting sub-services
626
+ ```
627
+
628
+ ---
629
+
630
+ ## 19. Folder Overcrowding
631
+
632
+ Too many files in one folder makes navigation difficult. Organize into subfolders by domain.
633
+
634
+ | Threshold | Action |
635
+ |---|---|
636
+ | **< 8 files** | Fine as-is. |
637
+ | **8-15 files** | Consider grouping into subfolders. |
638
+ | **> 15 files** | MUST be organized into subfolders. |
639
+
640
+ ```
641
+ # Bad: 25 files dumped in controllers/
642
+ controllers/
643
+ auth.controller.ts
644
+ user.controller.ts
645
+ payment.controller.ts
646
+ invoice.controller.ts
647
+ product.controller.ts
648
+ ... (20 more)
649
+
650
+ # Good: Organized by domain
651
+ modules/
652
+ auth/auth.controller.ts
653
+ user/user.controller.ts
654
+ billing/
655
+ payment.controller.ts
656
+ invoice.controller.ts
657
+ catalog/
658
+ product.controller.ts
659
+ ```
660
+
661
+ ---
662
+
663
+ ## 20. Strict Typing
664
+
665
+ TypeScript without strict types is just JavaScript with extra steps.
666
+
667
+ ### Rules
668
+
669
+ 1. **No `any` type** - Use `unknown` and narrow, or define a proper interface.
670
+ 2. **No implicit `any`** - Enable `noImplicitAny` in tsconfig.
671
+ 3. **All functions must have explicit return types** - Not just exported functions.
672
+ 4. **All parameters must be typed** - No untyped function parameters.
673
+ 5. **Prefer `interface` over `type`** for object shapes - Easier to extend.
674
+ 6. **Use `strict: true`** in tsconfig.json.
675
+
676
+ ```typescript
677
+ // Bad: Missing types, implicit any
678
+ function processData(data) {
679
+ const result = data.items.map(item => item.value);
680
+ return result;
681
+ }
682
+
683
+ // Good: Explicit types everywhere
684
+ interface DataPayload {
685
+ items: Array<{ value: number }>;
686
+ }
687
+
688
+ function processData(data: DataPayload): number[] {
689
+ return data.items.map((item) => item.value);
690
+ }
691
+ ```
692
+
693
+ ```typescript
694
+ // Bad: any type
695
+ function handleResponse(response: any): any {
696
+ return response.data;
697
+ }
698
+
699
+ // Good: Proper types
700
+ interface ApiResponse<T> {
701
+ data: T;
702
+ status: number;
703
+ }
704
+
705
+ function handleResponse<T>(response: ApiResponse<T>): T {
706
+ return response.data;
707
+ }
708
+ ```
709
+
710
+ ### tsconfig.json Requirements
711
+
712
+ ```json
713
+ {
714
+ "compilerOptions": {
715
+ "strict": true,
716
+ "noImplicitAny": true,
717
+ "noImplicitReturns": true,
718
+ "noUnusedLocals": true,
719
+ "noUnusedParameters": true
720
+ }
721
+ }
722
+ ```
723
+
724
+ ---
725
+
591
726
  ## AI Instructions
592
727
 
593
728
  When generating code:
@@ -605,6 +740,10 @@ When generating code:
605
740
  11. **Always** add JSDoc to public members
606
741
  12. **Always** create index.ts with barrel exports
607
742
  13. **Always** verify build passes after changes
743
+ 14. **Never** let files exceed 1000 lines - split into sub-modules
744
+ 15. **Never** let folders exceed 15 files - organize into subfolders
745
+ 16. **Never** use `any` type - use `unknown` or proper interfaces
746
+ 17. **Always** add explicit return types to functions
608
747
 
609
748
  ### Cleanup Tasks
610
749
 
@@ -94,7 +94,8 @@ services:
94
94
  retries: 5
95
95
  restart: on-failure
96
96
 
97
- # TLC Dashboard
97
+ # TLC Dashboard (Express API + React SPA on port 3147)
98
+ # Since v1.8.1, the server serves the React dashboard from dashboard-web/dist/
98
99
  dashboard:
99
100
  image: node:20-alpine
100
101
  container_name: tlc-${COMPOSE_PROJECT_NAME:-dev}-dashboard
@@ -105,7 +106,7 @@ services:
105
106
  npm install -g tlc-claude-code@latest &&
106
107
  TLC_DIR=/usr/local/lib/node_modules/tlc-claude-code &&
107
108
  echo 'TLC installed at:' $$TLC_DIR &&
108
- ls -la $$TLC_DIR/server/ &&
109
+ ls $$TLC_DIR/dashboard-web/dist/index.html && echo '[TLC] React SPA ready' || echo '[TLC] WARNING: dashboard-web/dist not found' &&
109
110
  cd /project && node $$TLC_DIR/server/index.js --proxy-only --skip-db
110
111
  "
111
112
  environment:
@@ -121,26 +122,6 @@ services:
121
122
  - app
122
123
  restart: on-failure
123
124
 
124
- # TLC Dashboard Web (built from dashboard-web, serves static files via nginx)
125
- dashboard-web:
126
- build:
127
- context: ./dashboard-web
128
- dockerfile: Dockerfile
129
- container_name: tlc-${COMPOSE_PROJECT_NAME:-dev}-dashboard-web
130
- ports:
131
- - "${DASHBOARD_WEB_PORT:-3002}:80"
132
- environment:
133
- - API_URL=http://dashboard:3147
134
- depends_on:
135
- - dashboard
136
- restart: on-failure
137
- healthcheck:
138
- test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:80/health"]
139
- interval: 30s
140
- timeout: 3s
141
- start_period: 5s
142
- retries: 3
143
-
144
125
  # Playwright E2E Tests (optional - starts on demand)
145
126
  playwright:
146
127
  image: mcr.microsoft.com/playwright:v1.40.0-jammy
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tlc-claude-code",
3
- "version": "1.8.1",
3
+ "version": "1.8.3",
4
4
  "description": "TLC - Test Led Coding for Claude Code",
5
5
  "bin": {
6
6
  "tlc": "./bin/tlc.js",
@@ -1,4 +1,6 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { mkdirSync, writeFileSync, rmSync } from 'fs';
3
+ import { join } from 'path';
2
4
  import {
3
5
  loadSchema,
4
6
  validateOutput,
@@ -9,8 +11,20 @@ import {
9
11
  schemaToPromptInstructions,
10
12
  } from './output-schemas.js';
11
13
 
14
+ const schemasDir = join(process.cwd(), '.tlc', 'schemas');
15
+ const testSchema = { type: 'object', required: ['summary'], properties: { summary: { type: 'string' } } };
16
+
12
17
  describe('Output Schemas', () => {
13
18
  describe('loadSchema', () => {
19
+ beforeEach(() => {
20
+ mkdirSync(schemasDir, { recursive: true });
21
+ writeFileSync(join(schemasDir, 'review-result.json'), JSON.stringify(testSchema));
22
+ });
23
+
24
+ afterEach(() => {
25
+ rmSync(join(schemasDir, 'review-result.json'), { force: true });
26
+ });
27
+
14
28
  it('reads from file', async () => {
15
29
  const schema = await loadSchema('review-result');
16
30
  expect(schema).toBeDefined();