tlc-claude-code 2.4.2 → 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.
@@ -66,21 +66,22 @@ describe('task-router-config', () => {
66
66
  });
67
67
 
68
68
  const result = loadPersonalConfig({ homeDir: '/home/user', fs });
69
- expect(result).toEqual(personalConfig);
69
+ expect(result).toEqual({ config: personalConfig, warning: null });
70
70
  });
71
71
 
72
72
  it('returns null when file does not exist', () => {
73
73
  const fs = mockFs({});
74
74
  const result = loadPersonalConfig({ homeDir: '/home/user', fs });
75
- expect(result).toBeNull();
75
+ expect(result).toEqual({ config: null, warning: null });
76
76
  });
77
77
 
78
- it('returns null for malformed JSON', () => {
78
+ it('returns warning for malformed JSON', () => {
79
79
  const fs = mockFs({
80
80
  '/home/user/.tlc/config.json': '{ not valid json',
81
81
  });
82
82
  const result = loadPersonalConfig({ homeDir: '/home/user', fs });
83
- expect(result).toBeNull();
83
+ expect(result.config).toBeNull();
84
+ expect(result.warning).toContain('/home/user/.tlc/config.json');
84
85
  });
85
86
  });
86
87
 
@@ -97,7 +98,7 @@ describe('task-router-config', () => {
97
98
  });
98
99
 
99
100
  const result = loadProjectOverride({ projectDir: '/project', fs });
100
- expect(result).toEqual(tlcJson.task_routing_override);
101
+ expect(result).toEqual({ override: tlcJson.task_routing_override, warning: null });
101
102
  });
102
103
 
103
104
  it('returns null when .tlc.json has no task_routing_override', () => {
@@ -106,21 +107,22 @@ describe('task-router-config', () => {
106
107
  });
107
108
 
108
109
  const result = loadProjectOverride({ projectDir: '/project', fs });
109
- expect(result).toBeNull();
110
+ expect(result).toEqual({ override: null, warning: null });
110
111
  });
111
112
 
112
113
  it('returns null when .tlc.json does not exist', () => {
113
114
  const fs = mockFs({});
114
115
  const result = loadProjectOverride({ projectDir: '/project', fs });
115
- expect(result).toBeNull();
116
+ expect(result).toEqual({ override: null, warning: null });
116
117
  });
117
118
 
118
- it('returns null for malformed JSON', () => {
119
+ it('returns warning for malformed JSON', () => {
119
120
  const fs = mockFs({
120
121
  '/project/.tlc.json': 'broken!!!',
121
122
  });
122
123
  const result = loadProjectOverride({ projectDir: '/project', fs });
123
- expect(result).toBeNull();
124
+ expect(result.override).toBeNull();
125
+ expect(result.warning).toContain('/project/.tlc.json');
124
126
  });
125
127
  });
126
128
 
@@ -139,6 +141,7 @@ describe('task-router-config', () => {
139
141
  models: ['claude'],
140
142
  strategy: 'single',
141
143
  source: 'shipped-defaults',
144
+ warnings: [],
142
145
  });
143
146
  });
144
147
 
@@ -163,6 +166,7 @@ describe('task-router-config', () => {
163
166
  models: ['codex'],
164
167
  strategy: 'single',
165
168
  source: 'personal-config',
169
+ warnings: [],
166
170
  });
167
171
  });
168
172
 
@@ -193,6 +197,7 @@ describe('task-router-config', () => {
193
197
  models: ['local'],
194
198
  strategy: 'single',
195
199
  source: 'project-override',
200
+ warnings: [],
196
201
  });
197
202
  });
198
203
 
@@ -224,6 +229,7 @@ describe('task-router-config', () => {
224
229
  models: ['gemini'],
225
230
  strategy: 'single',
226
231
  source: 'flag-override',
232
+ warnings: [],
227
233
  });
228
234
  });
229
235
 
@@ -240,6 +246,7 @@ describe('task-router-config', () => {
240
246
  models: ['claude'],
241
247
  strategy: 'single',
242
248
  source: 'shipped-defaults',
249
+ warnings: [],
243
250
  });
244
251
  });
245
252
 
@@ -264,6 +271,7 @@ describe('task-router-config', () => {
264
271
  models: ['claude', 'codex'],
265
272
  strategy: 'parallel',
266
273
  source: 'personal-config',
274
+ warnings: [],
267
275
  });
268
276
 
269
277
  // 'build' should still use defaults
@@ -277,6 +285,7 @@ describe('task-router-config', () => {
277
285
  models: ['claude'],
278
286
  strategy: 'single',
279
287
  source: 'shipped-defaults',
288
+ warnings: [],
280
289
  });
281
290
  });
282
291
 
@@ -301,6 +310,7 @@ describe('task-router-config', () => {
301
310
  expect(result.strategy).toBe('parallel');
302
311
  expect(result.models).toEqual(['claude']);
303
312
  expect(result.source).toBe('personal-config');
313
+ expect(result.warnings).toEqual([]);
304
314
  });
305
315
 
306
316
  it('partial personal config merges correctly (only models)', () => {
@@ -323,6 +333,7 @@ describe('task-router-config', () => {
323
333
  expect(result.models).toEqual(['codex']);
324
334
  expect(result.strategy).toBe('single');
325
335
  expect(result.source).toBe('personal-config');
336
+ expect(result.warnings).toEqual([]);
326
337
  });
327
338
 
328
339
  it('missing personal config file handled gracefully', () => {
@@ -345,10 +356,31 @@ describe('task-router-config', () => {
345
356
  models: ['local'],
346
357
  strategy: 'single',
347
358
  source: 'project-override',
359
+ warnings: [],
348
360
  });
349
361
  });
350
362
 
351
- it('malformed personal JSON handled gracefully', () => {
363
+ it('valid config returns empty warnings array', () => {
364
+ const personalConfig = {
365
+ task_routing: {
366
+ build: { models: ['codex'], strategy: 'single' },
367
+ },
368
+ };
369
+ const fs = mockFs({
370
+ '/home/user/.tlc/config.json': JSON.stringify(personalConfig),
371
+ });
372
+
373
+ const result = resolveRouting({
374
+ command: 'build',
375
+ projectDir: '/project',
376
+ homeDir: '/home/user',
377
+ fs,
378
+ });
379
+
380
+ expect(result.warnings).toEqual([]);
381
+ });
382
+
383
+ it('malformed personal JSON handled gracefully with warning', () => {
352
384
  const fs = mockFs({
353
385
  '/home/user/.tlc/config.json': '{{{{not json!',
354
386
  });
@@ -364,10 +396,11 @@ describe('task-router-config', () => {
364
396
  models: ['claude'],
365
397
  strategy: 'single',
366
398
  source: 'shipped-defaults',
399
+ warnings: [expect.stringContaining('/home/user/.tlc/config.json')],
367
400
  });
368
401
  });
369
402
 
370
- it('malformed project JSON handled gracefully', () => {
403
+ it('malformed project JSON handled gracefully with warning', () => {
371
404
  const fs = mockFs({
372
405
  '/project/.tlc.json': 'not json either!',
373
406
  });
@@ -383,9 +416,31 @@ describe('task-router-config', () => {
383
416
  models: ['claude'],
384
417
  strategy: 'single',
385
418
  source: 'shipped-defaults',
419
+ warnings: [expect.stringContaining('/project/.tlc.json')],
386
420
  });
387
421
  });
388
422
 
423
+ it('collects both warnings when personal and project JSON are malformed', () => {
424
+ const fs = mockFs({
425
+ '/home/user/.tlc/config.json': '{{{{not json!',
426
+ '/project/.tlc.json': 'not json either!',
427
+ });
428
+
429
+ const result = resolveRouting({
430
+ command: 'build',
431
+ projectDir: '/project',
432
+ homeDir: '/home/user',
433
+ fs,
434
+ });
435
+
436
+ expect(result.models).toEqual(['claude']);
437
+ expect(result.strategy).toBe('single');
438
+ expect(result.source).toBe('shipped-defaults');
439
+ expect(result.warnings).toHaveLength(2);
440
+ expect(result.warnings[0]).toContain('/home/user/.tlc/config.json');
441
+ expect(result.warnings[1]).toContain('/project/.tlc.json');
442
+ });
443
+
389
444
  it('model_providers loaded from personal config', () => {
390
445
  const personalConfig = {
391
446
  task_routing: {
@@ -410,9 +465,10 @@ describe('task-router-config', () => {
410
465
 
411
466
  expect(result.models).toEqual(['codex']);
412
467
  expect(result.providers).toEqual(personalConfig.model_providers);
468
+ expect(result.warnings).toEqual([]);
413
469
  });
414
470
 
415
- it('personal config with singular model (string) is normalized to models array', () => {
471
+ it('personal config with singular model (string) is normalized to models array with deprecation warning', () => {
416
472
  const personalConfig = {
417
473
  task_routing: {
418
474
  build: { model: 'codex', strategy: 'single' },
@@ -432,9 +488,12 @@ describe('task-router-config', () => {
432
488
  expect(result.models).toEqual(['codex']);
433
489
  expect(result.strategy).toBe('single');
434
490
  expect(result.source).toBe('personal-config');
491
+ expect(result.warnings).toEqual([
492
+ expect.stringMatching(/models/i),
493
+ ]);
435
494
  });
436
495
 
437
- it('project override with singular model (string) is normalized to models array', () => {
496
+ it('project override with singular model (string) is normalized to models array with deprecation warning', () => {
438
497
  const tlcJson = {
439
498
  task_routing_override: {
440
499
  review: { model: 'gemini', strategy: 'single' },
@@ -454,6 +513,9 @@ describe('task-router-config', () => {
454
513
  expect(result.models).toEqual(['gemini']);
455
514
  expect(result.strategy).toBe('single');
456
515
  expect(result.source).toBe('project-override');
516
+ expect(result.warnings).toEqual([
517
+ expect.stringMatching(/models/i),
518
+ ]);
457
519
  });
458
520
 
459
521
  it('models array takes precedence over model string in same config entry', () => {
@@ -475,6 +537,7 @@ describe('task-router-config', () => {
475
537
 
476
538
  expect(result.models).toEqual(['codex', 'claude']);
477
539
  expect(result.strategy).toBe('parallel');
540
+ expect(result.warnings).toEqual([]);
478
541
  });
479
542
 
480
543
  it('returns no providers when personal config has none', () => {
@@ -488,6 +551,7 @@ describe('task-router-config', () => {
488
551
  });
489
552
 
490
553
  expect(result.providers).toBeUndefined();
554
+ expect(result.warnings).toEqual([]);
491
555
  });
492
556
  });
493
557
  });