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.
Files changed (34) hide show
  1. package/.claude/commands/tlc/autofix.md +70 -6
  2. package/.claude/commands/tlc/build.md +138 -6
  3. package/.claude/commands/tlc/coverage.md +70 -6
  4. package/.claude/commands/tlc/discuss.md +244 -129
  5. package/.claude/commands/tlc/docs.md +70 -6
  6. package/.claude/commands/tlc/e2e-verify.md +1 -1
  7. package/.claude/commands/tlc/edge-cases.md +70 -6
  8. package/.claude/commands/tlc/plan.md +147 -8
  9. package/.claude/commands/tlc/quick.md +70 -6
  10. package/.claude/commands/tlc/review.md +70 -6
  11. package/.claude/commands/tlc/tlc.md +204 -473
  12. package/CLAUDE.md +6 -5
  13. package/package.json +4 -1
  14. package/scripts/dev-link.sh +29 -0
  15. package/scripts/test-package.sh +54 -0
  16. package/scripts/version-sync.js +42 -0
  17. package/scripts/version-sync.test.js +100 -0
  18. package/server/lib/model-router.js +11 -2
  19. package/server/lib/model-router.test.js +27 -1
  20. package/server/lib/orchestration/codex-orchestrator.js +185 -0
  21. package/server/lib/orchestration/codex-orchestrator.test.js +221 -0
  22. package/server/lib/orchestration/dep-linker.js +61 -0
  23. package/server/lib/orchestration/dep-linker.test.js +174 -0
  24. package/server/lib/router-config.js +18 -3
  25. package/server/lib/router-config.test.js +57 -1
  26. package/server/lib/routing/index.js +34 -0
  27. package/server/lib/routing/index.test.js +33 -0
  28. package/server/lib/routing-command.js +11 -2
  29. package/server/lib/routing-command.test.js +39 -1
  30. package/server/lib/routing-preamble.integration.test.js +319 -0
  31. package/server/lib/routing-preamble.js +116 -0
  32. package/server/lib/routing-preamble.test.js +266 -0
  33. package/server/lib/task-router-config.js +35 -14
  34. 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
- const { resolveRouting } = require('./server/lib/task-router-config');
15
- const flagModel = (process.argv.find(a => a.startsWith("--model")) || "").replace("--model=", "").replace("--model ", "") || null;
16
- const r = resolveRouting({ command: 'plan', flagModel, projectDir: process.cwd(), homeDir: process.env.HOME });
17
- console.log(JSON.stringify(r));
18
- " 2>/dev/null
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
- - [ ] {Testable criterion 1}
171
- - [ ] {Testable criterion 2}
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
- const { resolveRouting } = require('./server/lib/task-router-config');
15
- const flagModel = (process.argv.find(a => a.startsWith("--model")) || "").replace("--model=", "").replace("--model ", "") || null;
16
- const r = resolveRouting({ command: 'quick', flagModel, projectDir: process.cwd(), homeDir: process.env.HOME });
17
- console.log(JSON.stringify(r));
18
- " 2>/dev/null
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
- const { resolveRouting } = require('./server/lib/task-router-config');
15
- const flagModel = (process.argv.find(a => a.startsWith("--model")) || "").replace("--model=", "").replace("--model ", "") || null;
16
- const r = resolveRouting({ command: 'review', flagModel, projectDir: process.cwd(), homeDir: process.env.HOME });
17
- console.log(JSON.stringify(r));
18
- " 2>/dev/null
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):