tlc-claude-code 2.4.10 → 2.5.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.
@@ -96,6 +96,10 @@ process.stdout.write(JSON.stringify(result));" 2>/dev/null
96
96
  - Execute inline (Claude) AND dispatch each external provider via `dispatch` from `orchestration/cli-dispatch`
97
97
  - After each provider completes, capture its output with `captureFromProvider` from `capture`
98
98
  - Collect and merge results
99
+ - **CRITICAL: If a provider fails (wrong model, auth error, CLI missing), do NOT silently fall back to single-provider.** Instead:
100
+ 1. Check `.tlc/.router-state.json` for the provider's actual available model and retry
101
+ 2. If retry fails, ask the user: "[Provider] failed: [reason]. Run with [other provider] only, or fix and retry?"
102
+ 3. Never say "proceeding with X-only" without user consent
99
103
 
100
104
  **Override:** Pass `--model <name>` to route this specific run to a different model.
101
105
 
@@ -276,8 +280,7 @@ This is the core TLC command. Tests before code, one task at a time.
276
280
  ```
277
281
  /tlc:build <phase_number>
278
282
  /tlc:build <phase_number> --sequential # Force sequential mode
279
- /tlc:build <phase_number> --model sonnet # Force all agents to use sonnet
280
- /tlc:build <phase_number> --model haiku # Force all agents to use haiku (fast/cheap)
283
+ /tlc:build <phase_number> --model opus # All agents use opus (this is the default)
281
284
  /tlc:build <phase_number> --max-turns 30 # Limit agent execution length
282
285
  /tlc:build <phase_number> --agents 5 # Limit parallel agents to 5
283
286
  ```
@@ -333,7 +336,7 @@ After loading plans, analyze task dependencies and available providers to pick t
333
336
  |-----------|----------|--------------|
334
337
  | 2+ independent tasks, Claude + Codex available, tmux available | **Worktree + Tmux** | Each task gets own git worktree + tmux pane. Claude and Codex work simultaneously. |
335
338
  | 2+ independent tasks, Claude + Codex available, no tmux | **Worktree + Background** | Same but without tmux visibility. |
336
- | 2+ independent tasks, single provider only | **In-process agents** | Agent tool spawns parallel sub-agents (sonnet/haiku by complexity). |
339
+ | 2+ independent tasks, single provider only | **In-process agents** | Agent tool spawns parallel sub-agents (all opus). |
337
340
  | All tasks have dependencies | **Sequential** | One task at a time, in dependency order. |
338
341
  | 1 task only | **Sequential** | Just build it. |
339
342
 
@@ -359,9 +362,9 @@ Providers: claude (1 available)
359
362
  Strategy: in-process agents (parallel)
360
363
 
361
364
  Task 1: Create user schema → opus (heavy)
362
- Task 2: Add validation helpers → sonnet (standard)
363
- Task 3: Write migration scripts → sonnet (standard)
364
- Task 4: Create seed data → haiku (light)
365
+ Task 2: Add validation helpers → opus (standard)
366
+ Task 3: Write migration scripts → opus (standard)
367
+ Task 4: Create seed data → opus (light)
365
368
 
366
369
  Launching...
367
370
  ```
@@ -377,12 +380,13 @@ Strategy: sequential
377
380
  Starting Task 1...
378
381
  ```
379
382
 
380
- **Model auto-selection (in-process agent mode):**
381
- | Complexity | Model | When |
382
- |-----------|-------|------|
383
- | Heavy | opus | Architecture, multi-file features, security, auth, database |
384
- | Standard | sonnet | Normal implementation tasks (default) |
385
- | Light | haiku | Config, boilerplate, DTOs, enums, constants, seed data |
383
+ **Model selection (in-process agent mode):**
384
+
385
+ All agents use **opus**. No opus, no opus. Cost is not a concern — quality is.
386
+
387
+ | Task | Model |
388
+ |------|-------|
389
+ | Any | opus |
386
390
 
387
391
  **Provider round-robin (worktree mode):**
388
392
  Tasks alternate between available providers. If Claude + Codex are both available, odd tasks go to Claude, even to Codex. Respects router state — never dispatches to unavailable providers.
@@ -397,9 +401,9 @@ Tasks alternate between available providers. If Claude + Codex are both availabl
397
401
  Spawning 4 agents in parallel...
398
402
 
399
403
  Agent 1: Task 1 - Create user schema [opus]
400
- Agent 2: Task 2 - Add validation helpers [sonnet]
401
- Agent 3: Task 3 - Write migration scripts [sonnet]
402
- Agent 4: Task 4 - Create seed data [haiku]
404
+ Agent 2: Task 2 - Add validation helpers [opus]
405
+ Agent 3: Task 3 - Write migration scripts [opus]
406
+ Agent 4: Task 4 - Create seed data [opus]
403
407
 
404
408
  [All agents spawned - working in background]
405
409
  ```
@@ -416,9 +420,9 @@ Agent 4: Task 4 - Create seed data [haiku]
416
420
 
417
421
  ```
418
422
  Task(description="Agent 1: Task 1", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
419
- Task(description="Agent 2: Task 2", prompt="...", subagent_type="general-purpose", model="sonnet", max_turns=50, run_in_background=true)
420
- Task(description="Agent 3: Task 3", prompt="...", subagent_type="general-purpose", model="sonnet", max_turns=50, run_in_background=true)
421
- Task(description="Agent 4: Task 4", prompt="...", subagent_type="general-purpose", model="haiku", max_turns=50, run_in_background=true)
423
+ Task(description="Agent 2: Task 2", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
424
+ Task(description="Agent 3: Task 3", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
425
+ Task(description="Agent 4: Task 4", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
422
426
  ```
423
427
 
424
428
  **Live Progress Monitoring (TaskOutput):**
@@ -449,9 +453,9 @@ Display format:
449
453
  | Agent | Task | Model | Tests | Phase |
450
454
  |-------|------|-------|-------|-------|
451
455
  | a1b2c3 | User Schema | opus | 29 ✓ | committed |
452
- | d4e5f6 | Validation | sonnet | 18 ✓ | implementing |
453
- | g7h8i9 | Migrations | sonnet | - | writing-tests |
454
- | j0k1l2 | Seed Data | haiku | 5 ✓ | committed |
456
+ | d4e5f6 | Validation | opus | 18 ✓ | implementing |
457
+ | g7h8i9 | Migrations | opus | - | writing-tests |
458
+ | j0k1l2 | Seed Data | opus | 5 ✓ | committed |
455
459
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
456
460
  ```
457
461
 
@@ -787,6 +791,33 @@ Review the task's:
787
791
  - Acceptance criteria
788
792
  - Test cases (now written and failing)
789
793
 
794
+ #### 7a-gh. GitHub: Mark In Progress
795
+
796
+ If GitHub sync is enabled, update the linked GitHub issue for this task:
797
+
798
+ ```bash
799
+ node -e "
800
+ const path = require('path');
801
+ const fs = require('fs');
802
+ const { execSync } = require('child_process');
803
+ let gh;
804
+ try { gh = require('tlc-claude-code/server/lib/github'); }
805
+ catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
806
+ try {
807
+ const cfg = gh.loadGitHubConfig(process.cwd(), { fs }).config;
808
+ if (!cfg) process.exit(0);
809
+ const exec = (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] });
810
+ const user = execSync('git config user.name', { encoding: 'utf-8' }).trim();
811
+ const issueNumber = process.argv[1];
812
+ if (issueNumber) {
813
+ gh.assignIssue({ owner: cfg.owner, repo: cfg.repo, number: Number(issueNumber), assignees: [user], exec });
814
+ }
815
+ } catch (e) { console.error('[TLC] GitHub status update failed:', e.message); }
816
+ " "{issue_number}" 2>/dev/null
817
+ ```
818
+
819
+ If no issue is linked to this task, skip silently. Failures warn but never block.
820
+
790
821
  #### 7b. Implement the code
791
822
  Write the minimum code needed to pass the tests:
792
823
  - Create files specified in the task
@@ -828,6 +859,35 @@ This keeps the plan file as the single source of truth for task status. Do NOT w
828
859
 
829
860
  If in multi-user mode, also push to share progress with team.
830
861
 
862
+ #### 7e-gh. GitHub: Mark Done
863
+
864
+ If GitHub sync is enabled, update the linked issue status after marking the task `[x]`:
865
+
866
+ ```bash
867
+ node -e "
868
+ const path = require('path');
869
+ const fs = require('fs');
870
+ const { execSync } = require('child_process');
871
+ let gh;
872
+ try { gh = require('tlc-claude-code/server/lib/github'); }
873
+ catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
874
+ try {
875
+ const cfg = gh.loadGitHubConfig(process.cwd(), { fs }).config;
876
+ if (!cfg) process.exit(0);
877
+ const planPath = process.argv[1];
878
+ const r = gh.updateTaskStatuses({
879
+ planPath,
880
+ config: cfg,
881
+ ghClient: { exec: (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] }) },
882
+ fs
883
+ });
884
+ console.log(JSON.stringify(r));
885
+ } catch (e) { console.error('[TLC] GitHub status sync failed:', e.message); }
886
+ " ".planning/phases/{N}-PLAN.md" 2>/dev/null
887
+ ```
888
+
889
+ Failures warn but never block the build.
890
+
831
891
  #### 7f. Move to next task
832
892
  Repeat 7a-7e for each task in the phase.
833
893
 
@@ -1008,6 +1068,39 @@ gh pr create \
1008
1068
  {test runner summary}"
1009
1069
  ```
1010
1070
 
1071
+ #### Step 11-gh: GitHub: Link PR to Project
1072
+
1073
+ If GitHub sync is enabled, after the PR is created:
1074
+
1075
+ 1. Add `Closes #N` references to the PR body for all completed task issues (parse `<!-- gh:N -->` markers from PLAN.md)
1076
+ 2. Add the PR to the GitHub project board if configured
1077
+ 3. Set the phase issue Status to "In review"
1078
+
1079
+ ```bash
1080
+ node -e "
1081
+ const path = require('path');
1082
+ const fs = require('fs');
1083
+ const { execSync } = require('child_process');
1084
+ let gh;
1085
+ try { gh = require('tlc-claude-code/server/lib/github'); }
1086
+ catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
1087
+ try {
1088
+ const cfg = gh.loadGitHubConfig(process.cwd(), { fs }).config;
1089
+ if (!cfg) process.exit(0);
1090
+ const exec = (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] });
1091
+ const prNumber = Number(process.argv[1]);
1092
+ const planPath = process.argv[2];
1093
+ const content = fs.readFileSync(planPath, 'utf-8');
1094
+ const issueRefs = [...content.matchAll(/<!-- gh:(\d+) -->/g)].map(m => Number(m[1]));
1095
+ if (issueRefs.length > 0) {
1096
+ gh.linkPrToIssue({ ...cfg, prNumber, issueNumbers: issueRefs, exec });
1097
+ }
1098
+ } catch (e) { console.error('[TLC] GitHub PR link failed:', e.message); }
1099
+ " "{pr_number}" ".planning/phases/{N}-PLAN.md" 2>/dev/null
1100
+ ```
1101
+
1102
+ Failures warn but never block.
1103
+
1011
1104
  **The PR is the deliverable, not the commits.** CI runs on the PR. Merge when green.
1012
1105
 
1013
1106
  **If the user has already been working on main** (e.g., first time using branching): push main directly but note that future phases MUST use branches.
@@ -28,6 +28,26 @@ Commands:
28
28
 
29
29
  ## Setup
30
30
 
31
+ ### Implementation
32
+
33
+ Setup uses `server/lib/github/config.js` to auto-detect the repo and configure sync:
34
+
35
+ ```bash
36
+ node -e "
37
+ const path = require('path');
38
+ const fs = require('fs');
39
+ const { execSync } = require('child_process');
40
+ let gh;
41
+ try { gh = require('tlc-claude-code/server/lib/github'); }
42
+ catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
43
+ const exec = (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] });
44
+ const suggestion = gh.detectAndSuggestConfig({ ghClient: { detectRepo: () => gh.detectRepo({ exec }), checkAuth: () => gh.checkAuth({ exec }) }, ghProjects: null, projectDir: process.cwd(), fs });
45
+ console.log(JSON.stringify(suggestion, null, 2));
46
+ // If user approves, write config:
47
+ // gh.writeGitHubConfig(process.cwd(), suggestion.config, { fs });
48
+ " 2>/dev/null
49
+ ```
50
+
31
51
  ### GitHub Issues
32
52
 
33
53
  ```
@@ -129,6 +149,32 @@ In `.tlc.json`:
129
149
 
130
150
  ## Sync Tasks
131
151
 
152
+ ### Implementation
153
+
154
+ The sync is powered by `server/lib/github/plan-sync.js`:
155
+
156
+ ```bash
157
+ node -e "
158
+ const path = require('path');
159
+ const fs = require('fs');
160
+ const { execSync } = require('child_process');
161
+ let gh;
162
+ try { gh = require('tlc-claude-code/server/lib/github'); }
163
+ catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
164
+ const cfg = gh.loadGitHubConfig(process.cwd(), { fs }).config;
165
+ if (!cfg) { console.error('No GitHub config. Run /tlc:issues setup first.'); process.exit(1); }
166
+ const planPath = process.argv[1];
167
+ const r = gh.syncPlan({
168
+ planPath,
169
+ config: cfg,
170
+ ghClient: { exec: (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] }) },
171
+ ghProjects: null,
172
+ fs
173
+ });
174
+ console.log(JSON.stringify(r, null, 2));
175
+ " ".planning/phases/{N}-PLAN.md" 2>/dev/null
176
+ ```
177
+
132
178
  ### Export to Issue Tracker
133
179
 
134
180
  ```
@@ -296,6 +296,49 @@ Create `.planning/phases/{N}-PLAN.md`:
296
296
  - File size warnings: {None / list any files projected to exceed 1000 lines and planned split}
297
297
  ```
298
298
 
299
+ ### Step 4b: GitHub Sync (automatic)
300
+
301
+ After writing the plan file, check if GitHub sync is enabled and sync tasks to GitHub Issues:
302
+
303
+ ```bash
304
+ node -e "
305
+ const path = require('path');
306
+ let gh;
307
+ try { gh = require('tlc-claude-code/server/lib/github'); }
308
+ catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
309
+ console.log(gh.isGitHubEnabled(process.cwd()));
310
+ " 2>/dev/null
311
+ ```
312
+
313
+ If the result is `true`:
314
+
315
+ 1. Run the sync:
316
+ ```bash
317
+ node -e "
318
+ const path = require('path');
319
+ const fs = require('fs');
320
+ const { execSync } = require('child_process');
321
+ let gh;
322
+ try { gh = require('tlc-claude-code/server/lib/github'); }
323
+ catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
324
+ const config = gh.loadGitHubConfig(process.cwd(), { fs }).config;
325
+ if (!config) { console.log('GitHub sync: no config'); process.exit(0); }
326
+ const planPath = process.argv[1];
327
+ const r = gh.syncPlan({
328
+ planPath,
329
+ config,
330
+ ghClient: { exec: (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] }) },
331
+ ghProjects: null,
332
+ fs
333
+ });
334
+ console.log(JSON.stringify(r));
335
+ " ".planning/phases/{N}-PLAN.md" 2>/dev/null
336
+ ```
337
+ 2. Report results: "GitHub: Created N issues, updated M, linked to project board"
338
+ 3. If sync fails, warn but do not block: "GitHub sync failed: [reason]. Plan saved locally. Run `/tlc:issues sync` to retry."
339
+
340
+ If not enabled, skip silently (no message).
341
+
299
342
  ### Step 5: Review Plan
300
343
 
301
344
  Present plan summary:
@@ -94,6 +94,10 @@ process.stdout.write(JSON.stringify(result));" 2>/dev/null
94
94
  4. If `strategy` is `parallel`:
95
95
  - Execute inline (Claude) AND dispatch to CLI models simultaneously
96
96
  - Collect and merge results
97
+ - **CRITICAL: If a provider fails (wrong model, auth error, CLI missing), do NOT silently fall back to single-provider.** Instead:
98
+ 1. Check `.tlc/.router-state.json` for the provider's actual available model and retry
99
+ 2. If retry fails, ask the user: "[Provider] failed: [reason]. Run with [other provider] only, or fix and retry?"
100
+ 3. Never say "proceeding with X-only" without user consent
97
101
 
98
102
  **Override:** Pass `--model <name>` to route this specific run to a different model.
99
103
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tlc-claude-code",
3
- "version": "2.4.10",
3
+ "version": "2.5.0",
4
4
  "description": "TLC - Test Led Coding for Claude Code",
5
5
  "bin": {
6
6
  "tlc-claude-code": "./bin/install.js",