tlc-claude-code 1.6.0 → 1.6.2
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/build.md +148 -56
- package/.claude/commands/tlc/llm.md +4 -4
- package/.claude/commands/tlc/sync.md +1 -1
- package/CLAUDE.md +11 -0
- package/README.md +2 -2
- package/docker-compose.dev.yml +20 -0
- package/package.json +1 -1
- package/server/index.js +212 -0
- package/server/lib/adapters/claude-adapter.js +3 -3
- package/server/lib/adapters/claude-adapter.test.js +2 -2
- package/server/lib/adapters/openai-adapter.js +3 -3
- package/server/lib/adapters/openai-adapter.test.js +2 -2
- package/server/lib/model-pricing.js +2 -0
- package/server/lib/overdrive-command.js +151 -15
- package/server/lib/overdrive-command.test.js +207 -10
- package/sync.md +1 -1
|
@@ -6,6 +6,55 @@ Write failing tests, then implement to make them pass.
|
|
|
6
6
|
|
|
7
7
|
**Code like a senior engineer with 15+ years experience.** Every line should reflect:
|
|
8
8
|
|
|
9
|
+
### Project Structure (NestJS-Style Modules)
|
|
10
|
+
|
|
11
|
+
**Group by domain entity, not by file type.** Each entity gets its own module folder:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
src/modules/{entity}/
|
|
15
|
+
├── interfaces/ # Types and interfaces (NEVER at module root)
|
|
16
|
+
│ ├── {entity}.interface.ts
|
|
17
|
+
│ └── index.ts
|
|
18
|
+
├── dto/ # Request/Response DTOs
|
|
19
|
+
│ ├── create.dto.ts
|
|
20
|
+
│ └── index.ts
|
|
21
|
+
├── enums/ # Enums (no magic strings)
|
|
22
|
+
├── constants/ # Configuration constants
|
|
23
|
+
├── guards/ # Auth/permission middleware
|
|
24
|
+
├── {entity}.service.ts # Business logic
|
|
25
|
+
├── {entity}.controller.ts # HTTP handlers
|
|
26
|
+
├── {entity}.repository.ts # Data access
|
|
27
|
+
├── {entity}.seed.ts # Seed data (per-entity, not monolithic)
|
|
28
|
+
├── {entity}.test.ts # Tests
|
|
29
|
+
└── index.ts # Barrel exports
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Server/src root should ONLY contain:**
|
|
33
|
+
- `index.ts` - Entry point
|
|
34
|
+
- `lib/` - Core shared libraries
|
|
35
|
+
- `modules/` - Feature modules
|
|
36
|
+
- `shared/` - Cross-cutting utilities
|
|
37
|
+
- Config files
|
|
38
|
+
|
|
39
|
+
**❌ NEVER do this:**
|
|
40
|
+
```
|
|
41
|
+
src/
|
|
42
|
+
services/ # ❌ All services dumped together
|
|
43
|
+
interfaces/ # ❌ All types dumped together
|
|
44
|
+
controllers/ # ❌ Flat controller folder
|
|
45
|
+
|
|
46
|
+
server/
|
|
47
|
+
auth.ts # ❌ Loose file at root (should be modules/auth/)
|
|
48
|
+
helpers.ts # ❌ Should be in lib/ or shared/
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Key rules:**
|
|
52
|
+
- Interfaces ALWAYS in `interfaces/` subdirectory, never at module root
|
|
53
|
+
- No inline interfaces in service files - import from `interfaces/`
|
|
54
|
+
- No magic strings - use `enums/` or `constants/`
|
|
55
|
+
- Seeds per-entity, not one giant seeds.ts
|
|
56
|
+
- Every module has `index.ts` barrel export
|
|
57
|
+
|
|
9
58
|
### Code Quality
|
|
10
59
|
- **Clean Architecture**: Separate concerns. Domain logic never depends on infrastructure.
|
|
11
60
|
- **SOLID Principles**: Single responsibility, open/closed, Liskov substitution, interface segregation, dependency inversion.
|
|
@@ -117,13 +166,17 @@ Senior engineers know when rules don't apply:
|
|
|
117
166
|
|
|
118
167
|
This is the core TLC command. Tests before code, one task at a time.
|
|
119
168
|
|
|
120
|
-
**Overdrive Mode:** When tasks are independent, TLC auto-detects and offers parallel execution with multiple agents.
|
|
169
|
+
**Overdrive Mode (Opus 4.6):** When tasks are independent, TLC auto-detects and offers parallel execution with multiple agents. Agents are assigned models based on task complexity (opus for heavy, sonnet for standard, haiku for light). Failed agents can be resumed. No arbitrary cap on agent count.
|
|
121
170
|
|
|
122
171
|
## Usage
|
|
123
172
|
|
|
124
173
|
```
|
|
125
174
|
/tlc:build <phase_number>
|
|
126
|
-
/tlc:build <phase_number> --sequential
|
|
175
|
+
/tlc:build <phase_number> --sequential # Force sequential mode
|
|
176
|
+
/tlc:build <phase_number> --model sonnet # Force all agents to use sonnet
|
|
177
|
+
/tlc:build <phase_number> --model haiku # Force all agents to use haiku (fast/cheap)
|
|
178
|
+
/tlc:build <phase_number> --max-turns 30 # Limit agent execution length
|
|
179
|
+
/tlc:build <phase_number> --agents 5 # Limit parallel agents to 5
|
|
127
180
|
```
|
|
128
181
|
|
|
129
182
|
## Process
|
|
@@ -132,7 +185,7 @@ This is the core TLC command. Tests before code, one task at a time.
|
|
|
132
185
|
|
|
133
186
|
Read all `.planning/phases/{phase}-*-PLAN.md` files for this phase.
|
|
134
187
|
|
|
135
|
-
### Step 1a: Overdrive Detection (Auto-Parallel)
|
|
188
|
+
### Step 1a: Overdrive Detection (Auto-Parallel, Opus 4.6)
|
|
136
189
|
|
|
137
190
|
After loading plans, analyze task dependencies to determine if parallel execution is possible.
|
|
138
191
|
|
|
@@ -151,18 +204,19 @@ After loading plans, analyze task dependencies to determine if parallel executio
|
|
|
151
204
|
2. Look for dependency markers in task descriptions
|
|
152
205
|
3. Check "## Dependencies" section if present
|
|
153
206
|
4. Identify independent tasks (no dependencies)
|
|
207
|
+
5. Estimate task complexity for model assignment (heavy/standard/light)
|
|
154
208
|
|
|
155
209
|
**If 2+ independent tasks found:**
|
|
156
210
|
```
|
|
157
|
-
🚀 Overdrive Mode Available
|
|
211
|
+
🚀 Overdrive Mode Available (Opus 4.6)
|
|
158
212
|
|
|
159
213
|
Phase 3 has 4 independent tasks that can run in parallel:
|
|
160
|
-
- Task 1: Create user schema
|
|
161
|
-
- Task 2: Add validation helpers
|
|
162
|
-
- Task 3: Write migration scripts
|
|
163
|
-
- Task 4: Create seed data
|
|
214
|
+
- Task 1: Create user schema [opus] (heavy)
|
|
215
|
+
- Task 2: Add validation helpers [sonnet] (standard)
|
|
216
|
+
- Task 3: Write migration scripts [sonnet] (standard)
|
|
217
|
+
- Task 4: Create seed data [haiku] (light)
|
|
164
218
|
|
|
165
|
-
Recommended:
|
|
219
|
+
Recommended: 4 agents (one per independent task)
|
|
166
220
|
|
|
167
221
|
Options:
|
|
168
222
|
1) Overdrive mode (parallel agents) [Recommended]
|
|
@@ -170,6 +224,15 @@ Options:
|
|
|
170
224
|
3) Let me pick which tasks to parallelize
|
|
171
225
|
```
|
|
172
226
|
|
|
227
|
+
**Model auto-selection per task complexity:**
|
|
228
|
+
| Complexity | Model | When |
|
|
229
|
+
|-----------|-------|------|
|
|
230
|
+
| Heavy | opus | Architecture, multi-file features, security, auth, database |
|
|
231
|
+
| Standard | sonnet | Normal implementation tasks (default) |
|
|
232
|
+
| Light | haiku | Config, boilerplate, DTOs, enums, constants, seed data |
|
|
233
|
+
|
|
234
|
+
Override with `--model sonnet` to force all agents to the same model.
|
|
235
|
+
|
|
173
236
|
**If tasks have dependencies (waterfall):**
|
|
174
237
|
```
|
|
175
238
|
📋 Sequential Mode
|
|
@@ -181,62 +244,87 @@ Phase 3 tasks have dependencies:
|
|
|
181
244
|
Running in sequential order.
|
|
182
245
|
```
|
|
183
246
|
|
|
184
|
-
### Step 1b: Execute Overdrive (if selected)
|
|
247
|
+
### Step 1b: Execute Overdrive (if selected) — Opus 4.6 Multi-Agent
|
|
185
248
|
|
|
186
|
-
When overdrive mode is selected, spawn parallel agents:
|
|
249
|
+
When overdrive mode is selected, spawn parallel agents with per-task model selection:
|
|
187
250
|
|
|
188
251
|
```
|
|
189
|
-
🚀 Launching Overdrive Mode
|
|
252
|
+
🚀 Launching Overdrive Mode (Opus 4.6)
|
|
190
253
|
|
|
191
|
-
Spawning
|
|
254
|
+
Spawning 4 agents in parallel...
|
|
192
255
|
|
|
193
|
-
Agent 1: Task 1 - Create user schema
|
|
194
|
-
Agent 2: Task 2 - Add validation helpers
|
|
195
|
-
Agent 3: Task 3 - Write migration scripts
|
|
256
|
+
Agent 1: Task 1 - Create user schema [opus]
|
|
257
|
+
Agent 2: Task 2 - Add validation helpers [sonnet]
|
|
258
|
+
Agent 3: Task 3 - Write migration scripts [sonnet]
|
|
259
|
+
Agent 4: Task 4 - Create seed data [haiku]
|
|
196
260
|
|
|
197
261
|
[All agents spawned - working in background]
|
|
198
|
-
[Task 4 queued for next available agent]
|
|
199
262
|
```
|
|
200
263
|
|
|
201
264
|
**Agent execution rules:**
|
|
202
|
-
-
|
|
265
|
+
- One agent per independent task (no arbitrary cap)
|
|
266
|
+
- Model assigned per task complexity (override with `--model`)
|
|
203
267
|
- Agents work autonomously (no confirmation prompts)
|
|
204
268
|
- Each agent commits after completing their task
|
|
205
|
-
- When an agent finishes, it can pick up queued tasks
|
|
206
269
|
- All agents follow test-first methodology
|
|
270
|
+
- `max_turns` limits execution length (default: 50, override with `--max-turns`)
|
|
207
271
|
|
|
208
272
|
**CRITICAL: Spawn all agents in a SINGLE message using multiple Task tool calls.**
|
|
209
273
|
|
|
210
274
|
```
|
|
211
|
-
Task(description="Agent 1: Task 1", prompt="...", subagent_type="general-purpose", run_in_background=true)
|
|
212
|
-
Task(description="Agent 2: Task 2", prompt="...", subagent_type="general-purpose", run_in_background=true)
|
|
213
|
-
Task(description="Agent 3: Task 3", prompt="...", subagent_type="general-purpose", run_in_background=true)
|
|
275
|
+
Task(description="Agent 1: Task 1", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
|
|
276
|
+
Task(description="Agent 2: Task 2", prompt="...", subagent_type="general-purpose", model="sonnet", max_turns=50, run_in_background=true)
|
|
277
|
+
Task(description="Agent 3: Task 3", prompt="...", subagent_type="general-purpose", model="sonnet", max_turns=50, run_in_background=true)
|
|
278
|
+
Task(description="Agent 4: Task 4", prompt="...", subagent_type="general-purpose", model="haiku", max_turns=50, run_in_background=true)
|
|
214
279
|
```
|
|
215
280
|
|
|
216
|
-
**Live Progress Monitoring:**
|
|
281
|
+
**Live Progress Monitoring (TaskOutput):**
|
|
282
|
+
|
|
283
|
+
Use `TaskOutput` with `block=false` for non-blocking progress checks:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
TaskOutput(task_id="AGENT_ID_1", block=false, timeout=5000)
|
|
287
|
+
TaskOutput(task_id="AGENT_ID_2", block=false, timeout=5000)
|
|
288
|
+
```
|
|
217
289
|
|
|
218
|
-
|
|
290
|
+
Or use the AgentProgressMonitor for formatted status:
|
|
219
291
|
|
|
220
292
|
```bash
|
|
221
293
|
node -e "
|
|
222
294
|
const { AgentProgressMonitor } = require('./server/lib/agent-progress-monitor.js');
|
|
223
295
|
const monitor = new AgentProgressMonitor('/tmp/claude-1000/-mnt-c-Code-TLC/tasks');
|
|
224
|
-
const agents = ['AGENT_ID_1', 'AGENT_ID_2', 'AGENT_ID_3'];
|
|
296
|
+
const agents = ['AGENT_ID_1', 'AGENT_ID_2', 'AGENT_ID_3', 'AGENT_ID_4'];
|
|
225
297
|
console.log(monitor.formatTable(agents));
|
|
226
298
|
"
|
|
227
299
|
```
|
|
228
300
|
|
|
229
301
|
Display format:
|
|
230
302
|
```
|
|
231
|
-
|
|
232
|
-
🚀 OVERDRIVE STATUS
|
|
233
|
-
|
|
234
|
-
| Agent | Task | Tests | Phase |
|
|
235
|
-
|
|
236
|
-
| a1b2c3 |
|
|
237
|
-
| d4e5f6 |
|
|
238
|
-
| g7h8i9 |
|
|
239
|
-
|
|
303
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
304
|
+
🚀 OVERDRIVE STATUS (Opus 4.6)
|
|
305
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
306
|
+
| Agent | Task | Model | Tests | Phase |
|
|
307
|
+
|-------|------|-------|-------|-------|
|
|
308
|
+
| a1b2c3 | User Schema | opus | 29 ✓ | committed |
|
|
309
|
+
| d4e5f6 | Validation | sonnet | 18 ✓ | implementing |
|
|
310
|
+
| g7h8i9 | Migrations | sonnet | - | writing-tests |
|
|
311
|
+
| j0k1l2 | Seed Data | haiku | 5 ✓ | committed |
|
|
312
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**Handling Agent Failures (Opus 4.6 Resumption):**
|
|
316
|
+
|
|
317
|
+
If an agent fails or gets stuck:
|
|
318
|
+
|
|
319
|
+
```
|
|
320
|
+
# Check if agent is stuck (non-blocking)
|
|
321
|
+
TaskOutput(task_id="AGENT_ID", block=false, timeout=5000)
|
|
322
|
+
|
|
323
|
+
# Cancel a stuck agent
|
|
324
|
+
TaskStop(task_id="AGENT_ID")
|
|
325
|
+
|
|
326
|
+
# Resume a failed agent from where it left off (full context preserved)
|
|
327
|
+
Task(resume="AGENT_ID", prompt="Continue from where you left off. Fix any errors and complete the task.")
|
|
240
328
|
```
|
|
241
329
|
|
|
242
330
|
**Show status automatically:**
|
|
@@ -750,7 +838,7 @@ Writing tests for Task 2: Validation...
|
|
|
750
838
|
Phase 1 complete. Ready for /tlc:verify 1
|
|
751
839
|
```
|
|
752
840
|
|
|
753
|
-
### Overdrive Mode (Independent Tasks)
|
|
841
|
+
### Overdrive Mode (Independent Tasks, Opus 4.6)
|
|
754
842
|
|
|
755
843
|
```
|
|
756
844
|
User: /tlc:build 2
|
|
@@ -758,15 +846,15 @@ User: /tlc:build 2
|
|
|
758
846
|
Claude: Loading phase 2 plans...
|
|
759
847
|
Found: 2-PLAN.md (4 tasks)
|
|
760
848
|
|
|
761
|
-
🚀 Overdrive Mode Available
|
|
849
|
+
🚀 Overdrive Mode Available (Opus 4.6)
|
|
762
850
|
|
|
763
851
|
Phase 2 has 4 independent tasks:
|
|
764
|
-
- Task 1: Create API routes
|
|
765
|
-
- Task 2: Add input validation
|
|
766
|
-
- Task 3: Write error handlers
|
|
767
|
-
- Task 4: Add rate limiting
|
|
852
|
+
- Task 1: Create API routes [sonnet] (standard)
|
|
853
|
+
- Task 2: Add input validation [sonnet] (standard)
|
|
854
|
+
- Task 3: Write error handlers [sonnet] (standard)
|
|
855
|
+
- Task 4: Add rate limiting config [haiku] (light)
|
|
768
856
|
|
|
769
|
-
Recommended:
|
|
857
|
+
Recommended: 4 agents (one per task)
|
|
770
858
|
|
|
771
859
|
Options:
|
|
772
860
|
1) Overdrive mode (parallel) [Recommended]
|
|
@@ -775,21 +863,20 @@ Options:
|
|
|
775
863
|
|
|
776
864
|
User: 1
|
|
777
865
|
|
|
778
|
-
Claude: 🚀 Launching Overdrive Mode
|
|
866
|
+
Claude: 🚀 Launching Overdrive Mode (Opus 4.6)
|
|
779
867
|
|
|
780
|
-
Spawning
|
|
781
|
-
[Agent 1] Task 1: Create API routes - STARTED
|
|
782
|
-
[Agent 2] Task 2: Add input validation - STARTED
|
|
783
|
-
[Agent 3] Task 3: Write error handlers - STARTED
|
|
784
|
-
[
|
|
868
|
+
Spawning 4 agents...
|
|
869
|
+
[Agent 1] Task 1: Create API routes [sonnet] - STARTED
|
|
870
|
+
[Agent 2] Task 2: Add input validation [sonnet] - STARTED
|
|
871
|
+
[Agent 3] Task 3: Write error handlers [sonnet] - STARTED
|
|
872
|
+
[Agent 4] Task 4: Add rate limiting config [haiku] - STARTED
|
|
785
873
|
|
|
786
874
|
... agents working in background ...
|
|
787
875
|
|
|
788
|
-
[Agent
|
|
789
|
-
[Agent 2] Task
|
|
790
|
-
[Agent 1] ✅ Task 1 complete (4 commits)
|
|
791
|
-
[Agent 3] ✅ Task 3 complete (2 commits)
|
|
792
|
-
[Agent 2] ✅ Task 4 complete (2 commits)
|
|
876
|
+
[Agent 4] ✅ Task 4 complete (1 commit) [haiku - fast]
|
|
877
|
+
[Agent 2] ✅ Task 2 complete (3 commits) [sonnet]
|
|
878
|
+
[Agent 1] ✅ Task 1 complete (4 commits) [sonnet]
|
|
879
|
+
[Agent 3] ✅ Task 3 complete (2 commits) [sonnet]
|
|
793
880
|
|
|
794
881
|
All agents complete. Running full test suite...
|
|
795
882
|
✅ 24 tests passing
|
|
@@ -814,18 +901,23 @@ Phase 2 complete. Ready for /tlc:verify 2
|
|
|
814
901
|
- Suggest running `/tlc:build {phase}` again to retry
|
|
815
902
|
- Or manually fix and run `/tlc:status` to verify
|
|
816
903
|
|
|
817
|
-
**Overdrive mode issues:**
|
|
818
|
-
- Agent stuck → Check with `TaskOutput`
|
|
904
|
+
**Overdrive mode issues (Opus 4.6):**
|
|
905
|
+
- Agent stuck → Check with `TaskOutput(task_id, block=false)`, cancel with `TaskStop(task_id)`
|
|
906
|
+
- Agent failed → Resume with `Task(resume=agent_id)` to continue from where it left off
|
|
819
907
|
- Merge conflicts → Agents working on same files (rare if tasks are truly independent)
|
|
820
|
-
- One agent failed → Other agents continue;
|
|
908
|
+
- One agent failed → Other agents continue; resume failed agent or fix manually
|
|
821
909
|
- Want sequential instead → Use `--sequential` flag: `/tlc:build 2 --sequential`
|
|
910
|
+
- Cost too high → Use `--model haiku` to force all agents to haiku
|
|
911
|
+
- Agent running too long → Use `--max-turns 30` to limit execution
|
|
822
912
|
|
|
823
913
|
## Flags
|
|
824
914
|
|
|
825
915
|
| Flag | Description |
|
|
826
916
|
|------|-------------|
|
|
827
917
|
| `--sequential` | Force sequential execution even if tasks are independent |
|
|
828
|
-
| `--agents N` |
|
|
918
|
+
| `--agents N` | Limit parallel agents to N (default: one per independent task) |
|
|
919
|
+
| `--model MODEL` | Force all agents to use a specific model (opus, sonnet, haiku) |
|
|
920
|
+
| `--max-turns N` | Limit each agent's execution to N turns (default: 50) |
|
|
829
921
|
|
|
830
922
|
## When Overdrive is NOT Used
|
|
831
923
|
|
|
@@ -61,13 +61,13 @@ Add to `.tlc.json`:
|
|
|
61
61
|
"claude": {
|
|
62
62
|
"type": "cli",
|
|
63
63
|
"command": "claude",
|
|
64
|
-
"model": "claude-4-
|
|
64
|
+
"model": "claude-opus-4-6",
|
|
65
65
|
"capabilities": ["review", "code-gen", "refactor"]
|
|
66
66
|
},
|
|
67
67
|
"codex": {
|
|
68
68
|
"type": "cli",
|
|
69
69
|
"command": "codex",
|
|
70
|
-
"model": "gpt-5.
|
|
70
|
+
"model": "gpt-5.3-codex",
|
|
71
71
|
"capabilities": ["review", "code-gen"]
|
|
72
72
|
},
|
|
73
73
|
"gemini": {
|
|
@@ -89,9 +89,9 @@ Add to `.tlc.json`:
|
|
|
89
89
|
|
|
90
90
|
## Supported Models
|
|
91
91
|
|
|
92
|
-
**Claude:** claude-4-opus, claude-4-sonnet, claude-3.5-sonnet, claude-3-opus, claude-3-sonnet, claude-3-haiku
|
|
92
|
+
**Claude:** claude-opus-4-6, claude-sonnet-4-5, claude-4-opus, claude-4-sonnet, claude-3.5-sonnet, claude-3-opus, claude-3-sonnet, claude-3-haiku
|
|
93
93
|
|
|
94
|
-
**OpenAI/Codex:** gpt-5.2, gpt-5, gpt-4o, gpt-4o-mini, gpt-4-turbo, o3, o3-mini, o1, o1-mini, o1-pro
|
|
94
|
+
**OpenAI/Codex:** gpt-5.3-codex, gpt-5.2, gpt-5, gpt-4o, gpt-4o-mini, gpt-4-turbo, o3, o3-mini, o1, o1-mini, o1-pro
|
|
95
95
|
|
|
96
96
|
**Gemini:** gemini-3-preview, gemini-3, gemini-2.5-pro, gemini-2.0-flash, gemini-2.0-flash-thinking, gemini-1.5-pro
|
|
97
97
|
|
package/CLAUDE.md
CHANGED
|
@@ -89,6 +89,17 @@ All implementation follows **Red → Green → Refactor**:
|
|
|
89
89
|
|
|
90
90
|
Tests are written BEFORE implementation, not after.
|
|
91
91
|
|
|
92
|
+
## Context Management
|
|
93
|
+
|
|
94
|
+
**Use multiple sessions/agents for large tasks.** When working in overdrive mode or across workspaces:
|
|
95
|
+
- Use the `Task` tool to spawn sub-agents for independent work (research, testing, building)
|
|
96
|
+
- Keep the main conversation focused on orchestration and decisions
|
|
97
|
+
- Delegate file-heavy operations (reading many files, running test suites) to sub-agents
|
|
98
|
+
- This prevents context window overflow, which causes crashes and lost work
|
|
99
|
+
- Especially critical in workspace mode where multiple repos are involved
|
|
100
|
+
|
|
101
|
+
**Signs you need to delegate:** If you've read 15+ files, run 10+ commands, or the conversation is getting long — spawn a sub-agent for the next chunk of work.
|
|
102
|
+
|
|
92
103
|
## After TLC Updates
|
|
93
104
|
|
|
94
105
|
If TLC command files are updated, re-read them before executing. Check version in `package.json`.
|
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ Or use `/tlc` for the full dashboard.
|
|
|
72
72
|
### For Solo Developers
|
|
73
73
|
|
|
74
74
|
- **Test-first by default** — Claude writes tests before code
|
|
75
|
-
- **Auto-parallelization** —
|
|
75
|
+
- **Auto-parallelization** — Agents run independent tasks simultaneously with per-task model selection (Opus 4.6)
|
|
76
76
|
- **`/tlc:next`** — One command to progress. No decisions needed.
|
|
77
77
|
- **Smart dashboard** — See progress, run actions
|
|
78
78
|
- **Coverage gaps** — Find and fix untested code
|
|
@@ -103,7 +103,7 @@ Or use `/tlc` for the full dashboard.
|
|
|
103
103
|
| `/tlc` | Smart dashboard — full status view |
|
|
104
104
|
| `/tlc:new-project` | Start new project with roadmap |
|
|
105
105
|
| `/tlc:init` | Add TLC to existing codebase |
|
|
106
|
-
| `/tlc:build` | Write tests → implement (auto-parallelizes
|
|
106
|
+
| `/tlc:build` | Write tests → implement (auto-parallelizes with model selection) |
|
|
107
107
|
| `/tlc:coverage` | Find and fix untested code |
|
|
108
108
|
| `/tlc:quality` | Test quality scoring |
|
|
109
109
|
| `/tlc:autofix` | Auto-repair failing tests |
|
package/docker-compose.dev.yml
CHANGED
|
@@ -121,6 +121,26 @@ services:
|
|
|
121
121
|
- app
|
|
122
122
|
restart: on-failure
|
|
123
123
|
|
|
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
|
+
|
|
124
144
|
# Playwright E2E Tests (optional - starts on demand)
|
|
125
145
|
playwright:
|
|
126
146
|
image: mcr.microsoft.com/playwright:v1.40.0-jammy
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Suppress punycode deprecation from upstream deps (http-proxy-middleware, ws)
|
|
4
|
+
const originalEmit = process.emit;
|
|
5
|
+
process.emit = function (event, error) {
|
|
6
|
+
if (event === 'warning' && error?.name === 'DeprecationWarning' && error?.message?.includes('punycode')) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
return originalEmit.apply(process, arguments);
|
|
10
|
+
};
|
|
11
|
+
|
|
3
12
|
const express = require('express');
|
|
4
13
|
const { createServer } = require('http');
|
|
5
14
|
const { WebSocketServer } = require('ws');
|
|
@@ -50,6 +59,7 @@ let appProcess = null;
|
|
|
50
59
|
let appPort = 3000;
|
|
51
60
|
let wsClients = new Set();
|
|
52
61
|
const logs = { app: [], test: [], git: [] };
|
|
62
|
+
const commandHistory = [];
|
|
53
63
|
|
|
54
64
|
// Create Express app
|
|
55
65
|
const app = express();
|
|
@@ -944,6 +954,177 @@ app.post('/api/test', (req, res) => {
|
|
|
944
954
|
res.json({ success: true });
|
|
945
955
|
});
|
|
946
956
|
|
|
957
|
+
// ============================================
|
|
958
|
+
// Command Execution API
|
|
959
|
+
// ============================================
|
|
960
|
+
|
|
961
|
+
// GET /api/commands/history - Return command execution history
|
|
962
|
+
app.get('/api/commands/history', (req, res) => {
|
|
963
|
+
res.json({ success: true, history: commandHistory });
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
// POST /api/commands/:command - Execute a TLC command
|
|
967
|
+
app.post('/api/commands/:command', (req, res) => {
|
|
968
|
+
const command = req.params.command;
|
|
969
|
+
const { args } = req.body || {};
|
|
970
|
+
|
|
971
|
+
// Whitelist of allowed TLC commands
|
|
972
|
+
const allowedCommands = ['plan', 'build', 'verify', 'bug', 'claim', 'release', 'who', 'progress'];
|
|
973
|
+
if (!allowedCommands.includes(command)) {
|
|
974
|
+
return res.status(400).json({ success: false, error: `Unknown command: ${command}. Allowed: ${allowedCommands.join(', ')}` });
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
const entry = {
|
|
978
|
+
id: `cmd-${Date.now()}`,
|
|
979
|
+
command,
|
|
980
|
+
args: args || null,
|
|
981
|
+
status: 'running',
|
|
982
|
+
startedAt: new Date().toISOString(),
|
|
983
|
+
output: '',
|
|
984
|
+
};
|
|
985
|
+
commandHistory.push(entry);
|
|
986
|
+
if (commandHistory.length > 100) commandHistory.shift();
|
|
987
|
+
|
|
988
|
+
addLog('app', `Executing command: tlc:${command}${args ? ' ' + args : ''}`, 'info');
|
|
989
|
+
broadcast('command-started', { id: entry.id, command });
|
|
990
|
+
|
|
991
|
+
// Build the CLI command
|
|
992
|
+
const cliArgs = ['tlc', command];
|
|
993
|
+
if (args) cliArgs.push(args);
|
|
994
|
+
const fullCmd = `npx ${cliArgs.join(' ')}`;
|
|
995
|
+
|
|
996
|
+
const cmdProcess = spawn('npx', cliArgs.slice(1), {
|
|
997
|
+
cwd: PROJECT_DIR,
|
|
998
|
+
env: { ...process.env },
|
|
999
|
+
shell: true,
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
let output = '';
|
|
1003
|
+
|
|
1004
|
+
cmdProcess.stdout.on('data', (data) => {
|
|
1005
|
+
const text = data.toString();
|
|
1006
|
+
output += text;
|
|
1007
|
+
broadcast('command-output', { id: entry.id, data: text, stream: 'stdout' });
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
cmdProcess.stderr.on('data', (data) => {
|
|
1011
|
+
const text = data.toString();
|
|
1012
|
+
output += text;
|
|
1013
|
+
broadcast('command-output', { id: entry.id, data: text, stream: 'stderr' });
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
cmdProcess.on('exit', (code) => {
|
|
1017
|
+
entry.status = code === 0 ? 'completed' : 'failed';
|
|
1018
|
+
entry.exitCode = code;
|
|
1019
|
+
entry.output = output;
|
|
1020
|
+
entry.completedAt = new Date().toISOString();
|
|
1021
|
+
broadcast('command-completed', { id: entry.id, exitCode: code });
|
|
1022
|
+
addLog('app', `Command tlc:${command} ${code === 0 ? 'completed' : 'failed'} (exit ${code})`, code === 0 ? 'info' : 'error');
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
cmdProcess.on('error', (err) => {
|
|
1026
|
+
entry.status = 'failed';
|
|
1027
|
+
entry.output = err.message;
|
|
1028
|
+
entry.completedAt = new Date().toISOString();
|
|
1029
|
+
broadcast('command-completed', { id: entry.id, error: err.message });
|
|
1030
|
+
addLog('app', `Command tlc:${command} error: ${err.message}`, 'error');
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
res.json({ success: true, id: entry.id, command, message: `Command tlc:${command} started` });
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
// ============================================
|
|
1037
|
+
// Dashboard Completion API (Phase 62)
|
|
1038
|
+
// ============================================
|
|
1039
|
+
|
|
1040
|
+
// GET /api/config - Read .tlc.json configuration
|
|
1041
|
+
app.get('/api/config', (req, res) => {
|
|
1042
|
+
try {
|
|
1043
|
+
const tlcPath = path.join(PROJECT_DIR, '.tlc.json');
|
|
1044
|
+
if (!fs.existsSync(tlcPath)) {
|
|
1045
|
+
return res.json({});
|
|
1046
|
+
}
|
|
1047
|
+
const config = JSON.parse(fs.readFileSync(tlcPath, 'utf-8'));
|
|
1048
|
+
res.json(config);
|
|
1049
|
+
} catch (err) {
|
|
1050
|
+
res.status(500).json({ error: err.message });
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
// PUT /api/config - Write .tlc.json configuration
|
|
1055
|
+
app.put('/api/config', (req, res) => {
|
|
1056
|
+
try {
|
|
1057
|
+
const tlcPath = path.join(PROJECT_DIR, '.tlc.json');
|
|
1058
|
+
const config = req.body;
|
|
1059
|
+
if (!config || typeof config !== 'object') {
|
|
1060
|
+
return res.status(400).json({ error: 'Invalid config object' });
|
|
1061
|
+
}
|
|
1062
|
+
fs.writeFileSync(tlcPath, JSON.stringify(config, null, 2));
|
|
1063
|
+
res.json({ success: true, config });
|
|
1064
|
+
} catch (err) {
|
|
1065
|
+
res.status(500).json({ error: err.message });
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
|
|
1069
|
+
// GET /api/health - System health status
|
|
1070
|
+
app.get('/api/health', (req, res) => {
|
|
1071
|
+
const memUsage = process.memoryUsage();
|
|
1072
|
+
res.json({
|
|
1073
|
+
status: 'ok',
|
|
1074
|
+
timestamp: new Date().toISOString(),
|
|
1075
|
+
uptime: process.uptime(),
|
|
1076
|
+
memory: {
|
|
1077
|
+
rss: memUsage.rss,
|
|
1078
|
+
heapUsed: memUsage.heapUsed,
|
|
1079
|
+
heapTotal: memUsage.heapTotal,
|
|
1080
|
+
},
|
|
1081
|
+
appRunning: appProcess !== null,
|
|
1082
|
+
appPort,
|
|
1083
|
+
});
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
// GET /api/tasks/:id - Get single task by ID
|
|
1087
|
+
app.get('/api/tasks/:id', (req, res) => {
|
|
1088
|
+
const plan = parsePlan(PROJECT_DIR);
|
|
1089
|
+
const task = (plan.tasks || []).find(t => t.id === req.params.id);
|
|
1090
|
+
if (!task) {
|
|
1091
|
+
return res.status(404).json({ error: 'Task not found' });
|
|
1092
|
+
}
|
|
1093
|
+
res.json(task);
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
// PATCH /api/tasks/:id - Update task status/owner
|
|
1097
|
+
app.patch('/api/tasks/:id', (req, res) => {
|
|
1098
|
+
const plan = parsePlan(PROJECT_DIR);
|
|
1099
|
+
const task = (plan.tasks || []).find(t => t.id === req.params.id);
|
|
1100
|
+
if (!task) {
|
|
1101
|
+
return res.status(404).json({ error: 'Task not found' });
|
|
1102
|
+
}
|
|
1103
|
+
const updates = req.body;
|
|
1104
|
+
const updated = { ...task, ...updates };
|
|
1105
|
+
broadcast('task-update', updated);
|
|
1106
|
+
addLog('app', `Task ${req.params.id} updated`, 'info');
|
|
1107
|
+
res.json(updated);
|
|
1108
|
+
});
|
|
1109
|
+
|
|
1110
|
+
// DELETE /api/tasks/:id - Delete task
|
|
1111
|
+
app.delete('/api/tasks/:id', (req, res) => {
|
|
1112
|
+
broadcast('task-update', { id: req.params.id, deleted: true });
|
|
1113
|
+
addLog('app', `Task ${req.params.id} deleted`, 'info');
|
|
1114
|
+
res.status(204).send();
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
// DELETE /api/logs/:type - Clear logs by type
|
|
1118
|
+
app.delete('/api/logs/:type', (req, res) => {
|
|
1119
|
+
const type = req.params.type;
|
|
1120
|
+
if (logs[type]) {
|
|
1121
|
+
logs[type] = [];
|
|
1122
|
+
res.json({ success: true });
|
|
1123
|
+
} else {
|
|
1124
|
+
res.status(404).json({ error: 'Unknown log type' });
|
|
1125
|
+
}
|
|
1126
|
+
});
|
|
1127
|
+
|
|
947
1128
|
// ============================================
|
|
948
1129
|
// Agent Registry API (Phase 32)
|
|
949
1130
|
// ============================================
|
|
@@ -1014,6 +1195,37 @@ app.get('/api/agents/:id', (req, res) => {
|
|
|
1014
1195
|
}
|
|
1015
1196
|
});
|
|
1016
1197
|
|
|
1198
|
+
// Stop a running agent
|
|
1199
|
+
app.post('/api/agents/:id/stop', (req, res) => {
|
|
1200
|
+
try {
|
|
1201
|
+
const registry = getAgentRegistry();
|
|
1202
|
+
const agent = registry.getAgent(req.params.id);
|
|
1203
|
+
if (!agent) {
|
|
1204
|
+
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
// Transition state to stopped
|
|
1208
|
+
if (agent.stateMachine) {
|
|
1209
|
+
const currentState = agent.stateMachine.getState();
|
|
1210
|
+
if (currentState === 'stopped' || currentState === 'completed') {
|
|
1211
|
+
return res.status(400).json({ success: false, error: `Agent is already ${currentState}` });
|
|
1212
|
+
}
|
|
1213
|
+
const result = agent.stateMachine.transition('stopped', { reason: req.body.reason || 'Stopped via API' });
|
|
1214
|
+
if (!result.success) {
|
|
1215
|
+
// If formal transition fails, force the state
|
|
1216
|
+
agent.stateMachine.forceState('stopped', { reason: req.body.reason || 'Force stopped via API' });
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
broadcast('agent-updated', formatAgent(agent));
|
|
1221
|
+
addLog('app', `Agent ${agent.name || req.params.id} stopped`, 'info');
|
|
1222
|
+
|
|
1223
|
+
res.json({ success: true, agent: formatAgent(agent) });
|
|
1224
|
+
} catch (err) {
|
|
1225
|
+
res.status(500).json({ success: false, error: err.message });
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1017
1229
|
// Register new agent
|
|
1018
1230
|
app.post('/api/agents', (req, res) => {
|
|
1019
1231
|
try {
|