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.
- package/.claude/commands/tlc/audit.md +39 -20
- package/.claude/commands/tlc/build.md +25 -1
- package/.claude/commands/tlc/cleanup.md +70 -0
- package/.claude/commands/tlc/dashboard.md +138 -0
- package/.claude/commands/tlc/plan.md +17 -1
- package/.claude/commands/tlc/review-pr.md +15 -0
- package/.claude/commands/tlc/review.md +62 -0
- package/.claude/commands/tlc/tlc.md +176 -1
- package/CLAUDE.md +1 -0
- package/CODING-STANDARDS.md +139 -0
- package/docker-compose.dev.yml +3 -22
- package/package.json +1 -1
- package/server/lib/output-schemas.test.js +15 -1
- package/server/setup.sh +271 -271
- package/templates/docker-compose.tlc.yml +38 -0
|
@@ -16,10 +16,185 @@ See `/tlc:build` for the complete engineering standards checklist.
|
|
|
16
16
|
|
|
17
17
|
## What This Does
|
|
18
18
|
|
|
19
|
-
|
|
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
|
|
package/CODING-STANDARDS.md
CHANGED
|
@@ -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
|
|
package/docker-compose.dev.yml
CHANGED
|
@@ -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
|
|
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,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();
|