tlc-claude-code 0.8.7 → 0.9.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/bin/init.js +17 -4
- package/package.json +2 -7
- package/server/index.js +118 -0
- package/server.md +1 -1
package/bin/init.js
CHANGED
|
@@ -79,16 +79,29 @@ echo ""
|
|
|
79
79
|
# TODO: Implement full macOS/Linux support
|
|
80
80
|
`;
|
|
81
81
|
|
|
82
|
+
// Detect OS - WSL counts as Windows since user will double-click .bat from Explorer
|
|
83
|
+
const isWSL = process.platform === 'linux' && fs.existsSync('/mnt/c');
|
|
84
|
+
const isWindows = process.platform === 'win32' || isWSL;
|
|
85
|
+
const launcherFile = isWindows ? 'tlc-start.bat' : 'tlc-start.sh';
|
|
86
|
+
const launcherPath = path.join(projectDir, launcherFile);
|
|
87
|
+
|
|
88
|
+
// FAST PATH: If already initialized, just confirm and exit
|
|
89
|
+
if (fs.existsSync(launcherPath) && fs.existsSync(path.join(projectDir, '.tlc.json'))) {
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log(`[TLC] Already initialized. ${launcherFile} exists.`);
|
|
92
|
+
console.log('');
|
|
93
|
+
console.log('[TLC] To start: Double-click ' + launcherFile);
|
|
94
|
+
console.log('[TLC] To rebuild: tlc rebuild');
|
|
95
|
+
console.log('');
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
82
99
|
console.log('');
|
|
83
100
|
console.log(' ============================');
|
|
84
101
|
console.log(' TLC Project Init');
|
|
85
102
|
console.log(' ============================');
|
|
86
103
|
console.log('');
|
|
87
104
|
|
|
88
|
-
// Detect OS - WSL counts as Windows since user will double-click .bat from Explorer
|
|
89
|
-
const isWSL = process.platform === 'linux' && fs.existsSync('/mnt/c');
|
|
90
|
-
const isWindows = process.platform === 'win32' || isWSL;
|
|
91
|
-
|
|
92
105
|
if (isWindows) {
|
|
93
106
|
// Create Windows launcher
|
|
94
107
|
const batPath = path.join(projectDir, 'tlc-start.bat');
|
package/package.json
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tlc-claude-code",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "TLC - Test Led Coding for Claude Code",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tlc": "./bin/tlc.js",
|
|
7
|
-
"tlc-claude-code": "./bin/install.js"
|
|
8
|
-
"tlc-dashboard": "./dashboard/dist/index.js",
|
|
9
|
-
"tlc-server": "./bin/server.js",
|
|
10
|
-
"tlc-setup": "./bin/setup.js",
|
|
11
|
-
"tlc-init": "./bin/init.js",
|
|
12
|
-
"tlc-rebuild": "./bin/rebuild.js"
|
|
7
|
+
"tlc-claude-code": "./bin/install.js"
|
|
13
8
|
},
|
|
14
9
|
"files": [
|
|
15
10
|
"bin/",
|
package/server/index.js
CHANGED
|
@@ -211,6 +211,7 @@ app.get('/api/status', (req, res) => {
|
|
|
211
211
|
appPort,
|
|
212
212
|
testsPass: plan.testsPass || 0,
|
|
213
213
|
testsFail: plan.testsFail || 0,
|
|
214
|
+
tasks: plan.tasks?.length || 0,
|
|
214
215
|
bugsOpen: bugs.filter(b => b.status === 'open').length,
|
|
215
216
|
phase: plan.currentPhase,
|
|
216
217
|
phaseName: plan.currentPhaseName
|
|
@@ -235,6 +236,123 @@ app.get('/api/tasks', (req, res) => {
|
|
|
235
236
|
});
|
|
236
237
|
});
|
|
237
238
|
|
|
239
|
+
// Plan content endpoint
|
|
240
|
+
app.get('/api/plan', (req, res) => {
|
|
241
|
+
const plan = parsePlan(PROJECT_DIR);
|
|
242
|
+
let content = '';
|
|
243
|
+
|
|
244
|
+
// Try to read current phase plan file
|
|
245
|
+
const phasesDir = path.join(PROJECT_DIR, '.planning', 'phases');
|
|
246
|
+
if (plan.currentPhase && fs.existsSync(phasesDir)) {
|
|
247
|
+
const planFile = path.join(phasesDir, `${plan.currentPhase}-PLAN.md`);
|
|
248
|
+
if (fs.existsSync(planFile)) {
|
|
249
|
+
content = fs.readFileSync(planFile, 'utf-8');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Fallback to ROADMAP.md
|
|
254
|
+
if (!content) {
|
|
255
|
+
const roadmapFile = path.join(PROJECT_DIR, '.planning', 'ROADMAP.md');
|
|
256
|
+
if (fs.existsSync(roadmapFile)) {
|
|
257
|
+
content = fs.readFileSync(roadmapFile, 'utf-8');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
res.json({
|
|
262
|
+
phase: plan.currentPhase,
|
|
263
|
+
phaseName: plan.currentPhaseName,
|
|
264
|
+
content
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Test checklist endpoint
|
|
269
|
+
app.get('/api/tests', (req, res) => {
|
|
270
|
+
const plan = parsePlan(PROJECT_DIR);
|
|
271
|
+
let items = [];
|
|
272
|
+
|
|
273
|
+
// Try to read TESTS.md for current phase
|
|
274
|
+
const phasesDir = path.join(PROJECT_DIR, '.planning', 'phases');
|
|
275
|
+
if (plan.currentPhase && fs.existsSync(phasesDir)) {
|
|
276
|
+
const testsFile = path.join(phasesDir, `${plan.currentPhase}-TESTS.md`);
|
|
277
|
+
if (fs.existsSync(testsFile)) {
|
|
278
|
+
const content = fs.readFileSync(testsFile, 'utf-8');
|
|
279
|
+
// Parse checkboxes
|
|
280
|
+
const lines = content.split('\n');
|
|
281
|
+
for (const line of lines) {
|
|
282
|
+
const match = line.match(/^[-*]\s*\[([ x])\]\s*(.+)$/i);
|
|
283
|
+
if (match) {
|
|
284
|
+
items.push({
|
|
285
|
+
checked: match[1].toLowerCase() === 'x',
|
|
286
|
+
text: match[2].trim()
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
res.json({ items });
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// Bugs list endpoint
|
|
297
|
+
app.get('/api/bugs', (req, res) => {
|
|
298
|
+
const bugs = parseBugs(PROJECT_DIR);
|
|
299
|
+
res.json(bugs);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Changelog endpoint
|
|
303
|
+
app.get('/api/changelog', (req, res) => {
|
|
304
|
+
try {
|
|
305
|
+
const { execSync } = require('child_process');
|
|
306
|
+
const output = execSync('git log --oneline -20 --pretty=format:"%h|%s|%an|%ar"', {
|
|
307
|
+
cwd: PROJECT_DIR,
|
|
308
|
+
encoding: 'utf-8'
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const commits = output.trim().split('\n').filter(Boolean).map(line => {
|
|
312
|
+
const [hash, message, author, date] = line.split('|');
|
|
313
|
+
return { hash, message, author, date };
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
res.json({ commits });
|
|
317
|
+
} catch (e) {
|
|
318
|
+
res.json({ commits: [] });
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// Playwright endpoint
|
|
323
|
+
app.post('/api/playwright', (req, res) => {
|
|
324
|
+
addLog('test', '--- Running Playwright tests ---', 'info');
|
|
325
|
+
|
|
326
|
+
const testProcess = spawn('npx', ['playwright', 'test'], {
|
|
327
|
+
cwd: PROJECT_DIR,
|
|
328
|
+
env: { ...process.env, CI: 'true' },
|
|
329
|
+
shell: true
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
testProcess.stdout.on('data', (data) => {
|
|
333
|
+
const text = data.toString().trim();
|
|
334
|
+
if (text) {
|
|
335
|
+
broadcast('test-output', { data: text, stream: 'stdout' });
|
|
336
|
+
addLog('test', text);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
testProcess.stderr.on('data', (data) => {
|
|
341
|
+
const text = data.toString().trim();
|
|
342
|
+
if (text) {
|
|
343
|
+
broadcast('test-output', { data: text, stream: 'stderr' });
|
|
344
|
+
addLog('test', text, 'error');
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
testProcess.on('exit', (code) => {
|
|
349
|
+
broadcast('test-complete', { exitCode: code });
|
|
350
|
+
addLog('test', `Playwright ${code === 0 ? 'passed' : 'failed'}`, code === 0 ? 'success' : 'error');
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
res.json({ success: true });
|
|
354
|
+
});
|
|
355
|
+
|
|
238
356
|
app.post('/api/bug', (req, res) => {
|
|
239
357
|
const { description, url, screenshot, severity } = req.body;
|
|
240
358
|
|
package/server.md
CHANGED
|
@@ -4,7 +4,7 @@ Set up Docker-based development environment.
|
|
|
4
4
|
|
|
5
5
|
## Instructions for Claude
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**CRITICAL: Do NOT analyze the codebase. Do NOT check files. Do NOT run any detection. Just execute the two steps below immediately.**
|
|
8
8
|
|
|
9
9
|
### Step 1: Create the launcher
|
|
10
10
|
|