tlc-claude-code 1.7.0 → 1.8.0
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/package.json +1 -1
- package/server/lib/code-gate/first-commit-audit.js +138 -0
- package/server/lib/code-gate/first-commit-audit.test.js +203 -0
- package/server/lib/code-gate/multi-model-reviewer.js +172 -0
- package/server/lib/code-gate/multi-model-reviewer.test.js +217 -0
- package/server/lib/infra/infra-generator.js +331 -0
- package/server/lib/infra/infra-generator.test.js +146 -0
- package/server/lib/llm/adapters/api-adapter.js +95 -0
- package/server/lib/llm/adapters/api-adapter.test.js +81 -0
- package/server/lib/llm/adapters/codex-adapter.js +85 -0
- package/server/lib/llm/adapters/codex-adapter.test.js +54 -0
- package/server/lib/llm/adapters/gemini-adapter.js +100 -0
- package/server/lib/llm/adapters/gemini-adapter.test.js +54 -0
- package/server/lib/llm/index.js +109 -0
- package/server/lib/llm/index.test.js +147 -0
- package/server/lib/llm/provider-executor.js +168 -0
- package/server/lib/llm/provider-executor.test.js +244 -0
- package/server/lib/llm/provider-registry.js +104 -0
- package/server/lib/llm/provider-registry.test.js +157 -0
- package/server/lib/llm/review-service.js +222 -0
- package/server/lib/llm/review-service.test.js +220 -0
- package/server/lib/shame/shame-registry.js +224 -0
- package/server/lib/shame/shame-registry.test.js +202 -0
- package/server/lib/standards/cleanup-dry-run.js +254 -0
- package/server/lib/standards/cleanup-dry-run.test.js +220 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleanup Dry-Run Mode Tests
|
|
3
|
+
*
|
|
4
|
+
* Preview what /tlc:cleanup would change without making modifications.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
planCleanup,
|
|
10
|
+
listFilesToMove,
|
|
11
|
+
listHardcodedUrls,
|
|
12
|
+
listInterfacesToExtract,
|
|
13
|
+
listFunctionsNeedingJsDoc,
|
|
14
|
+
planCommitMessages,
|
|
15
|
+
} = require('./cleanup-dry-run.js');
|
|
16
|
+
|
|
17
|
+
describe('Cleanup Dry-Run Mode', () => {
|
|
18
|
+
describe('listFilesToMove', () => {
|
|
19
|
+
it('lists files that would be moved from flat folders', async () => {
|
|
20
|
+
const mockGlob = vi.fn()
|
|
21
|
+
.mockResolvedValueOnce(['src/services/userService.js', 'src/services/orderService.js'])
|
|
22
|
+
.mockResolvedValueOnce([]) // interfaces
|
|
23
|
+
.mockResolvedValueOnce([]); // controllers
|
|
24
|
+
|
|
25
|
+
const result = await listFilesToMove('/project', { glob: mockGlob });
|
|
26
|
+
expect(result).toHaveLength(2);
|
|
27
|
+
expect(result[0]).toMatchObject({
|
|
28
|
+
source: expect.stringContaining('services'),
|
|
29
|
+
destination: expect.any(String),
|
|
30
|
+
reason: expect.any(String),
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('returns empty for clean project', async () => {
|
|
35
|
+
const mockGlob = vi.fn().mockResolvedValue([]);
|
|
36
|
+
|
|
37
|
+
const result = await listFilesToMove('/project', { glob: mockGlob });
|
|
38
|
+
expect(result).toHaveLength(0);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('listHardcodedUrls', () => {
|
|
43
|
+
it('lists hardcoded URLs that would be extracted', async () => {
|
|
44
|
+
const mockGlob = vi.fn().mockResolvedValue(['src/api.js']);
|
|
45
|
+
const mockReadFile = vi.fn().mockResolvedValue(
|
|
46
|
+
'const api = "http://localhost:3000/api";\nconst port = 8080;'
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const result = await listHardcodedUrls('/project', {
|
|
50
|
+
glob: mockGlob,
|
|
51
|
+
readFile: mockReadFile,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(result.length).toBeGreaterThan(0);
|
|
55
|
+
expect(result[0]).toMatchObject({
|
|
56
|
+
file: expect.any(String),
|
|
57
|
+
value: expect.any(String),
|
|
58
|
+
suggestedEnvVar: expect.any(String),
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('listInterfacesToExtract', () => {
|
|
64
|
+
it('lists interfaces that would be extracted', async () => {
|
|
65
|
+
const mockGlob = vi.fn().mockResolvedValue(['src/user/user.service.ts']);
|
|
66
|
+
const mockReadFile = vi.fn().mockResolvedValue(
|
|
67
|
+
'interface UserDTO {\n id: number;\n name: string;\n}\n\nexport class UserService {}'
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const result = await listInterfacesToExtract('/project', {
|
|
71
|
+
glob: mockGlob,
|
|
72
|
+
readFile: mockReadFile,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(result).toHaveLength(1);
|
|
76
|
+
expect(result[0]).toMatchObject({
|
|
77
|
+
file: expect.any(String),
|
|
78
|
+
interfaceName: 'UserDTO',
|
|
79
|
+
targetPath: expect.any(String),
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('listFunctionsNeedingJsDoc', () => {
|
|
85
|
+
it('lists functions needing JSDoc', async () => {
|
|
86
|
+
const mockGlob = vi.fn().mockResolvedValue(['src/utils.js']);
|
|
87
|
+
const mockReadFile = vi.fn().mockResolvedValue(
|
|
88
|
+
'export function calculateTotal(items, tax) {\n return items.reduce((s, i) => s + i.price, 0) * (1 + tax);\n}'
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const result = await listFunctionsNeedingJsDoc('/project', {
|
|
92
|
+
glob: mockGlob,
|
|
93
|
+
readFile: mockReadFile,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(result).toHaveLength(1);
|
|
97
|
+
expect(result[0]).toMatchObject({
|
|
98
|
+
file: expect.any(String),
|
|
99
|
+
functionName: 'calculateTotal',
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('planCommitMessages', () => {
|
|
105
|
+
it('shows planned commit messages', () => {
|
|
106
|
+
const plan = {
|
|
107
|
+
filesToMove: [{ source: 'src/services/user.js', destination: 'src/user/user.js' }],
|
|
108
|
+
hardcodedUrls: [{ file: 'src/api.js', value: 'http://localhost:3000' }],
|
|
109
|
+
interfacesToExtract: [{ file: 'src/user.service.ts', interfaceName: 'UserDTO' }],
|
|
110
|
+
functionsNeedingJsDoc: [{ file: 'src/utils.js', functionName: 'calculate' }],
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const commits = planCommitMessages(plan);
|
|
114
|
+
expect(commits.length).toBeGreaterThan(0);
|
|
115
|
+
expect(commits.every(c => typeof c === 'string')).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('returns empty for clean project', () => {
|
|
119
|
+
const plan = {
|
|
120
|
+
filesToMove: [],
|
|
121
|
+
hardcodedUrls: [],
|
|
122
|
+
interfacesToExtract: [],
|
|
123
|
+
functionsNeedingJsDoc: [],
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const commits = planCommitMessages(plan);
|
|
127
|
+
expect(commits).toHaveLength(0);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('planCleanup', () => {
|
|
132
|
+
it('returns structured report with no side effects', async () => {
|
|
133
|
+
const mockGlob = vi.fn().mockResolvedValue([]);
|
|
134
|
+
const mockReadFile = vi.fn().mockResolvedValue('');
|
|
135
|
+
|
|
136
|
+
const result = await planCleanup('/project', {
|
|
137
|
+
glob: mockGlob,
|
|
138
|
+
readFile: mockReadFile,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(result).toMatchObject({
|
|
142
|
+
filesToMove: expect.any(Array),
|
|
143
|
+
hardcodedUrls: expect.any(Array),
|
|
144
|
+
interfacesToExtract: expect.any(Array),
|
|
145
|
+
functionsNeedingJsDoc: expect.any(Array),
|
|
146
|
+
plannedCommits: expect.any(Array),
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('handles multiple issue types', async () => {
|
|
151
|
+
const mockGlob = vi.fn()
|
|
152
|
+
.mockResolvedValueOnce(['src/services/userService.js']) // flat: services
|
|
153
|
+
.mockResolvedValueOnce([]) // flat: interfaces
|
|
154
|
+
.mockResolvedValueOnce([]) // flat: controllers
|
|
155
|
+
.mockResolvedValueOnce(['src/api.js']) // hardcoded urls
|
|
156
|
+
.mockResolvedValueOnce([]) // interfaces
|
|
157
|
+
.mockResolvedValueOnce([]); // jsdoc
|
|
158
|
+
const mockReadFile = vi.fn().mockResolvedValue(
|
|
159
|
+
'const url = "http://localhost:3000";'
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
const result = await planCleanup('/project', {
|
|
163
|
+
glob: mockGlob,
|
|
164
|
+
readFile: mockReadFile,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
expect(result.filesToMove.length + result.hardcodedUrls.length).toBeGreaterThan(0);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it('includes before/after preview for transforms', async () => {
|
|
171
|
+
const mockGlob = vi.fn()
|
|
172
|
+
.mockResolvedValueOnce([]) // flat: services
|
|
173
|
+
.mockResolvedValueOnce([]) // flat: interfaces
|
|
174
|
+
.mockResolvedValueOnce([]) // flat: controllers
|
|
175
|
+
.mockResolvedValueOnce(['src/api.js']) // hardcoded urls
|
|
176
|
+
.mockResolvedValueOnce([]) // interfaces
|
|
177
|
+
.mockResolvedValueOnce([]); // jsdoc
|
|
178
|
+
const mockReadFile = vi.fn().mockResolvedValue(
|
|
179
|
+
'const api = "http://localhost:3000/api";'
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const result = await planCleanup('/project', {
|
|
183
|
+
glob: mockGlob,
|
|
184
|
+
readFile: mockReadFile,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (result.hardcodedUrls.length > 0) {
|
|
188
|
+
expect(result.hardcodedUrls[0].suggestedEnvVar).toBeDefined();
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('makes no fs.writeFileSync calls in dry-run', async () => {
|
|
193
|
+
const writeFile = vi.fn();
|
|
194
|
+
const mockGlob = vi.fn().mockResolvedValue([]);
|
|
195
|
+
const mockReadFile = vi.fn().mockResolvedValue('');
|
|
196
|
+
|
|
197
|
+
await planCleanup('/project', {
|
|
198
|
+
glob: mockGlob,
|
|
199
|
+
readFile: mockReadFile,
|
|
200
|
+
writeFile,
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
expect(writeFile).not.toHaveBeenCalled();
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('makes no git operations in dry-run', async () => {
|
|
207
|
+
const exec = vi.fn();
|
|
208
|
+
const mockGlob = vi.fn().mockResolvedValue([]);
|
|
209
|
+
const mockReadFile = vi.fn().mockResolvedValue('');
|
|
210
|
+
|
|
211
|
+
await planCleanup('/project', {
|
|
212
|
+
glob: mockGlob,
|
|
213
|
+
readFile: mockReadFile,
|
|
214
|
+
exec,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
expect(exec).not.toHaveBeenCalled();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
});
|