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.
- package/.claude/commands/tlc/build.md +114 -21
- package/.claude/commands/tlc/issues.md +46 -0
- package/.claude/commands/tlc/plan.md +43 -0
- package/.claude/commands/tlc/review.md +4 -0
- package/package.json +1 -1
- package/server/lib/github/config.js +458 -0
- package/server/lib/github/config.test.js +385 -0
- package/server/lib/github/gh-client.js +303 -0
- package/server/lib/github/gh-client.test.js +499 -0
- package/server/lib/github/gh-projects.js +594 -0
- package/server/lib/github/gh-projects.test.js +583 -0
- package/server/lib/github/index.js +19 -0
- package/server/lib/github/plan-sync.js +456 -0
- package/server/lib/github/plan-sync.test.js +805 -0
|
@@ -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
|
|
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 (
|
|
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 →
|
|
363
|
-
Task 3: Write migration scripts →
|
|
364
|
-
Task 4: Create seed data →
|
|
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
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
|
385
|
-
|
|
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 [
|
|
401
|
-
Agent 3: Task 3 - Write migration scripts [
|
|
402
|
-
Agent 4: Task 4 - Create seed data [
|
|
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="
|
|
420
|
-
Task(description="Agent 3: Task 3", prompt="...", subagent_type="general-purpose", model="
|
|
421
|
-
Task(description="Agent 4: Task 4", prompt="...", subagent_type="general-purpose", model="
|
|
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 |
|
|
453
|
-
| g7h8i9 | Migrations |
|
|
454
|
-
| j0k1l2 | Seed Data |
|
|
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
|
|