tlc-claude-code 2.4.9 → 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.
@@ -57,6 +57,11 @@ Run `auditProject(projectPath)` which executes:
57
57
  | **Secrets in Responses** | Response objects containing fields named `apiKey`, `secret`, `token`, `password` | error |
58
58
  | **Manual Instantiation** | `new .*Provider(` or `new .*Service(` in application code | warning |
59
59
  | **Missing Ownership Check** | Controller methods with `@Param('id')` but no ownership/authorization guard | warning |
60
+ | **Empty Catch Blocks** | `catch (e) {}` or `catch { }` with no logging or rethrow | error |
61
+ | **Silent Error Returns** | `catch (e) { return null/undefined/false }` without logging | error |
62
+ | **Missing Error Handling** | External calls (HTTP, DB, Docker, file I/O, spawn) without try/catch | error |
63
+ | **No Connection Logging** | DB/Redis/WebSocket/Docker connections without connect/disconnect/error logging | warning |
64
+ | **Bare console.log Errors** | `console.log(error)` instead of structured logging with context | warning |
60
65
 
61
66
  ### Step 3: Generate Report
62
67
 
@@ -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
 
@@ -210,19 +214,25 @@ function processOrder(order) {
210
214
  - **JSDoc for public API**: Parameters, returns, throws, examples.
211
215
  - **No obvious comments**: `// increment i` before `i++` is noise.
212
216
 
213
- ### Error Handling
217
+ ### Error Handling (Zero Silent Failures)
218
+ - **Every catch block must be visible**: Log with context OR rethrow. No exceptions.
214
219
  - **Specific error types**: `UserNotFoundError` not generic `Error`.
215
- - **Actionable messages**: "User 'abc123' not found" not "Not found".
216
- - **Don't swallow errors**: Log or rethrow, never empty catch blocks.
220
+ - **Actionable messages**: "User 'abc123' not found in database 'users'" not "Not found".
221
+ - **Don't swallow errors**: Log or rethrow, never empty catch blocks. This is the #1 cause of production bugs.
217
222
  - **Error boundaries**: Catch at appropriate level, not everywhere.
218
223
  - **User vs developer errors**: Different messages for each audience.
219
-
220
- ### Logging Standards
221
- - **Structured logging**: JSON format with consistent fields.
222
- - **Log levels**: ERROR (failures), WARN (concerning), INFO (business events), DEBUG (troubleshooting).
223
- - **Context**: Include request ID, user ID, relevant entity IDs.
224
+ - **External calls MUST have error handling**: HTTP, DB, Docker, file I/O, CLI spawn — every one gets try/catch with logging.
225
+
226
+ ### Observability (CRITICAL silent failures are unacceptable)
227
+ - **No empty catch blocks**: Every `catch` must log with context OR rethrow. `catch (e) {}` is never acceptable.
228
+ - **No silent returns**: `catch (e) { return null }` without logging is a bug. Log first, then return.
229
+ - **Structured logging**: JSON format: `{ level, message, context, error, timestamp }`.
230
+ - **Log levels**: ERROR (failures needing attention), WARN (degraded but working), INFO (business events), DEBUG (troubleshooting).
231
+ - **Context in every log**: Include what operation failed, what inputs caused it, what the caller should know.
232
+ - **Connection state logging**: Every external connection (DB, Redis, Docker, WebSocket, HTTP client, queue) must log: connect, disconnect, reconnect, and failure. Connection state changes are INFO level, not DEBUG.
233
+ - **Startup health**: Log the state of every dependency on startup. "Connected to Postgres", "Docker socket accessible", "Redis: connection refused". Never start silently.
224
234
  - **No sensitive data**: Never log passwords, tokens, PII.
225
- - **Performance**: Don't log in tight loops.
235
+ - **Performance**: Don't log in tight loops. Do log every error, even in loops.
226
236
 
227
237
  ### Performance Awareness
228
238
  - **O(n) thinking**: Know the complexity of your algorithms. Avoid nested loops on large datasets.
@@ -270,8 +280,7 @@ This is the core TLC command. Tests before code, one task at a time.
270
280
  ```
271
281
  /tlc:build <phase_number>
272
282
  /tlc:build <phase_number> --sequential # Force sequential mode
273
- /tlc:build <phase_number> --model sonnet # Force all agents to use sonnet
274
- /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)
275
284
  /tlc:build <phase_number> --max-turns 30 # Limit agent execution length
276
285
  /tlc:build <phase_number> --agents 5 # Limit parallel agents to 5
277
286
  ```
@@ -327,7 +336,7 @@ After loading plans, analyze task dependencies and available providers to pick t
327
336
  |-----------|----------|--------------|
328
337
  | 2+ independent tasks, Claude + Codex available, tmux available | **Worktree + Tmux** | Each task gets own git worktree + tmux pane. Claude and Codex work simultaneously. |
329
338
  | 2+ independent tasks, Claude + Codex available, no tmux | **Worktree + Background** | Same but without tmux visibility. |
330
- | 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). |
331
340
  | All tasks have dependencies | **Sequential** | One task at a time, in dependency order. |
332
341
  | 1 task only | **Sequential** | Just build it. |
333
342
 
@@ -353,9 +362,9 @@ Providers: claude (1 available)
353
362
  Strategy: in-process agents (parallel)
354
363
 
355
364
  Task 1: Create user schema → opus (heavy)
356
- Task 2: Add validation helpers → sonnet (standard)
357
- Task 3: Write migration scripts → sonnet (standard)
358
- 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)
359
368
 
360
369
  Launching...
361
370
  ```
@@ -371,12 +380,13 @@ Strategy: sequential
371
380
  Starting Task 1...
372
381
  ```
373
382
 
374
- **Model auto-selection (in-process agent mode):**
375
- | Complexity | Model | When |
376
- |-----------|-------|------|
377
- | Heavy | opus | Architecture, multi-file features, security, auth, database |
378
- | Standard | sonnet | Normal implementation tasks (default) |
379
- | 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 |
380
390
 
381
391
  **Provider round-robin (worktree mode):**
382
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.
@@ -391,9 +401,9 @@ Tasks alternate between available providers. If Claude + Codex are both availabl
391
401
  Spawning 4 agents in parallel...
392
402
 
393
403
  Agent 1: Task 1 - Create user schema [opus]
394
- Agent 2: Task 2 - Add validation helpers [sonnet]
395
- Agent 3: Task 3 - Write migration scripts [sonnet]
396
- 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]
397
407
 
398
408
  [All agents spawned - working in background]
399
409
  ```
@@ -410,9 +420,9 @@ Agent 4: Task 4 - Create seed data [haiku]
410
420
 
411
421
  ```
412
422
  Task(description="Agent 1: Task 1", prompt="...", subagent_type="general-purpose", model="opus", max_turns=50, run_in_background=true)
413
- Task(description="Agent 2: Task 2", prompt="...", subagent_type="general-purpose", model="sonnet", max_turns=50, run_in_background=true)
414
- Task(description="Agent 3: Task 3", prompt="...", subagent_type="general-purpose", model="sonnet", max_turns=50, run_in_background=true)
415
- 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)
416
426
  ```
417
427
 
418
428
  **Live Progress Monitoring (TaskOutput):**
@@ -443,9 +453,9 @@ Display format:
443
453
  | Agent | Task | Model | Tests | Phase |
444
454
  |-------|------|-------|-------|-------|
445
455
  | a1b2c3 | User Schema | opus | 29 ✓ | committed |
446
- | d4e5f6 | Validation | sonnet | 18 ✓ | implementing |
447
- | g7h8i9 | Migrations | sonnet | - | writing-tests |
448
- | 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 |
449
459
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
450
460
  ```
451
461
 
@@ -781,6 +791,33 @@ Review the task's:
781
791
  - Acceptance criteria
782
792
  - Test cases (now written and failing)
783
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
+
784
821
  #### 7b. Implement the code
785
822
  Write the minimum code needed to pass the tests:
786
823
  - Create files specified in the task
@@ -822,6 +859,35 @@ This keeps the plan file as the single source of truth for task status. Do NOT w
822
859
 
823
860
  If in multi-user mode, also push to share progress with team.
824
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
+
825
891
  #### 7f. Move to next task
826
892
  Repeat 7a-7e for each task in the phase.
827
893
 
@@ -868,20 +934,22 @@ git diff --name-status main...HEAD
868
934
 
869
935
  **Checks performed:**
870
936
 
871
- 1. **Test Coverage** - Every implementation file has a test file
872
- 2. **TDD Compliance** - Commits show test-first pattern (score 50%)
873
- 3. **Security Scan** - No hardcoded secrets, eval(), innerHTML, etc.
874
- 4. **Authorization** - Every data-access endpoint has ownership checks, not just auth guards
875
- 5. **Secrets Exposure** - No API keys, tokens, or passwords returned in responses/HTML
876
- 6. **Config Hygiene** - No `process.env` outside config module; config validated at startup
877
- 7. **Output Encoding** - No unescaped `${...}` interpolation in HTML template strings
878
- 8. **Sensitive Data** - OTPs, reset tokens, session secrets are hashed before storage
879
- 9. **DI Compliance** - No manual `new Service()` / `new Provider()` in application code
880
- 10. **File Size** - No file exceeds 1000 lines (warning at 500+)
881
- 11. **Folder Size** - No folder exceeds 15 files (warning at 8+)
882
- 12. **Strict Typing** - No `any` types in new/changed files
883
- 13. **Return Types** - All exported functions have explicit return types
884
- 14. **Module Structure** - Files grouped by domain entity, not by type
937
+ 1. **Silent Failure Scan (CRITICAL)** - No empty catch blocks. Every catch must log with context OR rethrow. `catch (e) {}` and `catch (e) { return null }` without logging are **auto-rejected**. Every external call (HTTP, DB, Docker, CLI, file I/O) must have error handling that produces an observable signal (log, metric, or rethrow).
938
+ 2. **Error Logging** - Every error path must use structured logging: `{ level, message, context, error }`. No bare `console.log` for errors — use a logger with level + context. No `console.error` without identifying WHAT failed and WHERE.
939
+ 3. **Test Coverage** - Every implementation file has a test file
940
+ 4. **TDD Compliance** - Commits show test-first pattern (score 50%)
941
+ 5. **Security Scan** - No hardcoded secrets, eval(), innerHTML, etc.
942
+ 6. **Authorization** - Every data-access endpoint has ownership checks, not just auth guards
943
+ 7. **Secrets Exposure** - No API keys, tokens, or passwords returned in responses/HTML
944
+ 8. **Config Hygiene** - No `process.env` outside config module; config validated at startup
945
+ 9. **Output Encoding** - No unescaped `${...}` interpolation in HTML template strings
946
+ 10. **Sensitive Data** - OTPs, reset tokens, session secrets are hashed before storage
947
+ 11. **DI Compliance** - No manual `new Service()` / `new Provider()` in application code
948
+ 12. **File Size** - No file exceeds 1000 lines (warning at 500+)
949
+ 13. **Folder Size** - No folder exceeds 15 files (warning at 8+)
950
+ 14. **Strict Typing** - No `any` types in new/changed files
951
+ 15. **Return Types** - All exported functions have explicit return types
952
+ 16. **Module Structure** - Files grouped by domain entity, not by type
885
953
 
886
954
  **Review output:**
887
955
 
@@ -1000,6 +1068,39 @@ gh pr create \
1000
1068
  {test runner summary}"
1001
1069
  ```
1002
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
+
1003
1104
  **The PR is the deliverable, not the commits.** CI runs on the PR. Merge when green.
1004
1105
 
1005
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: