tlc-claude-code 1.2.27 → 1.2.28

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 (172) hide show
  1. package/dashboard/dist/components/ActivityFeed.d.ts +17 -0
  2. package/dashboard/dist/components/ActivityFeed.js +42 -0
  3. package/dashboard/dist/components/ActivityFeed.test.d.ts +1 -0
  4. package/dashboard/dist/components/ActivityFeed.test.js +162 -0
  5. package/dashboard/dist/components/BranchSelector.d.ts +16 -0
  6. package/dashboard/dist/components/BranchSelector.js +49 -0
  7. package/dashboard/dist/components/BranchSelector.test.d.ts +1 -0
  8. package/dashboard/dist/components/BranchSelector.test.js +166 -0
  9. package/dashboard/dist/components/CommandPalette.d.ts +17 -0
  10. package/dashboard/dist/components/CommandPalette.js +118 -0
  11. package/dashboard/dist/components/CommandPalette.test.d.ts +1 -0
  12. package/dashboard/dist/components/CommandPalette.test.js +181 -0
  13. package/dashboard/dist/components/ConnectionStatus.d.ts +16 -0
  14. package/dashboard/dist/components/ConnectionStatus.js +27 -0
  15. package/dashboard/dist/components/ConnectionStatus.test.d.ts +1 -0
  16. package/dashboard/dist/components/ConnectionStatus.test.js +121 -0
  17. package/dashboard/dist/components/DeviceFrame.d.ts +19 -0
  18. package/dashboard/dist/components/DeviceFrame.js +52 -0
  19. package/dashboard/dist/components/DeviceFrame.test.d.ts +1 -0
  20. package/dashboard/dist/components/DeviceFrame.test.js +118 -0
  21. package/dashboard/dist/components/EnvironmentBadge.d.ts +11 -0
  22. package/dashboard/dist/components/EnvironmentBadge.js +16 -0
  23. package/dashboard/dist/components/EnvironmentBadge.test.d.ts +1 -0
  24. package/dashboard/dist/components/EnvironmentBadge.test.js +102 -0
  25. package/dashboard/dist/components/FocusIndicator.d.ts +19 -0
  26. package/dashboard/dist/components/FocusIndicator.js +47 -0
  27. package/dashboard/dist/components/FocusIndicator.test.d.ts +1 -0
  28. package/dashboard/dist/components/FocusIndicator.test.js +117 -0
  29. package/dashboard/dist/components/KeyboardHelp.d.ts +15 -0
  30. package/dashboard/dist/components/KeyboardHelp.js +61 -0
  31. package/dashboard/dist/components/KeyboardHelp.test.d.ts +1 -0
  32. package/dashboard/dist/components/KeyboardHelp.test.js +131 -0
  33. package/dashboard/dist/components/LogSearch.d.ts +13 -0
  34. package/dashboard/dist/components/LogSearch.js +43 -0
  35. package/dashboard/dist/components/LogSearch.test.d.ts +1 -0
  36. package/dashboard/dist/components/LogSearch.test.js +100 -0
  37. package/dashboard/dist/components/LogStream.d.ts +21 -0
  38. package/dashboard/dist/components/LogStream.js +123 -0
  39. package/dashboard/dist/components/LogStream.test.d.ts +1 -0
  40. package/dashboard/dist/components/LogStream.test.js +159 -0
  41. package/dashboard/dist/components/PreviewPanel.d.ts +18 -0
  42. package/dashboard/dist/components/PreviewPanel.js +73 -0
  43. package/dashboard/dist/components/PreviewPanel.test.d.ts +1 -0
  44. package/dashboard/dist/components/PreviewPanel.test.js +124 -0
  45. package/dashboard/dist/components/ProjectCard.d.ts +18 -0
  46. package/dashboard/dist/components/ProjectCard.js +19 -0
  47. package/dashboard/dist/components/ProjectCard.test.d.ts +1 -0
  48. package/dashboard/dist/components/ProjectCard.test.js +53 -0
  49. package/dashboard/dist/components/ProjectDetail.d.ts +44 -0
  50. package/dashboard/dist/components/ProjectDetail.js +65 -0
  51. package/dashboard/dist/components/ProjectDetail.test.d.ts +1 -0
  52. package/dashboard/dist/components/ProjectDetail.test.js +196 -0
  53. package/dashboard/dist/components/ProjectList.d.ts +11 -0
  54. package/dashboard/dist/components/ProjectList.js +62 -0
  55. package/dashboard/dist/components/ProjectList.test.d.ts +1 -0
  56. package/dashboard/dist/components/ProjectList.test.js +93 -0
  57. package/dashboard/dist/components/SettingsPanel.d.ts +32 -0
  58. package/dashboard/dist/components/SettingsPanel.js +154 -0
  59. package/dashboard/dist/components/SettingsPanel.test.d.ts +1 -0
  60. package/dashboard/dist/components/SettingsPanel.test.js +196 -0
  61. package/dashboard/dist/components/StatusBar.d.ts +16 -0
  62. package/dashboard/dist/components/StatusBar.js +47 -0
  63. package/dashboard/dist/components/StatusBar.test.d.ts +1 -0
  64. package/dashboard/dist/components/StatusBar.test.js +123 -0
  65. package/dashboard/dist/components/TaskBoard.d.ts +22 -0
  66. package/dashboard/dist/components/TaskBoard.js +102 -0
  67. package/dashboard/dist/components/TaskBoard.test.d.ts +1 -0
  68. package/dashboard/dist/components/TaskBoard.test.js +113 -0
  69. package/dashboard/dist/components/TaskCard.d.ts +17 -0
  70. package/dashboard/dist/components/TaskCard.js +29 -0
  71. package/dashboard/dist/components/TaskCard.test.d.ts +1 -0
  72. package/dashboard/dist/components/TaskCard.test.js +109 -0
  73. package/dashboard/dist/components/TaskDetail.d.ts +36 -0
  74. package/dashboard/dist/components/TaskDetail.js +41 -0
  75. package/dashboard/dist/components/TaskDetail.test.d.ts +1 -0
  76. package/dashboard/dist/components/TaskDetail.test.js +164 -0
  77. package/dashboard/dist/components/TaskFilter.d.ts +12 -0
  78. package/dashboard/dist/components/TaskFilter.js +138 -0
  79. package/dashboard/dist/components/TaskFilter.test.d.ts +1 -0
  80. package/dashboard/dist/components/TaskFilter.test.js +109 -0
  81. package/dashboard/dist/components/TeamPanel.d.ts +15 -0
  82. package/dashboard/dist/components/TeamPanel.js +24 -0
  83. package/dashboard/dist/components/TeamPanel.test.d.ts +1 -0
  84. package/dashboard/dist/components/TeamPanel.test.js +109 -0
  85. package/dashboard/dist/components/TeamPresence.d.ts +14 -0
  86. package/dashboard/dist/components/TeamPresence.js +31 -0
  87. package/dashboard/dist/components/TeamPresence.test.d.ts +1 -0
  88. package/dashboard/dist/components/TeamPresence.test.js +144 -0
  89. package/dashboard/dist/components/layout/Header.d.ts +9 -0
  90. package/dashboard/dist/components/layout/Header.js +11 -0
  91. package/dashboard/dist/components/layout/Header.test.d.ts +1 -0
  92. package/dashboard/dist/components/layout/Header.test.js +35 -0
  93. package/dashboard/dist/components/layout/Shell.d.ts +10 -0
  94. package/dashboard/dist/components/layout/Shell.js +5 -0
  95. package/dashboard/dist/components/layout/Shell.test.d.ts +1 -0
  96. package/dashboard/dist/components/layout/Shell.test.js +34 -0
  97. package/dashboard/dist/components/layout/Sidebar.d.ts +14 -0
  98. package/dashboard/dist/components/layout/Sidebar.js +8 -0
  99. package/dashboard/dist/components/layout/Sidebar.test.d.ts +1 -0
  100. package/dashboard/dist/components/layout/Sidebar.test.js +40 -0
  101. package/dashboard/dist/components/ui/Badge.d.ts +9 -0
  102. package/dashboard/dist/components/ui/Badge.js +13 -0
  103. package/dashboard/dist/components/ui/Badge.test.d.ts +1 -0
  104. package/dashboard/dist/components/ui/Badge.test.js +69 -0
  105. package/dashboard/dist/components/ui/Button.d.ts +12 -0
  106. package/dashboard/dist/components/ui/Button.js +14 -0
  107. package/dashboard/dist/components/ui/Button.test.d.ts +1 -0
  108. package/dashboard/dist/components/ui/Button.test.js +81 -0
  109. package/dashboard/dist/components/ui/Card.d.ts +21 -0
  110. package/dashboard/dist/components/ui/Card.js +20 -0
  111. package/dashboard/dist/components/ui/Card.test.d.ts +1 -0
  112. package/dashboard/dist/components/ui/Card.test.js +82 -0
  113. package/dashboard/dist/components/ui/Input.d.ts +13 -0
  114. package/dashboard/dist/components/ui/Input.js +8 -0
  115. package/dashboard/dist/components/ui/Input.test.d.ts +1 -0
  116. package/dashboard/dist/components/ui/Input.test.js +68 -0
  117. package/dashboard/dist/styles/tokens.d.ts +150 -0
  118. package/dashboard/dist/styles/tokens.js +184 -0
  119. package/dashboard/dist/styles/tokens.test.d.ts +1 -0
  120. package/dashboard/dist/styles/tokens.test.js +95 -0
  121. package/dashboard/dist/test/setup.d.ts +1 -0
  122. package/dashboard/dist/test/setup.js +1 -0
  123. package/dashboard/package.json +3 -0
  124. package/package.json +1 -1
  125. package/server/lib/adapters/base-adapter.js +114 -0
  126. package/server/lib/adapters/base-adapter.test.js +90 -0
  127. package/server/lib/adapters/claude-adapter.js +141 -0
  128. package/server/lib/adapters/claude-adapter.test.js +180 -0
  129. package/server/lib/adapters/deepseek-adapter.js +153 -0
  130. package/server/lib/adapters/deepseek-adapter.test.js +193 -0
  131. package/server/lib/adapters/openai-adapter.js +190 -0
  132. package/server/lib/adapters/openai-adapter.test.js +231 -0
  133. package/server/lib/budget-tracker.js +169 -0
  134. package/server/lib/budget-tracker.test.js +165 -0
  135. package/server/lib/claude-injector.js +85 -0
  136. package/server/lib/claude-injector.test.js +161 -0
  137. package/server/lib/consensus-engine.js +135 -0
  138. package/server/lib/consensus-engine.test.js +152 -0
  139. package/server/lib/context-builder.js +112 -0
  140. package/server/lib/context-builder.test.js +120 -0
  141. package/server/lib/file-collector.js +322 -0
  142. package/server/lib/file-collector.test.js +307 -0
  143. package/server/lib/memory-classifier.js +175 -0
  144. package/server/lib/memory-classifier.test.js +169 -0
  145. package/server/lib/memory-committer.js +138 -0
  146. package/server/lib/memory-committer.test.js +136 -0
  147. package/server/lib/memory-hooks.js +127 -0
  148. package/server/lib/memory-hooks.test.js +136 -0
  149. package/server/lib/memory-init.js +104 -0
  150. package/server/lib/memory-init.test.js +119 -0
  151. package/server/lib/memory-observer.js +149 -0
  152. package/server/lib/memory-observer.test.js +158 -0
  153. package/server/lib/memory-reader.js +243 -0
  154. package/server/lib/memory-reader.test.js +216 -0
  155. package/server/lib/memory-storage.js +120 -0
  156. package/server/lib/memory-storage.test.js +136 -0
  157. package/server/lib/memory-writer.js +176 -0
  158. package/server/lib/memory-writer.test.js +231 -0
  159. package/server/lib/overdrive-command.js +30 -6
  160. package/server/lib/overdrive-command.test.js +8 -1
  161. package/server/lib/pattern-detector.js +216 -0
  162. package/server/lib/pattern-detector.test.js +241 -0
  163. package/server/lib/relevance-scorer.js +175 -0
  164. package/server/lib/relevance-scorer.test.js +107 -0
  165. package/server/lib/review-command.js +238 -0
  166. package/server/lib/review-command.test.js +245 -0
  167. package/server/lib/review-orchestrator.js +273 -0
  168. package/server/lib/review-orchestrator.test.js +300 -0
  169. package/server/lib/review-reporter.js +288 -0
  170. package/server/lib/review-reporter.test.js +240 -0
  171. package/server/lib/session-summary.js +90 -0
  172. package/server/lib/session-summary.test.js +156 -0
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Design Tokens - Colors, spacing, typography, and themes
3
+ */
4
+ export declare const colors: {
5
+ primary: {
6
+ 50: string;
7
+ 100: string;
8
+ 200: string;
9
+ 300: string;
10
+ 400: string;
11
+ 500: string;
12
+ 600: string;
13
+ 700: string;
14
+ 800: string;
15
+ 900: string;
16
+ };
17
+ gray: {
18
+ 50: string;
19
+ 100: string;
20
+ 200: string;
21
+ 300: string;
22
+ 400: string;
23
+ 500: string;
24
+ 600: string;
25
+ 700: string;
26
+ 800: string;
27
+ 900: string;
28
+ };
29
+ success: {
30
+ 50: string;
31
+ 100: string;
32
+ 500: string;
33
+ 600: string;
34
+ 700: string;
35
+ };
36
+ warning: {
37
+ 50: string;
38
+ 100: string;
39
+ 500: string;
40
+ 600: string;
41
+ 700: string;
42
+ };
43
+ error: {
44
+ 50: string;
45
+ 100: string;
46
+ 500: string;
47
+ 600: string;
48
+ 700: string;
49
+ };
50
+ info: {
51
+ 50: string;
52
+ 100: string;
53
+ 500: string;
54
+ 600: string;
55
+ 700: string;
56
+ };
57
+ white: string;
58
+ black: string;
59
+ };
60
+ export declare const spacing: {
61
+ 0: string;
62
+ 0.5: string;
63
+ 1: string;
64
+ 1.5: string;
65
+ 2: string;
66
+ 2.5: string;
67
+ 3: string;
68
+ 3.5: string;
69
+ 4: string;
70
+ 5: string;
71
+ 6: string;
72
+ 7: string;
73
+ 8: string;
74
+ 9: string;
75
+ 10: string;
76
+ 12: string;
77
+ 14: string;
78
+ 16: string;
79
+ };
80
+ export declare const typography: {
81
+ fontSize: {
82
+ xs: string;
83
+ sm: string;
84
+ base: string;
85
+ lg: string;
86
+ xl: string;
87
+ '2xl': string;
88
+ '3xl': string;
89
+ '4xl': string;
90
+ };
91
+ fontWeight: {
92
+ normal: number;
93
+ medium: number;
94
+ semibold: number;
95
+ bold: number;
96
+ };
97
+ lineHeight: {
98
+ none: number;
99
+ tight: number;
100
+ snug: number;
101
+ normal: number;
102
+ relaxed: number;
103
+ loose: number;
104
+ };
105
+ fontFamily: {
106
+ sans: string;
107
+ mono: string;
108
+ };
109
+ };
110
+ export declare const themes: {
111
+ light: {
112
+ background: string;
113
+ foreground: string;
114
+ muted: string;
115
+ mutedForeground: string;
116
+ border: string;
117
+ primary: string;
118
+ primaryForeground: string;
119
+ secondary: string;
120
+ secondaryForeground: string;
121
+ accent: string;
122
+ accentForeground: string;
123
+ success: string;
124
+ warning: string;
125
+ error: string;
126
+ info: string;
127
+ };
128
+ dark: {
129
+ background: string;
130
+ foreground: string;
131
+ muted: string;
132
+ mutedForeground: string;
133
+ border: string;
134
+ primary: string;
135
+ primaryForeground: string;
136
+ secondary: string;
137
+ secondaryForeground: string;
138
+ accent: string;
139
+ accentForeground: string;
140
+ success: string;
141
+ warning: string;
142
+ error: string;
143
+ info: string;
144
+ };
145
+ };
146
+ export type Theme = keyof typeof themes;
147
+ /**
148
+ * Generate CSS custom properties for a theme
149
+ */
150
+ export declare function generateCSSVariables(theme: Theme): string;
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Design Tokens - Colors, spacing, typography, and themes
3
+ */
4
+ export const colors = {
5
+ // Primary palette
6
+ primary: {
7
+ 50: '#eff6ff',
8
+ 100: '#dbeafe',
9
+ 200: '#bfdbfe',
10
+ 300: '#93c5fd',
11
+ 400: '#60a5fa',
12
+ 500: '#3b82f6',
13
+ 600: '#2563eb',
14
+ 700: '#1d4ed8',
15
+ 800: '#1e40af',
16
+ 900: '#1e3a8a',
17
+ },
18
+ // Neutral grays
19
+ gray: {
20
+ 50: '#f9fafb',
21
+ 100: '#f3f4f6',
22
+ 200: '#e5e7eb',
23
+ 300: '#d1d5db',
24
+ 400: '#9ca3af',
25
+ 500: '#6b7280',
26
+ 600: '#4b5563',
27
+ 700: '#374151',
28
+ 800: '#1f2937',
29
+ 900: '#111827',
30
+ },
31
+ // Status colors
32
+ success: {
33
+ 50: '#f0fdf4',
34
+ 100: '#dcfce7',
35
+ 500: '#22c55e',
36
+ 600: '#16a34a',
37
+ 700: '#15803d',
38
+ },
39
+ warning: {
40
+ 50: '#fffbeb',
41
+ 100: '#fef3c7',
42
+ 500: '#f59e0b',
43
+ 600: '#d97706',
44
+ 700: '#b45309',
45
+ },
46
+ error: {
47
+ 50: '#fef2f2',
48
+ 100: '#fee2e2',
49
+ 500: '#ef4444',
50
+ 600: '#dc2626',
51
+ 700: '#b91c1c',
52
+ },
53
+ info: {
54
+ 50: '#eff6ff',
55
+ 100: '#dbeafe',
56
+ 500: '#3b82f6',
57
+ 600: '#2563eb',
58
+ 700: '#1d4ed8',
59
+ },
60
+ // Pure colors
61
+ white: '#ffffff',
62
+ black: '#000000',
63
+ };
64
+ export const spacing = {
65
+ 0: '0',
66
+ 0.5: '0.125rem',
67
+ 1: '0.25rem',
68
+ 1.5: '0.375rem',
69
+ 2: '0.5rem',
70
+ 2.5: '0.625rem',
71
+ 3: '0.75rem',
72
+ 3.5: '0.875rem',
73
+ 4: '1rem',
74
+ 5: '1.25rem',
75
+ 6: '1.5rem',
76
+ 7: '1.75rem',
77
+ 8: '2rem',
78
+ 9: '2.25rem',
79
+ 10: '2.5rem',
80
+ 12: '3rem',
81
+ 14: '3.5rem',
82
+ 16: '4rem',
83
+ };
84
+ export const typography = {
85
+ fontSize: {
86
+ xs: '0.75rem',
87
+ sm: '0.875rem',
88
+ base: '1rem',
89
+ lg: '1.125rem',
90
+ xl: '1.25rem',
91
+ '2xl': '1.5rem',
92
+ '3xl': '1.875rem',
93
+ '4xl': '2.25rem',
94
+ },
95
+ fontWeight: {
96
+ normal: 400,
97
+ medium: 500,
98
+ semibold: 600,
99
+ bold: 700,
100
+ },
101
+ lineHeight: {
102
+ none: 1,
103
+ tight: 1.25,
104
+ snug: 1.375,
105
+ normal: 1.5,
106
+ relaxed: 1.625,
107
+ loose: 2,
108
+ },
109
+ fontFamily: {
110
+ sans: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
111
+ mono: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace',
112
+ },
113
+ };
114
+ export const themes = {
115
+ light: {
116
+ background: colors.white,
117
+ foreground: colors.gray[900],
118
+ muted: colors.gray[100],
119
+ mutedForeground: colors.gray[500],
120
+ border: colors.gray[200],
121
+ primary: colors.primary[600],
122
+ primaryForeground: colors.white,
123
+ secondary: colors.gray[100],
124
+ secondaryForeground: colors.gray[900],
125
+ accent: colors.primary[50],
126
+ accentForeground: colors.primary[700],
127
+ success: colors.success[600],
128
+ warning: colors.warning[600],
129
+ error: colors.error[600],
130
+ info: colors.info[600],
131
+ },
132
+ dark: {
133
+ background: colors.gray[900],
134
+ foreground: colors.gray[50],
135
+ muted: colors.gray[800],
136
+ mutedForeground: colors.gray[400],
137
+ border: colors.gray[700],
138
+ primary: colors.primary[500],
139
+ primaryForeground: colors.white,
140
+ secondary: colors.gray[800],
141
+ secondaryForeground: colors.gray[100],
142
+ accent: colors.gray[800],
143
+ accentForeground: colors.primary[400],
144
+ success: colors.success[500],
145
+ warning: colors.warning[500],
146
+ error: colors.error[500],
147
+ info: colors.info[500],
148
+ },
149
+ };
150
+ /**
151
+ * Generate CSS custom properties for a theme
152
+ */
153
+ export function generateCSSVariables(theme) {
154
+ const themeTokens = themes[theme];
155
+ const lines = [];
156
+ // Theme colors
157
+ for (const [key, value] of Object.entries(themeTokens)) {
158
+ lines.push(` --color-${key}: ${value};`);
159
+ }
160
+ // Primary palette
161
+ for (const [shade, value] of Object.entries(colors.primary)) {
162
+ lines.push(` --color-primary-${shade}: ${value};`);
163
+ }
164
+ // Gray palette
165
+ for (const [shade, value] of Object.entries(colors.gray)) {
166
+ lines.push(` --color-gray-${shade}: ${value};`);
167
+ }
168
+ // Spacing
169
+ for (const [key, value] of Object.entries(spacing)) {
170
+ const normalizedKey = String(key).replace('.', '_');
171
+ lines.push(` --spacing-${normalizedKey}: ${value};`);
172
+ }
173
+ // Typography
174
+ for (const [key, value] of Object.entries(typography.fontSize)) {
175
+ lines.push(` --font-size-${key}: ${value};`);
176
+ }
177
+ for (const [key, value] of Object.entries(typography.fontWeight)) {
178
+ lines.push(` --font-weight-${key}: ${value};`);
179
+ }
180
+ for (const [key, value] of Object.entries(typography.lineHeight)) {
181
+ lines.push(` --line-height-${key}: ${value};`);
182
+ }
183
+ return `:root {\n${lines.join('\n')}\n}`;
184
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,95 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { colors, spacing, typography, themes, generateCSSVariables, } from './tokens.js';
3
+ describe('tokens', () => {
4
+ describe('colors', () => {
5
+ it('has primary color palette', () => {
6
+ expect(colors.primary).toBeDefined();
7
+ expect(colors.primary[500]).toBeDefined();
8
+ });
9
+ it('has status colors', () => {
10
+ expect(colors.success).toBeDefined();
11
+ expect(colors.warning).toBeDefined();
12
+ expect(colors.error).toBeDefined();
13
+ expect(colors.info).toBeDefined();
14
+ });
15
+ it('has neutral grays', () => {
16
+ expect(colors.gray).toBeDefined();
17
+ expect(colors.gray[50]).toBeDefined();
18
+ expect(colors.gray[900]).toBeDefined();
19
+ });
20
+ });
21
+ describe('spacing', () => {
22
+ it('has spacing scale', () => {
23
+ expect(spacing[0]).toBe('0');
24
+ expect(spacing[1]).toBe('0.25rem');
25
+ expect(spacing[4]).toBe('1rem');
26
+ expect(spacing[8]).toBe('2rem');
27
+ });
28
+ it('has 16 steps', () => {
29
+ expect(Object.keys(spacing).length).toBeGreaterThanOrEqual(13);
30
+ });
31
+ });
32
+ describe('typography', () => {
33
+ it('has font sizes', () => {
34
+ expect(typography.fontSize.xs).toBeDefined();
35
+ expect(typography.fontSize.sm).toBeDefined();
36
+ expect(typography.fontSize.base).toBeDefined();
37
+ expect(typography.fontSize.lg).toBeDefined();
38
+ expect(typography.fontSize.xl).toBeDefined();
39
+ });
40
+ it('has font weights', () => {
41
+ expect(typography.fontWeight.normal).toBe(400);
42
+ expect(typography.fontWeight.medium).toBe(500);
43
+ expect(typography.fontWeight.semibold).toBe(600);
44
+ expect(typography.fontWeight.bold).toBe(700);
45
+ });
46
+ it('has line heights', () => {
47
+ expect(typography.lineHeight.tight).toBeDefined();
48
+ expect(typography.lineHeight.normal).toBeDefined();
49
+ expect(typography.lineHeight.relaxed).toBeDefined();
50
+ });
51
+ });
52
+ describe('themes', () => {
53
+ it('has light theme', () => {
54
+ expect(themes.light).toBeDefined();
55
+ expect(themes.light.background).toBeDefined();
56
+ expect(themes.light.foreground).toBeDefined();
57
+ expect(themes.light.primary).toBeDefined();
58
+ });
59
+ it('has dark theme', () => {
60
+ expect(themes.dark).toBeDefined();
61
+ expect(themes.dark.background).toBeDefined();
62
+ expect(themes.dark.foreground).toBeDefined();
63
+ expect(themes.dark.primary).toBeDefined();
64
+ });
65
+ it('has contrasting background/foreground', () => {
66
+ expect(themes.light.background).not.toBe(themes.light.foreground);
67
+ expect(themes.dark.background).not.toBe(themes.dark.foreground);
68
+ });
69
+ });
70
+ describe('generateCSSVariables', () => {
71
+ it('generates CSS custom properties', () => {
72
+ const css = generateCSSVariables('light');
73
+ expect(css).toContain('--color-primary');
74
+ expect(css).toContain('--color-background');
75
+ expect(css).toContain('--spacing-4');
76
+ });
77
+ it('generates different values for dark theme', () => {
78
+ const lightCSS = generateCSSVariables('light');
79
+ const darkCSS = generateCSSVariables('dark');
80
+ expect(lightCSS).not.toBe(darkCSS);
81
+ });
82
+ it('includes spacing variables', () => {
83
+ const css = generateCSSVariables('light');
84
+ expect(css).toContain('--spacing-0');
85
+ expect(css).toContain('--spacing-1');
86
+ expect(css).toContain('--spacing-8');
87
+ });
88
+ it('includes typography variables', () => {
89
+ const css = generateCSSVariables('light');
90
+ expect(css).toContain('--font-size-sm');
91
+ expect(css).toContain('--font-size-base');
92
+ expect(css).toContain('--font-weight-bold');
93
+ });
94
+ });
95
+ });
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
@@ -23,11 +23,14 @@
23
23
  "react": "^18.3.1"
24
24
  },
25
25
  "devDependencies": {
26
+ "@testing-library/jest-dom": "^6.9.1",
27
+ "@testing-library/react": "^14.3.1",
26
28
  "@types/dockerode": "^3.3.31",
27
29
  "@types/node": "^22.10.0",
28
30
  "@types/react": "^18.3.12",
29
31
  "@vitest/coverage-v8": "^4.0.18",
30
32
  "ink-testing-library": "^4.0.0",
33
+ "jsdom": "^27.4.0",
31
34
  "memfs": "^4.56.10",
32
35
  "tsx": "^4.19.2",
33
36
  "typescript": "^5.7.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tlc-claude-code",
3
- "version": "1.2.27",
3
+ "version": "1.2.28",
4
4
  "description": "TLC - Test Led Coding for Claude Code",
5
5
  "bin": {
6
6
  "tlc": "./bin/tlc.js",
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Base Adapter - Common interface for all model adapters
3
+ */
4
+
5
+ /**
6
+ * Schema for standardized review response
7
+ */
8
+ const REVIEW_RESPONSE_SCHEMA = {
9
+ issues: 'array', // Array of { id, severity, message, line?, suggestion? }
10
+ suggestions: 'array', // Array of strings
11
+ score: 'number', // 0-100 quality score
12
+ model: 'string', // Model name
13
+ tokensUsed: 'number', // Tokens consumed
14
+ cost: 'number', // Cost in USD
15
+ };
16
+
17
+ /**
18
+ * Base adapter class - all model adapters extend this
19
+ */
20
+ class BaseAdapter {
21
+ constructor(config = {}) {
22
+ this.config = config;
23
+ this.name = config.name || 'unknown';
24
+ }
25
+
26
+ /**
27
+ * Review code for issues and suggestions
28
+ * @param {string} code - Code to review
29
+ * @param {Object} context - Additional context
30
+ * @returns {Promise<Object>} Standardized review response
31
+ */
32
+ async review(code, context = {}) {
33
+ throw new Error('Not implemented: review');
34
+ }
35
+
36
+ /**
37
+ * Analyze codebase for patterns
38
+ * @param {string} codebase - Codebase path or content
39
+ * @param {string} query - Analysis query
40
+ * @returns {Promise<Object>} Analysis result
41
+ */
42
+ async analyze(codebase, query) {
43
+ throw new Error('Not implemented: analyze');
44
+ }
45
+
46
+ /**
47
+ * Get current usage stats
48
+ * @returns {Object} Usage stats { daily, monthly, requests }
49
+ */
50
+ getUsage() {
51
+ return { daily: 0, monthly: 0, requests: 0 };
52
+ }
53
+
54
+ /**
55
+ * Estimate cost for a request
56
+ * @param {number} tokens - Estimated tokens
57
+ * @returns {number} Estimated cost in USD
58
+ */
59
+ estimateCost(tokens) {
60
+ return 0;
61
+ }
62
+
63
+ /**
64
+ * Check if adapter can afford a request
65
+ * @param {number} estimatedCost - Estimated cost
66
+ * @returns {boolean} Whether request is within budget
67
+ */
68
+ canAfford(estimatedCost = 0) {
69
+ return true;
70
+ }
71
+
72
+ /**
73
+ * Create empty but valid response
74
+ * @returns {Object} Empty review response
75
+ */
76
+ createEmptyResponse() {
77
+ return {
78
+ issues: [],
79
+ suggestions: [],
80
+ score: 100,
81
+ model: this.name,
82
+ tokensUsed: 0,
83
+ cost: 0,
84
+ };
85
+ }
86
+
87
+ /**
88
+ * Validate response against schema
89
+ * @param {Object} response - Response to validate
90
+ * @returns {boolean} Whether response is valid
91
+ */
92
+ static validateResponse(response) {
93
+ if (!response) return false;
94
+
95
+ // Check required fields
96
+ for (const [field, type] of Object.entries(REVIEW_RESPONSE_SCHEMA)) {
97
+ if (!(field in response)) return false;
98
+
99
+ if (type === 'array' && !Array.isArray(response[field])) return false;
100
+ if (type === 'number' && typeof response[field] !== 'number') return false;
101
+ if (type === 'string' && typeof response[field] !== 'string') return false;
102
+ }
103
+
104
+ // Validate score range
105
+ if (response.score < 0 || response.score > 100) return false;
106
+
107
+ return true;
108
+ }
109
+ }
110
+
111
+ module.exports = {
112
+ BaseAdapter,
113
+ REVIEW_RESPONSE_SCHEMA,
114
+ };
@@ -0,0 +1,90 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { BaseAdapter, REVIEW_RESPONSE_SCHEMA } from './base-adapter.js';
3
+
4
+ describe('BaseAdapter', () => {
5
+ describe('interface', () => {
6
+ it('has required methods', () => {
7
+ const adapter = new BaseAdapter({ name: 'test' });
8
+
9
+ expect(typeof adapter.review).toBe('function');
10
+ expect(typeof adapter.analyze).toBe('function');
11
+ expect(typeof adapter.getUsage).toBe('function');
12
+ expect(typeof adapter.estimateCost).toBe('function');
13
+ expect(typeof adapter.canAfford).toBe('function');
14
+ });
15
+
16
+ it('has name property', () => {
17
+ const adapter = new BaseAdapter({ name: 'claude' });
18
+ expect(adapter.name).toBe('claude');
19
+ });
20
+
21
+ it('throws on unimplemented review', async () => {
22
+ const adapter = new BaseAdapter({ name: 'test' });
23
+ await expect(adapter.review('')).rejects.toThrow('Not implemented');
24
+ });
25
+
26
+ it('throws on unimplemented analyze', async () => {
27
+ const adapter = new BaseAdapter({ name: 'test' });
28
+ await expect(adapter.analyze('', '')).rejects.toThrow('Not implemented');
29
+ });
30
+ });
31
+
32
+ describe('response validation', () => {
33
+ it('validates correct response', () => {
34
+ const response = {
35
+ issues: [{ id: '1', severity: 'high', message: 'Test' }],
36
+ suggestions: ['Suggestion 1'],
37
+ score: 75,
38
+ model: 'claude',
39
+ tokensUsed: 1000,
40
+ cost: 0.01,
41
+ };
42
+
43
+ expect(BaseAdapter.validateResponse(response)).toBe(true);
44
+ });
45
+
46
+ it('rejects missing required fields', () => {
47
+ const response = {
48
+ issues: [],
49
+ // missing: suggestions, score, model, tokensUsed, cost
50
+ };
51
+
52
+ expect(BaseAdapter.validateResponse(response)).toBe(false);
53
+ });
54
+
55
+ it('rejects invalid score', () => {
56
+ const response = {
57
+ issues: [],
58
+ suggestions: [],
59
+ score: 150, // Invalid: > 100
60
+ model: 'test',
61
+ tokensUsed: 0,
62
+ cost: 0,
63
+ };
64
+
65
+ expect(BaseAdapter.validateResponse(response)).toBe(false);
66
+ });
67
+ });
68
+
69
+ describe('REVIEW_RESPONSE_SCHEMA', () => {
70
+ it('defines all required fields', () => {
71
+ expect(REVIEW_RESPONSE_SCHEMA).toHaveProperty('issues');
72
+ expect(REVIEW_RESPONSE_SCHEMA).toHaveProperty('suggestions');
73
+ expect(REVIEW_RESPONSE_SCHEMA).toHaveProperty('score');
74
+ expect(REVIEW_RESPONSE_SCHEMA).toHaveProperty('model');
75
+ expect(REVIEW_RESPONSE_SCHEMA).toHaveProperty('tokensUsed');
76
+ expect(REVIEW_RESPONSE_SCHEMA).toHaveProperty('cost');
77
+ });
78
+ });
79
+
80
+ describe('createEmptyResponse', () => {
81
+ it('creates valid empty response', () => {
82
+ const adapter = new BaseAdapter({ name: 'test' });
83
+ const response = adapter.createEmptyResponse();
84
+
85
+ expect(BaseAdapter.validateResponse(response)).toBe(true);
86
+ expect(response.issues).toEqual([]);
87
+ expect(response.model).toBe('test');
88
+ });
89
+ });
90
+ });