tlc-claude-code 1.6.0 → 1.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/commands/tlc/build.md +148 -56
- package/.claude/commands/tlc/llm.md +4 -4
- package/.claude/commands/tlc/sync.md +1 -1
- package/CLAUDE.md +11 -0
- package/README.md +2 -2
- package/docker-compose.dev.yml +20 -0
- package/package.json +1 -1
- package/server/index.js +212 -0
- package/server/lib/adapters/claude-adapter.js +3 -3
- package/server/lib/adapters/claude-adapter.test.js +2 -2
- package/server/lib/adapters/openai-adapter.js +3 -3
- package/server/lib/adapters/openai-adapter.test.js +2 -2
- package/server/lib/model-pricing.js +2 -0
- package/server/lib/overdrive-command.js +151 -15
- package/server/lib/overdrive-command.test.js +207 -10
- package/sync.md +1 -1
|
@@ -9,6 +9,12 @@ import {
|
|
|
9
9
|
canParallelize,
|
|
10
10
|
shouldUseOverdrive,
|
|
11
11
|
createOverdriveCommand,
|
|
12
|
+
estimateTaskComplexity,
|
|
13
|
+
getModelForTask,
|
|
14
|
+
selectAgentType,
|
|
15
|
+
AGENT_TYPES,
|
|
16
|
+
MODEL_TIERS,
|
|
17
|
+
DEFAULT_MAX_TURNS,
|
|
12
18
|
} from './overdrive-command.js';
|
|
13
19
|
|
|
14
20
|
describe('overdrive-command', () => {
|
|
@@ -21,6 +27,8 @@ describe('overdrive-command', () => {
|
|
|
21
27
|
expect(options.mode).toBe('build');
|
|
22
28
|
expect(options.dryRun).toBe(false);
|
|
23
29
|
expect(options.sequential).toBe(false);
|
|
30
|
+
expect(options.model).toBeNull();
|
|
31
|
+
expect(options.maxTurns).toBe(DEFAULT_MAX_TURNS);
|
|
24
32
|
});
|
|
25
33
|
|
|
26
34
|
it('parses phase number', () => {
|
|
@@ -28,14 +36,14 @@ describe('overdrive-command', () => {
|
|
|
28
36
|
expect(options.phase).toBe(5);
|
|
29
37
|
});
|
|
30
38
|
|
|
31
|
-
it('parses --agents flag', () => {
|
|
39
|
+
it('parses --agents flag without cap', () => {
|
|
32
40
|
const options = parseOverdriveArgs('--agents 4');
|
|
33
41
|
expect(options.agents).toBe(4);
|
|
34
42
|
});
|
|
35
43
|
|
|
36
|
-
it('
|
|
44
|
+
it('allows agent count beyond 10', () => {
|
|
37
45
|
const options = parseOverdriveArgs('--agents 15');
|
|
38
|
-
expect(options.agents).toBe(
|
|
46
|
+
expect(options.agents).toBe(15);
|
|
39
47
|
});
|
|
40
48
|
|
|
41
49
|
it('parses --mode flag', () => {
|
|
@@ -65,18 +73,35 @@ describe('overdrive-command', () => {
|
|
|
65
73
|
expect(options.agents).toBe(1);
|
|
66
74
|
});
|
|
67
75
|
|
|
68
|
-
it('parses
|
|
69
|
-
|
|
76
|
+
it('parses --model flag', () => {
|
|
77
|
+
expect(parseOverdriveArgs('--model opus').model).toBe('opus');
|
|
78
|
+
expect(parseOverdriveArgs('--model sonnet').model).toBe('sonnet');
|
|
79
|
+
expect(parseOverdriveArgs('--model haiku').model).toBe('haiku');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('ignores invalid model values', () => {
|
|
83
|
+
expect(parseOverdriveArgs('--model gpt4').model).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('parses --max-turns flag', () => {
|
|
87
|
+
const options = parseOverdriveArgs('--max-turns 30');
|
|
88
|
+
expect(options.maxTurns).toBe(30);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('parses multiple flags including new ones', () => {
|
|
92
|
+
const options = parseOverdriveArgs('5 --agents 2 --mode test --model sonnet --max-turns 25 --dry-run');
|
|
70
93
|
|
|
71
94
|
expect(options.phase).toBe(5);
|
|
72
95
|
expect(options.agents).toBe(2);
|
|
73
96
|
expect(options.mode).toBe('test');
|
|
97
|
+
expect(options.model).toBe('sonnet');
|
|
98
|
+
expect(options.maxTurns).toBe(25);
|
|
74
99
|
expect(options.dryRun).toBe(true);
|
|
75
100
|
});
|
|
76
101
|
});
|
|
77
102
|
|
|
78
103
|
describe('generateAgentPrompts', () => {
|
|
79
|
-
it('generates prompts for tasks', () => {
|
|
104
|
+
it('generates prompts for tasks with model and maxTurns', () => {
|
|
80
105
|
const tasks = [
|
|
81
106
|
{ id: 1, title: 'Create schema' },
|
|
82
107
|
{ id: 2, title: 'Add validation' },
|
|
@@ -93,6 +118,9 @@ describe('overdrive-command', () => {
|
|
|
93
118
|
expect(prompts[0].taskTitle).toBe('Create schema');
|
|
94
119
|
expect(prompts[0].prompt).toContain('Task 1');
|
|
95
120
|
expect(prompts[0].prompt).toContain('Write tests first');
|
|
121
|
+
expect(prompts[0].agentType).toBe('general-purpose');
|
|
122
|
+
expect(prompts[0].model).toBeDefined();
|
|
123
|
+
expect(prompts[0].maxTurns).toBe(DEFAULT_MAX_TURNS);
|
|
96
124
|
});
|
|
97
125
|
|
|
98
126
|
it('includes no-question rules', () => {
|
|
@@ -123,6 +151,55 @@ describe('overdrive-command', () => {
|
|
|
123
151
|
|
|
124
152
|
expect(prompts[0].prompt).toContain('Fix any failing tests');
|
|
125
153
|
});
|
|
154
|
+
|
|
155
|
+
it('assigns model based on task complexity', () => {
|
|
156
|
+
const tasks = [
|
|
157
|
+
{ id: 1, title: 'Refactor authentication system' },
|
|
158
|
+
{ id: 2, title: 'Add helper function' },
|
|
159
|
+
{ id: 3, title: 'Create enum constants' },
|
|
160
|
+
];
|
|
161
|
+
|
|
162
|
+
const prompts = generateAgentPrompts(tasks, {
|
|
163
|
+
mode: 'build', projectDir: '/p', phase: 1,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
expect(prompts[0].model).toBe('opus'); // refactor = heavy
|
|
167
|
+
expect(prompts[1].model).toBe('sonnet'); // default = standard
|
|
168
|
+
expect(prompts[2].model).toBe('haiku'); // enum = light
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('respects model override', () => {
|
|
172
|
+
const tasks = [
|
|
173
|
+
{ id: 1, title: 'Refactor auth' },
|
|
174
|
+
{ id: 2, title: 'Create enum' },
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
const prompts = generateAgentPrompts(tasks, {
|
|
178
|
+
mode: 'build', projectDir: '/p', phase: 1,
|
|
179
|
+
model: 'haiku',
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
expect(prompts[0].model).toBe('haiku');
|
|
183
|
+
expect(prompts[1].model).toBe('haiku');
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('respects maxTurns override', () => {
|
|
187
|
+
const prompts = generateAgentPrompts(
|
|
188
|
+
[{ id: 1, title: 'Test' }],
|
|
189
|
+
{ mode: 'build', projectDir: '/p', phase: 1, maxTurns: 25 }
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
expect(prompts[0].maxTurns).toBe(25);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('includes complexity estimate', () => {
|
|
196
|
+
const prompts = generateAgentPrompts(
|
|
197
|
+
[{ id: 1, title: 'Database migration' }],
|
|
198
|
+
{ mode: 'build', projectDir: '/p', phase: 1 }
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
expect(prompts[0].complexity).toBe('heavy');
|
|
202
|
+
});
|
|
126
203
|
});
|
|
127
204
|
|
|
128
205
|
describe('distributeTasks', () => {
|
|
@@ -207,10 +284,10 @@ describe('overdrive-command', () => {
|
|
|
207
284
|
});
|
|
208
285
|
|
|
209
286
|
describe('generateTaskCalls', () => {
|
|
210
|
-
it('generates task tool calls', () => {
|
|
287
|
+
it('generates task tool calls with model and max_turns', () => {
|
|
211
288
|
const prompts = [
|
|
212
|
-
{ taskId: 1, taskTitle: 'Test', prompt: 'Do task 1', agentType: '
|
|
213
|
-
{ taskId: 2, taskTitle: 'Test 2', prompt: 'Do task 2', agentType: '
|
|
289
|
+
{ taskId: 1, taskTitle: 'Test', prompt: 'Do task 1', agentType: 'general-purpose', model: 'sonnet', maxTurns: 50 },
|
|
290
|
+
{ taskId: 2, taskTitle: 'Test 2', prompt: 'Do task 2', agentType: 'general-purpose', model: 'haiku', maxTurns: 30 },
|
|
214
291
|
];
|
|
215
292
|
|
|
216
293
|
const calls = generateTaskCalls(prompts);
|
|
@@ -219,6 +296,11 @@ describe('overdrive-command', () => {
|
|
|
219
296
|
expect(calls[0].tool).toBe('Task');
|
|
220
297
|
expect(calls[0].params.description).toContain('Agent 1');
|
|
221
298
|
expect(calls[0].params.run_in_background).toBe(true);
|
|
299
|
+
expect(calls[0].params.subagent_type).toBe('general-purpose');
|
|
300
|
+
expect(calls[0].params.model).toBe('sonnet');
|
|
301
|
+
expect(calls[0].params.max_turns).toBe(50);
|
|
302
|
+
expect(calls[1].params.model).toBe('haiku');
|
|
303
|
+
expect(calls[1].params.max_turns).toBe(30);
|
|
222
304
|
});
|
|
223
305
|
});
|
|
224
306
|
|
|
@@ -326,7 +408,7 @@ Blocked by Task 1
|
|
|
326
408
|
expect(result.reason).toContain('Waterfall');
|
|
327
409
|
});
|
|
328
410
|
|
|
329
|
-
it('returns true for independent tasks', () => {
|
|
411
|
+
it('returns true for independent tasks with one agent per task', () => {
|
|
330
412
|
const result = canParallelize(
|
|
331
413
|
[{ id: 1 }, { id: 2 }, { id: 3 }],
|
|
332
414
|
{ hasDependencies: false, dependencies: [] }
|
|
@@ -337,6 +419,14 @@ Blocked by Task 1
|
|
|
337
419
|
expect(result.recommendedAgents).toBe(3);
|
|
338
420
|
});
|
|
339
421
|
|
|
422
|
+
it('recommends agents beyond 10 when tasks warrant it', () => {
|
|
423
|
+
const tasks = Array.from({ length: 15 }, (_, i) => ({ id: i + 1 }));
|
|
424
|
+
const result = canParallelize(tasks, { hasDependencies: false, dependencies: [] });
|
|
425
|
+
|
|
426
|
+
expect(result.canParallelize).toBe(true);
|
|
427
|
+
expect(result.recommendedAgents).toBe(15);
|
|
428
|
+
});
|
|
429
|
+
|
|
340
430
|
it('identifies mixed independent/dependent tasks', () => {
|
|
341
431
|
const result = canParallelize(
|
|
342
432
|
[{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }],
|
|
@@ -372,4 +462,111 @@ Blocked by Task 1
|
|
|
372
462
|
expect(handler.distributeTasks).toBeDefined();
|
|
373
463
|
});
|
|
374
464
|
});
|
|
465
|
+
|
|
466
|
+
describe('estimateTaskComplexity', () => {
|
|
467
|
+
it('classifies heavy tasks', () => {
|
|
468
|
+
expect(estimateTaskComplexity({ title: 'Refactor auth module' })).toBe('heavy');
|
|
469
|
+
expect(estimateTaskComplexity({ title: 'Database migration scripts' })).toBe('heavy');
|
|
470
|
+
expect(estimateTaskComplexity({ title: 'Redesign user schema' })).toBe('heavy');
|
|
471
|
+
expect(estimateTaskComplexity({ title: 'Security audit implementation' })).toBe('heavy');
|
|
472
|
+
expect(estimateTaskComplexity({ title: 'Integration with payment API' })).toBe('heavy');
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('classifies light tasks', () => {
|
|
476
|
+
expect(estimateTaskComplexity({ title: 'Add config constants' })).toBe('light');
|
|
477
|
+
expect(estimateTaskComplexity({ title: 'Create user enum' })).toBe('light');
|
|
478
|
+
expect(estimateTaskComplexity({ title: 'Create DTO for response' })).toBe('light');
|
|
479
|
+
expect(estimateTaskComplexity({ title: 'Add seed data' })).toBe('light');
|
|
480
|
+
expect(estimateTaskComplexity({ title: 'Create interface for service' })).toBe('light');
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
it('classifies standard tasks by default', () => {
|
|
484
|
+
expect(estimateTaskComplexity({ title: 'Add user listing endpoint' })).toBe('standard');
|
|
485
|
+
expect(estimateTaskComplexity({ title: 'Create helper function' })).toBe('standard');
|
|
486
|
+
expect(estimateTaskComplexity({ title: 'Add pagination support' })).toBe('standard');
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
it('handles empty or missing title', () => {
|
|
490
|
+
expect(estimateTaskComplexity({ title: '' })).toBe('standard');
|
|
491
|
+
expect(estimateTaskComplexity({})).toBe('standard');
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
describe('getModelForTask', () => {
|
|
496
|
+
it('returns opus for heavy tasks', () => {
|
|
497
|
+
expect(getModelForTask({ title: 'Refactor auth' })).toBe('opus');
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('returns sonnet for standard tasks', () => {
|
|
501
|
+
expect(getModelForTask({ title: 'Add endpoint' })).toBe('sonnet');
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
it('returns haiku for light tasks', () => {
|
|
505
|
+
expect(getModelForTask({ title: 'Create enum' })).toBe('haiku');
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
it('respects model override', () => {
|
|
509
|
+
expect(getModelForTask({ title: 'Refactor auth' }, 'haiku')).toBe('haiku');
|
|
510
|
+
expect(getModelForTask({ title: 'Create enum' }, 'opus')).toBe('opus');
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
describe('selectAgentType', () => {
|
|
515
|
+
it('returns general-purpose for build mode', () => {
|
|
516
|
+
expect(selectAgentType({ title: 'Test' }, 'build')).toBe('general-purpose');
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('returns general-purpose for test mode', () => {
|
|
520
|
+
expect(selectAgentType({ title: 'Test' }, 'test')).toBe('general-purpose');
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('returns general-purpose for fix mode', () => {
|
|
524
|
+
expect(selectAgentType({ title: 'Test' }, 'fix')).toBe('general-purpose');
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
describe('constants', () => {
|
|
529
|
+
it('exports valid agent types', () => {
|
|
530
|
+
expect(AGENT_TYPES.BUILD).toBe('general-purpose');
|
|
531
|
+
expect(AGENT_TYPES.SHELL).toBe('Bash');
|
|
532
|
+
expect(AGENT_TYPES.EXPLORE).toBe('Explore');
|
|
533
|
+
expect(AGENT_TYPES.PLAN).toBe('Plan');
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
it('exports valid model tiers', () => {
|
|
537
|
+
expect(MODEL_TIERS.HEAVY).toBe('opus');
|
|
538
|
+
expect(MODEL_TIERS.STANDARD).toBe('sonnet');
|
|
539
|
+
expect(MODEL_TIERS.LIGHT).toBe('haiku');
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('exports default max turns', () => {
|
|
543
|
+
expect(DEFAULT_MAX_TURNS).toBe(50);
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
describe('formatOverdrivePlan', () => {
|
|
548
|
+
it('includes Opus 4.6 branding and model info', () => {
|
|
549
|
+
const plan = {
|
|
550
|
+
phase: 3,
|
|
551
|
+
mode: 'build',
|
|
552
|
+
agentCount: 2,
|
|
553
|
+
totalTasks: 2,
|
|
554
|
+
modelOverride: null,
|
|
555
|
+
agentAssignments: [
|
|
556
|
+
{ tasks: [{ id: 1, title: 'Refactor auth' }] },
|
|
557
|
+
{ tasks: [{ id: 2, title: 'Create enum' }] },
|
|
558
|
+
],
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
const output = formatOverdrivePlan(plan);
|
|
562
|
+
|
|
563
|
+
expect(output).toContain('Opus 4.6');
|
|
564
|
+
expect(output).toContain('[opus]');
|
|
565
|
+
expect(output).toContain('[haiku]');
|
|
566
|
+
expect(output).toContain('Model selection per task complexity');
|
|
567
|
+
expect(output).toContain('Agent resumption');
|
|
568
|
+
expect(output).toContain('TaskOutput');
|
|
569
|
+
expect(output).toContain('TaskStop');
|
|
570
|
+
});
|
|
571
|
+
});
|
|
375
572
|
});
|