tlc-claude-code 2.4.10 → 2.6.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.
Files changed (86) hide show
  1. package/.claude/commands/tlc/autofix.md +34 -1
  2. package/.claude/commands/tlc/build.md +203 -27
  3. package/.claude/commands/tlc/ci.md +178 -414
  4. package/.claude/commands/tlc/coverage.md +34 -0
  5. package/.claude/commands/tlc/deploy.md +19 -6
  6. package/.claude/commands/tlc/discuss.md +34 -0
  7. package/.claude/commands/tlc/docs.md +35 -1
  8. package/.claude/commands/tlc/e2e.md +300 -0
  9. package/.claude/commands/tlc/edge-cases.md +35 -1
  10. package/.claude/commands/tlc/init.md +38 -8
  11. package/.claude/commands/tlc/issues.md +46 -0
  12. package/.claude/commands/tlc/new-project.md +46 -4
  13. package/.claude/commands/tlc/plan.md +76 -0
  14. package/.claude/commands/tlc/quick.md +33 -0
  15. package/.claude/commands/tlc/release.md +85 -135
  16. package/.claude/commands/tlc/restore.md +14 -0
  17. package/.claude/commands/tlc/review.md +80 -1
  18. package/.claude/commands/tlc/tlc.md +134 -0
  19. package/.claude/commands/tlc/verify.md +64 -65
  20. package/.claude/commands/tlc/watchci.md +10 -0
  21. package/.claude/hooks/tlc-block-tools.sh +13 -0
  22. package/.claude/hooks/tlc-session-init.sh +9 -0
  23. package/CODING-STANDARDS.md +35 -10
  24. package/package.json +1 -1
  25. package/server/lib/block-tools-hook.js +23 -0
  26. package/server/lib/e2e/acceptance-parser.js +132 -0
  27. package/server/lib/e2e/acceptance-parser.test.js +110 -0
  28. package/server/lib/e2e/framework-detector.js +47 -0
  29. package/server/lib/e2e/framework-detector.test.js +94 -0
  30. package/server/lib/e2e/log-assertions.js +107 -0
  31. package/server/lib/e2e/log-assertions.test.js +68 -0
  32. package/server/lib/e2e/test-generator.js +159 -0
  33. package/server/lib/e2e/test-generator.test.js +121 -0
  34. package/server/lib/e2e/verify-runner.js +191 -0
  35. package/server/lib/e2e/verify-runner.test.js +167 -0
  36. package/server/lib/github/config.js +458 -0
  37. package/server/lib/github/config.test.js +385 -0
  38. package/server/lib/github/gh-client.js +303 -0
  39. package/server/lib/github/gh-client.test.js +499 -0
  40. package/server/lib/github/gh-projects.js +594 -0
  41. package/server/lib/github/gh-projects.test.js +583 -0
  42. package/server/lib/github/index.js +19 -0
  43. package/server/lib/github/plan-sync.js +456 -0
  44. package/server/lib/github/plan-sync.test.js +805 -0
  45. package/server/lib/hooks/block-tools-hook.test.js +54 -0
  46. package/server/lib/orchestration/cli-dispatch.js +16 -1
  47. package/server/lib/orchestration/cli-dispatch.test.js +94 -8
  48. package/server/lib/orchestration/completion-checker.js +101 -0
  49. package/server/lib/orchestration/completion-checker.test.js +177 -0
  50. package/server/lib/orchestration/result-verifier.js +143 -0
  51. package/server/lib/orchestration/result-verifier.test.js +291 -0
  52. package/server/lib/orchestration/session-dispatcher.js +99 -0
  53. package/server/lib/orchestration/session-dispatcher.test.js +215 -0
  54. package/server/lib/orchestration/session-status.js +147 -0
  55. package/server/lib/orchestration/session-status.test.js +130 -0
  56. package/server/lib/release/agent-runner-updates.js +24 -0
  57. package/server/lib/release/agent-runner-updates.test.js +22 -0
  58. package/server/lib/release/changelog-generator.js +142 -0
  59. package/server/lib/release/changelog-generator.test.js +113 -0
  60. package/server/lib/release/ci-watcher.js +83 -0
  61. package/server/lib/release/ci-watcher.test.js +81 -0
  62. package/server/lib/release/health-checker.js +111 -0
  63. package/server/lib/release/health-checker.test.js +121 -0
  64. package/server/lib/release/release-pipeline.js +187 -0
  65. package/server/lib/release/release-pipeline.test.js +262 -0
  66. package/server/lib/release/version-bumper.js +183 -0
  67. package/server/lib/release/version-bumper.test.js +142 -0
  68. package/server/lib/routing-preamble.integration.test.js +12 -0
  69. package/server/lib/routing-preamble.js +13 -2
  70. package/server/lib/routing-preamble.test.js +49 -0
  71. package/server/lib/scaffolding/ci-detector.js +139 -0
  72. package/server/lib/scaffolding/ci-detector.test.js +198 -0
  73. package/server/lib/scaffolding/ci-scaffolder.js +347 -0
  74. package/server/lib/scaffolding/ci-scaffolder.test.js +157 -0
  75. package/server/lib/scaffolding/deploy-detector.js +135 -0
  76. package/server/lib/scaffolding/deploy-detector.test.js +106 -0
  77. package/server/lib/scaffolding/health-scaffold.js +374 -0
  78. package/server/lib/scaffolding/health-scaffold.test.js +99 -0
  79. package/server/lib/scaffolding/logger-scaffold.js +196 -0
  80. package/server/lib/scaffolding/logger-scaffold.test.js +146 -0
  81. package/server/lib/scaffolding/migration-detector.js +78 -0
  82. package/server/lib/scaffolding/migration-detector.test.js +127 -0
  83. package/server/lib/scaffolding/snapshot-manager.js +142 -0
  84. package/server/lib/scaffolding/snapshot-manager.test.js +225 -0
  85. package/server/lib/task-router-config.js +50 -20
  86. package/server/lib/task-router-config.test.js +29 -15
@@ -97,6 +97,39 @@ process.stdout.write(JSON.stringify(result));" 2>/dev/null
97
97
 
98
98
  **Override:** Pass `--model <name>` to route this specific run to a different model.
99
99
 
100
+ After `resolveRouting` returns, immediately write `.tlc/.plan-routing-active` with the active provider name from `models[0]` before doing any other plan work. Remove `.tlc/.plan-routing-active` on completion, cancellation, or failure cleanup.
101
+
102
+ **Routing decision:**
103
+ - If routing says external provider (`models[0] !== 'claude'`), follow **ONLY ORCHESTRATOR MODE** below.
104
+ - If routing says Claude (`models[0] === 'claude'`), follow **INLINE MODE** below.
105
+
106
+ ## ORCHESTRATOR MODE
107
+
108
+ Use this mode only when `models[0]` is **NOT** `claude`.
109
+
110
+ Claude does not execute the planning instructions inline in this path. Claude acts as the orchestrator for the routed provider run:
111
+
112
+ 1. Read the roadmap, current phase context, discussion artifacts, project constraints, and any existing research before dispatching planning work.
113
+ 2. Prepare a full planning prompt that asks the routed provider to research, decompose, and sequence the phase using the project's architectural and task-quality standards.
114
+ 3. Dispatch through the provider CLI path, using `codex exec` for Codex-style providers or `gemini -p` for Gemini-style providers, preferably via the shared dispatch layer when available.
115
+ 4. Verify the returned plan before accepting it:
116
+ - Confirm it addresses the correct phase and reflects the actual roadmap goal.
117
+ - Confirm tasks are actionable, testable, dependency-aware, and aligned with file/folder sizing constraints.
118
+ - Confirm the plan contains real implementation thinking rather than generic project-management filler.
119
+ 5. If the first routed result is incomplete, contradictory, or too vague to execute, send a focused follow-up planning dispatch that requests the missing structure instead of accepting it.
120
+ 6. Handle failures explicitly:
121
+ - If dispatch fails because of model mismatch, auth, or missing CLI, check `.tlc/.router-state.json` for the provider's actual available model and retry once with the corrected model.
122
+ - If retry still fails, stop and report the exact failure to the user. Do not silently fall back to Claude inline execution.
123
+ - If the provider produces a weak plan, reject it and re-dispatch with tighter scope and success criteria.
124
+ 7. When finished, display the routed planning result, persist any captured memory, remove `.tlc/.plan-routing-active`, and stop. Do **not** execute Inline Mode afterward.
125
+
126
+ ## INLINE MODE
127
+
128
+ Use this mode only when `models[0]` **IS** `claude`.
129
+
130
+ The existing planning instructions below are the Inline Mode instructions and should be followed unchanged.
131
+
132
+
100
133
  ## Architectural Standards
101
134
 
102
135
  **Plan like a principal engineer designing for scale:**
@@ -296,6 +329,49 @@ Create `.planning/phases/{N}-PLAN.md`:
296
329
  - File size warnings: {None / list any files projected to exceed 1000 lines and planned split}
297
330
  ```
298
331
 
332
+ ### Step 4b: GitHub Sync (automatic)
333
+
334
+ After writing the plan file, check if GitHub sync is enabled and sync tasks to GitHub Issues:
335
+
336
+ ```bash
337
+ node -e "
338
+ const path = require('path');
339
+ let gh;
340
+ try { gh = require('tlc-claude-code/server/lib/github'); }
341
+ catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
342
+ console.log(gh.isGitHubEnabled(process.cwd()));
343
+ " 2>/dev/null
344
+ ```
345
+
346
+ If the result is `true`:
347
+
348
+ 1. Run the sync:
349
+ ```bash
350
+ node -e "
351
+ const path = require('path');
352
+ const fs = require('fs');
353
+ const { execSync } = require('child_process');
354
+ let gh;
355
+ try { gh = require('tlc-claude-code/server/lib/github'); }
356
+ catch { gh = require(path.join(process.cwd(), 'server/lib/github')); }
357
+ const config = gh.loadGitHubConfig(process.cwd(), { fs }).config;
358
+ if (!config) { console.log('GitHub sync: no config'); process.exit(0); }
359
+ const planPath = process.argv[1];
360
+ const r = gh.syncPlan({
361
+ planPath,
362
+ config,
363
+ ghClient: { exec: (cmd) => execSync(cmd, { encoding: 'utf-8', stdio: ['pipe','pipe','pipe'] }) },
364
+ ghProjects: null,
365
+ fs
366
+ });
367
+ console.log(JSON.stringify(r));
368
+ " ".planning/phases/{N}-PLAN.md" 2>/dev/null
369
+ ```
370
+ 2. Report results: "GitHub: Created N issues, updated M, linked to project board"
371
+ 3. If sync fails, warn but do not block: "GitHub sync failed: [reason]. Plan saved locally. Run `/tlc:issues sync` to retry."
372
+
373
+ If not enabled, skip silently (no message).
374
+
299
375
  ### Step 5: Review Plan
300
376
 
301
377
  Present plan summary:
@@ -97,6 +97,39 @@ process.stdout.write(JSON.stringify(result));" 2>/dev/null
97
97
 
98
98
  **Override:** Pass `--model <name>` to route this specific run to a different model.
99
99
 
100
+ After `resolveRouting` returns, immediately write `.tlc/.quick-routing-active` with the active provider name from `models[0]` before doing any other quick work. Remove `.tlc/.quick-routing-active` on completion, cancellation, or failure cleanup.
101
+
102
+ **Routing decision:**
103
+ - If routing says external provider (`models[0] !== 'claude'`), follow **ONLY ORCHESTRATOR MODE** below.
104
+ - If routing says Claude (`models[0] === 'claude'`), follow **INLINE MODE** below.
105
+
106
+ ## ORCHESTRATOR MODE
107
+
108
+ Use this mode only when `models[0]` is **NOT** `claude`.
109
+
110
+ Claude does not execute the quick-task instructions inline in this path. Claude acts as the orchestrator for the routed provider run:
111
+
112
+ 1. Treat Claude as the orchestrator for a small, test-first routed task rather than executing the work inline.
113
+ 2. Gather the minimal project context needed for the quick task, then package a focused prompt with the task request, expected failing test, implementation scope, and verification target.
114
+ 3. Dispatch through the provider CLI path, using `codex exec` for Codex-style providers or `gemini -p` for Gemini-style providers, preferably via the shared dispatch layer when available.
115
+ 4. Verify the routed result before accepting it:
116
+ - Confirm a failing test or equivalent pre-change verification was part of the work when appropriate.
117
+ - Confirm the implementation actually addresses the user's quick task and stays scoped.
118
+ - Confirm post-change verification was run or the provider clearly explained why not.
119
+ 5. If the routed work is partial, off-scope, or skips verification, dispatch a narrow corrective follow-up instead of treating it as complete.
120
+ 6. Handle failures explicitly:
121
+ - If dispatch fails because of model mismatch, auth, or missing CLI, check `.tlc/.router-state.json` for the provider's actual available model and retry once with the corrected model.
122
+ - If retry still fails, stop and report the exact failure to the user. Do not silently fall back to Claude inline execution.
123
+ - If the provider returns incomplete code or missing tests, issue a targeted retry for the missing part.
124
+ 7. When finished, display the routed provider result, persist any captured memory, remove `.tlc/.quick-routing-active`, and stop. Do **not** execute Inline Mode afterward.
125
+
126
+ ## INLINE MODE
127
+
128
+ Use this mode only when `models[0]` **IS** `claude`.
129
+
130
+ The existing quick-task instructions below are the Inline Mode instructions and should be followed unchanged.
131
+
132
+
100
133
  ## Engineering Standards
101
134
 
102
135
  Even quick tasks follow senior engineer standards:
@@ -1,135 +1,85 @@
1
- # /tlc:release - Release a Task
2
-
3
- Release a task you claimed so others can work on it.
4
-
5
- ## Usage
6
-
7
- ```
8
- /tlc:release [task-number]
9
- ```
10
-
11
- ## When to Use
12
-
13
- - Blocked and can't continue
14
- - Switching to a different task
15
- - End of day, won't finish
16
- - Decided task approach needs rethinking
17
-
18
- ## Process
19
-
20
- ### Step 1: Identify User
21
-
22
- Get current user identity (same as `/tlc:claim`):
23
-
24
- ```bash
25
- if [ -n "$TLC_USER" ]; then
26
- user=$TLC_USER
27
- else
28
- user=$(git config user.name | tr '[:upper:]' '[:lower:]' | tr ' ' '-')
29
- fi
30
- ```
31
-
32
- ### Step 2: Find Your Claims
33
-
34
- Parse current phase PLAN.md for tasks claimed by you:
35
-
36
- ```
37
- Looking for tasks claimed by @alice...
38
-
39
- Your claimed tasks:
40
- 2. Add validation [>@alice]
41
- 5. Error handling [>@alice]
42
- ```
43
-
44
- ### Step 3: Select Task to Release
45
-
46
- If task-number provided:
47
- - Verify you own that task
48
- - If not yours, show error
49
-
50
- If not provided:
51
- - Show your claimed tasks
52
- - Prompt to select one
53
-
54
- ### Step 4: Update Task Marker
55
-
56
- Change from claimed to available:
57
-
58
- ```markdown
59
- ### Task 2: Add validation [>@alice]
60
- ```
61
-
62
- becomes:
63
-
64
- ```markdown
65
- ### Task 2: Add validation [ ]
66
- ```
67
-
68
- ### Step 5: Commit and Push
69
-
70
- ```bash
71
- git add .planning/phases/{N}-PLAN.md
72
- git commit -m "release: task {N} - {title} (@{user})"
73
- git push
74
- ```
75
-
76
- ## Example Session
77
-
78
- ```
79
- > /tlc:release
80
-
81
- Your claimed tasks in Phase 1:
82
- 2. Add validation [>@alice]
83
- 5. Error handling [>@alice]
84
-
85
- Release which task? [2/5]: 2
86
-
87
- Task 2: Add validation [>@alice] → [ ]
88
-
89
- ✓ Committed: release: task 2 - Add validation (@alice)
90
- ✓ Pushed
91
-
92
- Task 2 is now available for others.
93
- ```
94
-
95
- ## With Task Number
96
-
97
- ```
98
- > /tlc:release 2
99
-
100
- Task 2: Add validation [>@alice] → [ ]
101
-
102
- ✓ Committed: release: task 2 - Add validation (@alice)
103
- ✓ Pushed
104
-
105
- Task 2 is now available for others.
106
- ```
107
-
108
- ## Error Handling
109
-
110
- **Not your task:**
111
- ```
112
- Task 2 is claimed by @bob, not you.
113
- You can only release your own tasks.
114
-
115
- Your tasks: 5
116
- ```
117
-
118
- **Task not claimed:**
119
- ```
120
- Task 2 is not claimed (already available).
121
- Nothing to release.
122
- ```
123
-
124
- **No tasks claimed:**
125
- ```
126
- You have no claimed tasks in Phase 1.
127
- Use /tlc:claim to claim a task.
128
- ```
129
-
130
- ## Notes
131
-
132
- - Releasing doesn't undo any work you've done
133
- - Your commits remain in history
134
- - Another teammate can claim and continue where you left off
135
- - Consider adding a note to the task if you made partial progress
1
+ # /tlc:release - Run the Release Pipeline
2
+
3
+ Run the full TLC release pipeline for the current project.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ /tlc:release
9
+ /tlc:release --detached
10
+ ```
11
+
12
+ ## What This Does
13
+
14
+ 1. Watches CI for the release commit or branch state
15
+ 2. Bumps the version
16
+ 3. Updates the changelog
17
+ 4. Publishes the release
18
+ 5. Runs a mandatory post-release health check
19
+
20
+ This is the "prepare, publish, and verify the release" command.
21
+
22
+ ## Process
23
+
24
+ ### Step 1: Watch CI
25
+
26
+ - Confirm the branch is in a releasable state
27
+ - Watch the relevant CI run and stop on failure
28
+ - Do not continue to versioning or publish while CI is red
29
+
30
+ ### Step 2: Bump Version
31
+
32
+ - Determine the next version according to the project's release rules
33
+ - Update the version in the canonical project files
34
+ - Keep the version bump in the release commit
35
+
36
+ ### Step 3: Update Changelog
37
+
38
+ - Generate or update the changelog entry for the new version
39
+ - Summarize the user-facing changes included in the release
40
+ - Ensure the changelog and version stay in sync
41
+
42
+ ### Step 4: Publish
43
+
44
+ - Publish the package, artifact, or release target configured for the project
45
+ - Surface any publish failure immediately
46
+ - Do not mark the release complete until publish succeeds
47
+
48
+ ### Step 5: Run Health Check
49
+
50
+ - Read `deploy.healthUrl` from project configuration
51
+ - Block the release if `deploy.healthUrl` is missing
52
+ - Perform the health check after publish and fail the release if the deployed system is unhealthy
53
+
54
+ The health check is mandatory. If there is no `deploy.healthUrl`, stop and report that release verification cannot proceed.
55
+
56
+ ## Detached Mode
57
+
58
+ `/tlc:release --detached` dispatches the release pipeline to the local orchestrator instead of running inline.
59
+
60
+ - Dispatch via `POST` to the orchestrator at `http://localhost:3100`
61
+ - The orchestrator owns the long-running release session
62
+ - Return the dispatched session details to the user so they can monitor progress separately
63
+
64
+ Detached mode still requires the same release stages:
65
+
66
+ 1. CI watch
67
+ 2. Version bump
68
+ 3. Changelog update
69
+ 4. Publish
70
+ 5. Mandatory health check
71
+
72
+ Detached mode does not relax the health-check requirement. If `deploy.healthUrl` is not configured, the orchestrated release must block rather than skipping verification.
73
+
74
+ ## Guard Rails
75
+
76
+ - Do not publish while CI is failing or still running
77
+ - Do not skip the changelog for a versioned release
78
+ - Do not treat publish success as release success until the health check passes
79
+ - Do not continue when `deploy.healthUrl` is missing; the release blocks until health-check configuration exists
80
+
81
+ ## When to Use
82
+
83
+ - Shipping a new version after CI is green
84
+ - Running the standard TLC release flow end-to-end
85
+ - Handing a long-running release to the orchestrator with `--detached`
@@ -0,0 +1,14 @@
1
+ # /tlc:restore - Restore DB Snapshot
2
+
3
+ ## What This Does
4
+
5
+ Lists available snapshots and restores the selected one.
6
+
7
+ ## Process
8
+
9
+ 1. List snapshots from `.tlc/snapshots/` sorted newest first
10
+ 2. Show table: date, git ref, DB name, size
11
+ 3. Ask user to pick one
12
+ 4. Confirm: `Restore {snapshot}? This will overwrite the current database. (Y/n)`
13
+ 5. Run restore command based on DB type
14
+ 6. Report success or failure
@@ -94,9 +94,46 @@ 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
 
104
+ After `resolveRouting` returns, immediately write `.tlc/.review-routing-active` with the active provider name from `models[0]` before doing any other review work. Remove `.tlc/.review-routing-active` on completion, cancellation, or failure cleanup.
105
+
106
+ **Routing decision:**
107
+ - If routing says external provider (`models[0] !== 'claude'`), follow **ONLY ORCHESTRATOR MODE** below.
108
+ - If routing says Claude (`models[0] === 'claude'`), follow **INLINE MODE** below.
109
+
110
+ ## ORCHESTRATOR MODE
111
+
112
+ Use this mode only when `models[0]` is **NOT** `claude`.
113
+
114
+ Claude does not execute the review instructions inline in this path. Claude acts as the orchestrator for the routed provider run:
115
+
116
+ 1. Read the full review context, including project standards, changed files, base branch, router state, and any configured review providers.
117
+ 2. Package the exact review task for the routed provider and dispatch it through the provider CLI path, using `codex exec` for Codex-style providers or `gemini -p` for Gemini-style providers, preferably via the shared dispatch layer when available.
118
+ 3. Require the provider run to produce concrete review findings, approval state, coverage gaps, TDD observations, and security concerns aligned to the command intent.
119
+ 4. Verify the returned review before accepting it:
120
+ - Confirm the review actually inspected the current diff against the intended base.
121
+ - Confirm the findings are specific, actionable, and tied to the changed code rather than generic advice.
122
+ - Reject empty, shallow, or unverifiable review output and dispatch a focused retry.
123
+ 5. If the command is expected to fix issues in a review loop, orchestrate the fix dispatches as separate routed tasks, then re-run the external review until the result is clean or an explicit blocker remains.
124
+ 6. Handle failures explicitly:
125
+ - If dispatch fails because of model mismatch, auth, or missing CLI, check `.tlc/.router-state.json` for the provider's actual available model and retry once with the corrected model.
126
+ - If retry still fails, stop and report the exact failure to the user. Do not silently fall back to Claude inline execution.
127
+ - If the provider returns malformed or partial review output, issue a narrowed follow-up dispatch instead of continuing as if review succeeded.
128
+ 7. When finished, display the routed provider review result, persist any captured memory, remove `.tlc/.review-routing-active`, and stop. Do **not** execute Inline Mode afterward.
129
+
130
+ ## INLINE MODE
131
+
132
+ Use this mode only when `models[0]` **IS** `claude`.
133
+
134
+ The existing review instructions below are the Inline Mode instructions and should be followed unchanged.
135
+
136
+
100
137
  ## What This Does
101
138
 
102
139
  1. Compares current branch to main/master
@@ -236,7 +273,49 @@ Scan diff for common security issues:
236
273
 
237
274
  **Fail if:** Any HIGH severity issues found.
238
275
 
239
- ### Step 5b: Coding Standards Check
276
+ ### Step 5b: Run E2E Tests
277
+
278
+ Detect whether the project uses Playwright or a Supertest-based E2E setup, then execute the matching command:
279
+
280
+ ```bash
281
+ # Framework detection
282
+ if [ -f playwright.config.ts ] || [ -f playwright.config.js ] || [ -d tests/e2e ] || [ -d e2e ]; then
283
+ mkdir -p .tlc/screenshots
284
+ npx playwright test
285
+ elif rg -l "supertest" package.json test tests src >/dev/null 2>&1; then
286
+ mkdir -p .tlc/screenshots
287
+ npx supertest
288
+ else
289
+ echo "WARN: No E2E tests detected"
290
+ fi
291
+ ```
292
+
293
+ **Execution rules:**
294
+ - Use `npx playwright test` when Playwright is detected.
295
+ - Use `npx supertest` when a Supertest-based E2E framework is detected.
296
+ - If E2E tests fail, include the failure in the report as **CHANGES_REQUESTED**.
297
+ - If no E2E tests exist, add a warning to the report.
298
+ - On failure, save screenshots to `.tlc/screenshots/`.
299
+
300
+ **Report example:**
301
+ ```text
302
+ E2E Tests:
303
+ ✅ Playwright suite passed
304
+ ```
305
+
306
+ ```text
307
+ E2E Tests:
308
+ ❌ Playwright suite failed
309
+ Screenshots: .tlc/screenshots/
310
+ Verdict impact: CHANGES_REQUESTED
311
+ ```
312
+
313
+ ```text
314
+ E2E Tests:
315
+ ⚠️ No E2E tests detected
316
+ ```
317
+
318
+ ### Step 5c: Coding Standards Check
240
319
 
241
320
  Scan all changed/added files for coding standards violations:
242
321
 
@@ -2,6 +2,140 @@
2
2
 
3
3
  Detect the current TLC state in one pass, print a minimal status, and run the next safe action automatically.
4
4
 
5
+ ## Routing
6
+
7
+ This command supports multi-model routing via `~/.tlc/config.json`.
8
+
9
+ **Before executing this command:**
10
+
11
+ 1. Read routing config:
12
+ ```bash
13
+ node -e "const fs = require('fs');
14
+ const path = require('path');
15
+ const os = require('os');
16
+ function readJson(filePath, fileSystem) {
17
+ try {
18
+ return JSON.parse(fileSystem.readFileSync(filePath, 'utf8'));
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+ function loadPersonalConfig(options) {
24
+ const configPath = path.join(options.homeDir, '.tlc', 'config.json');
25
+ return readJson(configPath, options.fs);
26
+ }
27
+ function loadProjectOverride(options) {
28
+ const configPath = path.join(options.projectDir, '.tlc.json');
29
+ const data = readJson(configPath, options.fs);
30
+ return data && data.task_routing_override ? data.task_routing_override : null;
31
+ }
32
+ function resolveRouting(options) {
33
+ let models = ['claude'];
34
+ let strategy = 'single';
35
+ let source = 'shipped-defaults';
36
+ let providers;
37
+ const personal = loadPersonalConfig({ homeDir: options.homeDir, fs: options.fs });
38
+ if (personal) {
39
+ if (personal.model_providers) {
40
+ providers = personal.model_providers;
41
+ }
42
+ const personalRouting = personal.task_routing && personal.task_routing[options.command];
43
+ if (personalRouting) {
44
+ if (Array.isArray(personalRouting.models)) {
45
+ models = personalRouting.models.slice();
46
+ } else if (typeof personalRouting.model === 'string') {
47
+ models = [personalRouting.model];
48
+ }
49
+ if (personalRouting.strategy) {
50
+ strategy = personalRouting.strategy;
51
+ }
52
+ source = 'personal-config';
53
+ }
54
+ }
55
+ const projectOverride = loadProjectOverride({ projectDir: options.projectDir, fs: options.fs });
56
+ if (projectOverride) {
57
+ const overrideEntry = projectOverride[options.command];
58
+ if (overrideEntry) {
59
+ if (Array.isArray(overrideEntry.models)) {
60
+ models = overrideEntry.models.slice();
61
+ } else if (typeof overrideEntry.model === 'string') {
62
+ models = [overrideEntry.model];
63
+ }
64
+ if (overrideEntry.strategy) {
65
+ strategy = overrideEntry.strategy;
66
+ }
67
+ source = 'project-override';
68
+ }
69
+ }
70
+ if (options.flagModel) {
71
+ models = [options.flagModel];
72
+ strategy = 'single';
73
+ source = 'flag-override';
74
+ }
75
+ const result = { models, strategy, source };
76
+ if (providers) {
77
+ result.providers = providers;
78
+ }
79
+ return result;
80
+ }
81
+ const result = resolveRouting({ command: "tlc", flagModel: process.argv[1], projectDir: process.cwd(), homeDir: process.env.HOME || os.homedir(), fs });
82
+ process.stdout.write(JSON.stringify(result));" 2>/dev/null
83
+ ```
84
+
85
+ 2. If `models[0]` is NOT `claude` (i.e., routed to external model):
86
+ - Resolve orchestration modules with fallback: try `tlc-claude-code/server/lib/...` first, then `$PROJECT_DIR/server/lib/...`
87
+ - Build the full prompt with `buildFullPrompt` from `orchestration/prompt-builder`
88
+ - Dispatch to the target provider CLI through unified routing, using `codex exec` or `gemini -p` as appropriate
89
+ - Capture provider output for memory with `captureFromProvider` from `capture`, display the CLI output, and stop — do NOT execute the agent prompt below
90
+
91
+ 3. If `models[0]` IS `claude` (or no routing config exists):
92
+ - Execute the agent prompt below as normal (current behavior)
93
+
94
+ 4. If `strategy` is `parallel`:
95
+ - Resolve orchestration modules with fallback: try `tlc-claude-code/server/lib/...` first, then `$PROJECT_DIR/server/lib/...`
96
+ - Execute inline (Claude) AND dispatch each external provider through unified routing
97
+ - After each provider completes, capture its output with `captureFromProvider` from `capture`
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
103
+
104
+ **Override:** Pass `--model <name>` to route this specific run to a different model.
105
+
106
+ After `resolveRouting` returns, immediately write `.tlc/.tlc-routing-active` with the active provider name from `models[0]` before doing any other tlc work. Remove `.tlc/.tlc-routing-active` on completion, cancellation, or failure cleanup.
107
+
108
+ **Routing decision:**
109
+ - If routing says external provider (`models[0] !== 'claude'`), follow **ONLY ORCHESTRATOR MODE** below.
110
+ - If routing says Claude (`models[0] === 'claude'`), follow **INLINE MODE** below.
111
+
112
+ ## ORCHESTRATOR MODE
113
+
114
+ Use this mode only when `models[0]` is **NOT** `claude`.
115
+
116
+ Claude does not execute the auto-runner instructions inline in this path. Claude acts as the orchestrator for the routed provider run:
117
+
118
+ 1. Claude acts as the orchestrator for the routed `/tlc` auto-runner flow instead of executing the detection logic inline.
119
+ 2. Read the roadmap, phase artifacts, version context, and any routing or state files needed to determine the next safe action before dispatching.
120
+ 3. Package a prompt that asks the routed provider to perform the one-pass TLC state detection, choose the next non-destructive command, and produce the compact status output required by this command.
121
+ 4. Dispatch through the provider CLI path, using `codex exec` for Codex-style providers or `gemini -p` for Gemini-style providers, preferably via the shared dispatch layer when available.
122
+ 5. Verify the routed result before accepting it:
123
+ - Confirm it chose the active phase and next action from the actual repository state.
124
+ - Confirm it preserved the command's safety rules and did not auto-run destructive actions.
125
+ - Reject verbose, menu-driven, or repo-disconnected output that does not match `/tlc` behavior.
126
+ 6. If the routed result mis-detects state or picks the wrong next action, dispatch a corrective follow-up using the exact missing or conflicting evidence.
127
+ 7. Handle failures explicitly:
128
+ - If dispatch fails because of model mismatch, auth, or missing CLI, check `.tlc/.router-state.json` for the provider's actual available model and retry once with the corrected model.
129
+ - If retry still fails, stop and report the exact failure to the user. Do not silently fall back to Claude inline execution.
130
+ - If the provider returns incomplete status or action selection, reject it and request a constrained retry.
131
+ 8. When finished, display the routed `/tlc` result, persist any captured memory, remove `.tlc/.tlc-routing-active`, and stop. Do **not** execute Inline Mode afterward.
132
+
133
+ ## INLINE MODE
134
+
135
+ Use this mode only when `models[0]` **IS** `claude`.
136
+
137
+ The existing auto-runner instructions below are the Inline Mode instructions and should be followed unchanged.
138
+
5
139
  ## What This Does
6
140
 
7
141
  `/tlc` is the default entry point. It should: