tlc-claude-code 2.4.1 → 2.4.3
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/autofix.md +70 -6
- package/.claude/commands/tlc/build.md +138 -6
- package/.claude/commands/tlc/coverage.md +70 -6
- package/.claude/commands/tlc/discuss.md +244 -129
- package/.claude/commands/tlc/docs.md +70 -6
- package/.claude/commands/tlc/e2e-verify.md +1 -1
- package/.claude/commands/tlc/edge-cases.md +70 -6
- package/.claude/commands/tlc/plan.md +147 -8
- package/.claude/commands/tlc/quick.md +70 -6
- package/.claude/commands/tlc/review.md +70 -6
- package/.claude/commands/tlc/tlc.md +204 -473
- package/CLAUDE.md +6 -5
- package/package.json +4 -1
- package/scripts/dev-link.sh +29 -0
- package/scripts/test-package.sh +54 -0
- package/scripts/version-sync.js +42 -0
- package/scripts/version-sync.test.js +100 -0
- package/server/lib/model-router.js +11 -2
- package/server/lib/model-router.test.js +27 -1
- package/server/lib/orchestration/codex-orchestrator.js +185 -0
- package/server/lib/orchestration/codex-orchestrator.test.js +221 -0
- package/server/lib/orchestration/dep-linker.js +61 -0
- package/server/lib/orchestration/dep-linker.test.js +174 -0
- package/server/lib/router-config.js +18 -3
- package/server/lib/router-config.test.js +57 -1
- package/server/lib/routing/index.js +34 -0
- package/server/lib/routing/index.test.js +33 -0
- package/server/lib/routing-command.js +11 -2
- package/server/lib/routing-command.test.js +39 -1
- package/server/lib/routing-preamble.integration.test.js +319 -0
- package/server/lib/routing-preamble.js +116 -0
- package/server/lib/routing-preamble.test.js +266 -0
- package/server/lib/task-router-config.js +35 -14
- package/server/lib/task-router-config.test.js +77 -13
|
@@ -10,12 +10,76 @@ This command supports multi-model routing via `~/.tlc/config.json`.
|
|
|
10
10
|
|
|
11
11
|
1. Read routing config:
|
|
12
12
|
```bash
|
|
13
|
-
node -e "
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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: \"plan\", flagModel: process.argv[1], projectDir: process.cwd(), homeDir: process.env.HOME || os.homedir(), fs });
|
|
82
|
+
process.stdout.write(JSON.stringify(result));" 2>/dev/null
|
|
19
83
|
```
|
|
20
84
|
|
|
21
85
|
2. If `models[0]` is NOT `claude` (i.e., routed to external model):
|
|
@@ -47,6 +111,7 @@ This command supports multi-model routing via `~/.tlc/config.json`.
|
|
|
47
111
|
- **Extension points**: Where will this need to grow? Plan for it.
|
|
48
112
|
- **Error boundaries**: Where can failures occur? How are they handled?
|
|
49
113
|
- **Data flow**: How does data enter, transform, and exit the system?
|
|
114
|
+
- **Outcome constraints**: Define required behaviors, scale limits, latency targets, and failure handling. Avoid prescribing implementation mechanics unless a constraint is already fixed by the system.
|
|
50
115
|
|
|
51
116
|
### Code Quality Gates
|
|
52
117
|
- **File size limit**: No file should exceed 1000 lines. Plan splits for large modules.
|
|
@@ -58,6 +123,7 @@ This command supports multi-model routing via `~/.tlc/config.json`.
|
|
|
58
123
|
- **Vertical slices**: Each task delivers testable, visible progress
|
|
59
124
|
- **Risk-first**: Tackle unknowns and integrations early
|
|
60
125
|
- **Dependencies explicit**: Mark what blocks what
|
|
126
|
+
- **Strategic purpose clear**: Every task explains why it exists, what risk it retires, or what capability it unlocks
|
|
61
127
|
|
|
62
128
|
## What This Does
|
|
63
129
|
|
|
@@ -99,12 +165,21 @@ Each task should be:
|
|
|
99
165
|
- **Testable** - has clear pass/fail criteria
|
|
100
166
|
- **Independent** - minimal dependencies on other tasks
|
|
101
167
|
- **Standards-compliant** - won't produce files >1000 lines or folders >15 files
|
|
168
|
+
- **Strategically justified** - includes the problem solved and why this task matters now
|
|
102
169
|
|
|
103
170
|
**Before finalizing tasks, check:**
|
|
104
171
|
1. Will any planned file exceed 1000 lines? → Split into sub-modules
|
|
105
172
|
2. Will any folder exceed 15 files? → Plan domain subfolders
|
|
106
173
|
3. Are all interfaces defined? → Add `interfaces/` directory per module
|
|
107
174
|
4. Are types explicit? → Plan typed interfaces, not `any`
|
|
175
|
+
5. Do acceptance criteria describe outcomes instead of implementation? → Rewrite "use X" into "must achieve Y"
|
|
176
|
+
6. Are any files likely to grow past 1000 lines after follow-on tasks? → Add a warning and split plan now, not later
|
|
177
|
+
|
|
178
|
+
**When estimating file and folder impact:**
|
|
179
|
+
- Check existing target files and folders before assigning work
|
|
180
|
+
- Include projected growth from all tasks in the phase, not only the current task
|
|
181
|
+
- Add an explicit warning in the plan if any task is likely to create or expand a file beyond 1000 lines
|
|
182
|
+
- Prefer splitting by domain responsibility before implementation starts
|
|
108
183
|
|
|
109
184
|
#### Task Status Markers (Multi-User)
|
|
110
185
|
|
|
@@ -125,6 +200,8 @@ Use `/tlc:claim` to claim a task, `/tlc:release` to release one.
|
|
|
125
200
|
|
|
126
201
|
**Goal:** Define database schema for users table
|
|
127
202
|
|
|
203
|
+
**Strategic Purpose:** Establish the canonical user data contract early so authentication, profile management, and downstream integrations build on a stable foundation.
|
|
204
|
+
|
|
128
205
|
**Files:**
|
|
129
206
|
- src/modules/user/interfaces/user.interface.ts
|
|
130
207
|
- src/modules/user/user.repository.ts
|
|
@@ -135,6 +212,7 @@ Use `/tlc:claim` to claim a task, `/tlc:release` to release one.
|
|
|
135
212
|
- [ ] Timestamps auto-populate
|
|
136
213
|
- [ ] All types explicit (no `any`)
|
|
137
214
|
- [ ] Exported functions have return types
|
|
215
|
+
- [ ] Criteria describe observable outcomes and constraints, not implementation choices
|
|
138
216
|
|
|
139
217
|
**Test Cases:**
|
|
140
218
|
- Schema validates correct user data
|
|
@@ -157,18 +235,35 @@ Create `.planning/phases/{N}-PLAN.md`:
|
|
|
157
235
|
|
|
158
236
|
- [ ] {Any setup or prior work needed}
|
|
159
237
|
|
|
238
|
+
## Risk Assessment
|
|
239
|
+
|
|
240
|
+
- **Unknowns:** {Unclear requirements, technical unknowns, external dependencies}
|
|
241
|
+
- **Integration Points:** {Systems, modules, APIs, data contracts, migrations}
|
|
242
|
+
- **Potential Failures:** {What could break, degrade, or block delivery}
|
|
243
|
+
- **Mitigations:** {How the plan reduces or contains those risks}
|
|
244
|
+
|
|
245
|
+
## Decision Log
|
|
246
|
+
|
|
247
|
+
| Decision | Rationale | Alternatives Considered | Consequence |
|
|
248
|
+
|----------|-----------|--------------------------|-------------|
|
|
249
|
+
| {Architectural choice} | {Why this is the best fit now} | {What else was considered} | {Tradeoff or follow-on impact} |
|
|
250
|
+
|
|
160
251
|
## Tasks
|
|
161
252
|
|
|
162
253
|
### Task 1: {Title}
|
|
163
254
|
|
|
164
255
|
**Goal:** {What this accomplishes}
|
|
165
256
|
|
|
257
|
+
**Strategic Purpose:** {Why this task matters, what problem it solves, or what later work it unlocks}
|
|
258
|
+
|
|
166
259
|
**Files:**
|
|
167
260
|
- {files to create/modify}
|
|
261
|
+
- {note if any file is at risk of exceeding 1000 lines and how the plan avoids it}
|
|
168
262
|
|
|
169
263
|
**Acceptance Criteria:**
|
|
170
|
-
- [ ] {
|
|
171
|
-
- [ ] {
|
|
264
|
+
- [ ] {Observable behavior or outcome 1}
|
|
265
|
+
- [ ] {Observable behavior, scale constraint, latency target, or failure-handling outcome 2}
|
|
266
|
+
- [ ] {No criterion prescribes implementation details unless already mandated by project constraints}
|
|
172
267
|
|
|
173
268
|
**Test Cases:**
|
|
174
269
|
- {Test description 1}
|
|
@@ -184,11 +279,21 @@ Create `.planning/phases/{N}-PLAN.md`:
|
|
|
184
279
|
|
|
185
280
|
{Task dependencies if any - e.g., Task 3 requires Task 1}
|
|
186
281
|
|
|
282
|
+
## Definition of Done
|
|
283
|
+
|
|
284
|
+
| Dimension | Definition |
|
|
285
|
+
|-----------|------------|
|
|
286
|
+
| Behavior Change | {What users, operators, or dependent systems can now do differently} |
|
|
287
|
+
| Tests | {Unit, integration, end-to-end, contract, or manual checks required} |
|
|
288
|
+
| Failure Modes | {Known failure paths handled and verified} |
|
|
289
|
+
| Rollback | {How the change can be reverted or disabled safely if needed} |
|
|
290
|
+
|
|
187
291
|
## Estimated Scope
|
|
188
292
|
|
|
189
293
|
- Tasks: {N}
|
|
190
294
|
- Files: {N}
|
|
191
295
|
- Tests: ~{N} (estimated)
|
|
296
|
+
- File size warnings: {None / list any files projected to exceed 1000 lines and planned split}
|
|
192
297
|
```
|
|
193
298
|
|
|
194
299
|
### Step 5: Review Plan
|
|
@@ -205,6 +310,9 @@ Tasks: 4
|
|
|
205
310
|
4. Add loading/error states
|
|
206
311
|
|
|
207
312
|
Estimated tests: 12
|
|
313
|
+
Key risks: 2 integration points, 1 unresolved dependency
|
|
314
|
+
Architecture decisions logged: 3
|
|
315
|
+
File size warnings: none
|
|
208
316
|
|
|
209
317
|
Proceed with this plan? (Y/n)
|
|
210
318
|
```
|
|
@@ -230,12 +338,15 @@ Task: Create login API endpoint
|
|
|
230
338
|
- Validates email/password
|
|
231
339
|
- Returns JWT token
|
|
232
340
|
- Handles invalid credentials
|
|
341
|
+
- Explains why login is needed now and what downstream work it unlocks
|
|
342
|
+
- Defines outcomes such as response behavior, error handling, and throughput constraints
|
|
233
343
|
```
|
|
234
344
|
|
|
235
345
|
**Bad tasks:**
|
|
236
346
|
```
|
|
237
347
|
Task: Build auth system <- too big
|
|
238
348
|
Task: Add login <- too vague
|
|
349
|
+
Task: Use Redis cache <- prescribes implementation, not outcome
|
|
239
350
|
```
|
|
240
351
|
|
|
241
352
|
## Example Output
|
|
@@ -247,12 +358,27 @@ Task: Add login <- too vague
|
|
|
247
358
|
|
|
248
359
|
User registration and login with JWT tokens.
|
|
249
360
|
|
|
361
|
+
## Risk Assessment
|
|
362
|
+
|
|
363
|
+
- **Unknowns:** Final session expiry requirements and auth provider migration timeline
|
|
364
|
+
- **Integration Points:** Database schema, password hashing, token issuance, API error contracts
|
|
365
|
+
- **Potential Failures:** Duplicate users, weak validation, token leakage, incompatible response shapes
|
|
366
|
+
- **Mitigations:** Lock contracts early, validate edge cases, test failure paths before wiring UI
|
|
367
|
+
|
|
368
|
+
## Decision Log
|
|
369
|
+
|
|
370
|
+
| Decision | Rationale | Alternatives Considered | Consequence |
|
|
371
|
+
|----------|-----------|--------------------------|-------------|
|
|
372
|
+
| Use separate schema and endpoint tasks | Reduces coupling and surfaces auth contract risks early | Single end-to-end auth task | More handoff points, but lower integration risk |
|
|
373
|
+
|
|
250
374
|
## Tasks
|
|
251
375
|
|
|
252
376
|
### Task 1: Create user schema
|
|
253
377
|
|
|
254
378
|
**Goal:** Database schema for users
|
|
255
379
|
|
|
380
|
+
**Strategic Purpose:** Create the source-of-truth user contract first so registration, login, and profile features depend on one stable model instead of diverging assumptions.
|
|
381
|
+
|
|
256
382
|
**Files:**
|
|
257
383
|
- src/db/schema/users.ts
|
|
258
384
|
- src/db/migrations/001_users.sql
|
|
@@ -261,6 +387,7 @@ User registration and login with JWT tokens.
|
|
|
261
387
|
- [ ] Has id, email, passwordHash, createdAt, updatedAt
|
|
262
388
|
- [ ] Email unique constraint
|
|
263
389
|
- [ ] Password hashed with bcrypt
|
|
390
|
+
- [ ] User creation rules are expressed as observable schema outcomes, not storage implementation notes
|
|
264
391
|
|
|
265
392
|
**Test Cases:**
|
|
266
393
|
- Schema accepts valid user data
|
|
@@ -273,6 +400,8 @@ User registration and login with JWT tokens.
|
|
|
273
400
|
|
|
274
401
|
**Goal:** POST /api/auth/register
|
|
275
402
|
|
|
403
|
+
**Strategic Purpose:** Deliver the first externally usable auth capability and validate that schema, validation, and response contracts work together under real request flows.
|
|
404
|
+
|
|
276
405
|
**Files:**
|
|
277
406
|
- src/api/auth/register.ts
|
|
278
407
|
- src/lib/auth/password.ts
|
|
@@ -282,10 +411,20 @@ User registration and login with JWT tokens.
|
|
|
282
411
|
- [ ] Returns user without password
|
|
283
412
|
- [ ] Rejects existing email with 409
|
|
284
413
|
- [ ] Validates email format
|
|
414
|
+
- [ ] Handles invalid and duplicate registration attempts with stable API behavior under concurrent requests
|
|
285
415
|
|
|
286
416
|
**Test Cases:**
|
|
287
417
|
- Register with valid data returns user
|
|
288
418
|
- Register with existing email returns 409
|
|
289
419
|
- Register with invalid email returns 400
|
|
290
420
|
- Password stored as hash
|
|
421
|
+
|
|
422
|
+
## Definition of Done
|
|
423
|
+
|
|
424
|
+
| Dimension | Definition |
|
|
425
|
+
|-----------|------------|
|
|
426
|
+
| Behavior Change | Users can register and authenticate through stable API contracts |
|
|
427
|
+
| Tests | Schema, endpoint, validation, and concurrency-sensitive duplicate-user paths are covered |
|
|
428
|
+
| Failure Modes | Duplicate email, invalid payloads, and password exposure paths are rejected safely |
|
|
429
|
+
| Rollback | Auth routes can be disabled and schema migration reverted with a documented backout path |
|
|
291
430
|
```
|
|
@@ -10,12 +10,76 @@ This command supports multi-model routing via `~/.tlc/config.json`.
|
|
|
10
10
|
|
|
11
11
|
1. Read routing config:
|
|
12
12
|
```bash
|
|
13
|
-
node -e "
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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: \"quick\", flagModel: process.argv[1], projectDir: process.cwd(), homeDir: process.env.HOME || os.homedir(), fs });
|
|
82
|
+
process.stdout.write(JSON.stringify(result));" 2>/dev/null
|
|
19
83
|
```
|
|
20
84
|
|
|
21
85
|
2. If `models[0]` is NOT `claude` (i.e., routed to external model):
|
|
@@ -10,12 +10,76 @@ This command supports multi-model routing via `~/.tlc/config.json`.
|
|
|
10
10
|
|
|
11
11
|
1. Read routing config:
|
|
12
12
|
```bash
|
|
13
|
-
node -e "
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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: \"review\", flagModel: process.argv[1], projectDir: process.cwd(), homeDir: process.env.HOME || os.homedir(), fs });
|
|
82
|
+
process.stdout.write(JSON.stringify(result));" 2>/dev/null
|
|
19
83
|
```
|
|
20
84
|
|
|
21
85
|
2. If `models[0]` is NOT `claude` (i.e., routed to external model):
|