tlc-claude-code 1.2.28 → 1.2.29

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/README.md CHANGED
@@ -58,10 +58,12 @@ No manual testing. No "does this work?" No vibes.
58
58
  ### Then Just Run
59
59
 
60
60
  ```bash
61
- /tlc
61
+ /tlc:next
62
62
  ```
63
63
 
64
- TLC knows where you are and what's next.
64
+ Shows what's next, asks "Proceed? [Y/n]", then executes. That's it.
65
+
66
+ Or use `/tlc` for the full dashboard.
65
67
 
66
68
  ---
67
69
 
@@ -70,6 +72,8 @@ TLC knows where you are and what's next.
70
72
  ### For Solo Developers
71
73
 
72
74
  - **Test-first by default** — Claude writes tests before code
75
+ - **Auto-parallelization** — Up to 10 agents run independent tasks simultaneously
76
+ - **`/tlc:next`** — One command to progress. No decisions needed.
73
77
  - **Smart dashboard** — See progress, run actions
74
78
  - **Coverage gaps** — Find and fix untested code
75
79
  - **Auto-fix** — Automatically repair failing tests
@@ -95,10 +99,11 @@ TLC knows where you are and what's next.
95
99
 
96
100
  | Command | What It Does |
97
101
  |---------|--------------|
98
- | `/tlc` | **Smart entry pointknows what's next** |
102
+ | `/tlc:next` | **Just do itshows next action, asks once, executes** |
103
+ | `/tlc` | Smart dashboard — full status view |
99
104
  | `/tlc:new-project` | Start new project with roadmap |
100
105
  | `/tlc:init` | Add TLC to existing codebase |
101
- | `/tlc:build` | Write tests → implement verify |
106
+ | `/tlc:build` | Write tests → implement (auto-parallelizes up to 10 agents) |
102
107
  | `/tlc:coverage` | Find and fix untested code |
103
108
  | `/tlc:quality` | Test quality scoring |
104
109
  | `/tlc:autofix` | Auto-repair failing tests |
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "tlc-claude-code",
3
- "version": "1.2.28",
3
+ "version": "1.2.29",
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"
7
+ "tlc-claude-code": "./bin/install.js",
8
+ "tlc-docs": "./scripts/project-docs.js"
8
9
  },
9
10
  "files": [
10
11
  "bin/",
@@ -16,6 +17,8 @@
16
17
  "server/package.json",
17
18
  "server/package-lock.json",
18
19
  "server/setup.sh",
20
+ "scripts/",
21
+ "templates/",
19
22
  "docker-compose.dev.yml",
20
23
  "start-dev.ps1",
21
24
  "start-dev.sh",
@@ -26,7 +29,11 @@
26
29
  ],
27
30
  "scripts": {
28
31
  "build": "cd dashboard && npm run build",
29
- "prepublishOnly": "npm run build"
32
+ "prepublishOnly": "npm run build",
33
+ "docs": "node scripts/docs-update.js",
34
+ "docs:check": "node scripts/docs-update.js --check",
35
+ "docs:screenshots": "node scripts/generate-screenshots.js",
36
+ "docs:capture": "node scripts/capture-screenshots.js"
30
37
  },
31
38
  "repository": {
32
39
  "type": "git",
@@ -41,5 +48,9 @@
41
48
  "testing"
42
49
  ],
43
50
  "author": "Jurgen Calleja",
44
- "license": "MIT"
51
+ "license": "MIT",
52
+ "devDependencies": {
53
+ "playwright": "^1.58.1",
54
+ "text-to-image": "^8.0.1"
55
+ }
45
56
  }
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Capture real screenshots using Playwright
4
+ *
5
+ * Usage:
6
+ * node scripts/capture-screenshots.js # All screenshots
7
+ * node scripts/capture-screenshots.js dashboard # Dashboard only
8
+ * node scripts/capture-screenshots.js terminal # Terminal mockups only
9
+ */
10
+
11
+ const { chromium } = require('playwright');
12
+ const fs = require('fs');
13
+ const path = require('path');
14
+ const { spawn } = require('child_process');
15
+
16
+ const OUTPUT_DIR = path.join(__dirname, '../docs/wiki/images');
17
+ const DASHBOARD_URL = process.env.TLC_DASHBOARD_URL || 'http://localhost:3147';
18
+
19
+ // Ensure output directory exists
20
+ if (!fs.existsSync(OUTPUT_DIR)) {
21
+ fs.mkdirSync(OUTPUT_DIR, { recursive: true });
22
+ }
23
+
24
+ /**
25
+ * Check if dashboard is running
26
+ */
27
+ async function isDashboardRunning() {
28
+ try {
29
+ const response = await fetch(DASHBOARD_URL);
30
+ return response.ok;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Start dashboard server
38
+ */
39
+ async function startDashboard() {
40
+ return new Promise((resolve, reject) => {
41
+ console.log('Starting TLC dashboard...');
42
+ const proc = spawn('node', ['server/index.js'], {
43
+ cwd: path.join(__dirname, '..'),
44
+ detached: true,
45
+ stdio: 'ignore',
46
+ });
47
+ proc.unref();
48
+
49
+ // Wait for server to be ready
50
+ let attempts = 0;
51
+ const check = setInterval(async () => {
52
+ attempts++;
53
+ if (await isDashboardRunning()) {
54
+ clearInterval(check);
55
+ resolve(proc);
56
+ } else if (attempts > 30) {
57
+ clearInterval(check);
58
+ reject(new Error('Dashboard failed to start'));
59
+ }
60
+ }, 1000);
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Capture dashboard screenshots
66
+ */
67
+ async function captureDashboard(browser) {
68
+ console.log('\nCapturing dashboard screenshots...');
69
+
70
+ const page = await browser.newPage();
71
+ await page.setViewportSize({ width: 1280, height: 800 });
72
+
73
+ try {
74
+ // Main dashboard
75
+ await page.goto(DASHBOARD_URL, { waitUntil: 'networkidle' });
76
+ await page.waitForTimeout(1000);
77
+ await page.screenshot({
78
+ path: path.join(OUTPUT_DIR, 'dashboard-overview.png'),
79
+ fullPage: false,
80
+ });
81
+ console.log(' ✓ dashboard-overview.png');
82
+
83
+ // Try to navigate to different tabs if they exist
84
+ const tabs = ['tasks', 'logs', 'team', 'settings'];
85
+ for (const tab of tabs) {
86
+ try {
87
+ const tabSelector = `[data-tab="${tab}"], [href="#${tab}"], button:has-text("${tab}")`;
88
+ const tabElement = await page.$(tabSelector);
89
+ if (tabElement) {
90
+ await tabElement.click();
91
+ await page.waitForTimeout(500);
92
+ await page.screenshot({
93
+ path: path.join(OUTPUT_DIR, `dashboard-${tab}.png`),
94
+ fullPage: false,
95
+ });
96
+ console.log(` ✓ dashboard-${tab}.png`);
97
+ }
98
+ } catch (e) {
99
+ // Tab might not exist, skip
100
+ }
101
+ }
102
+ } catch (error) {
103
+ console.log(` ⚠ Dashboard capture failed: ${error.message}`);
104
+ } finally {
105
+ await page.close();
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Generate terminal-style screenshots using text-to-image
111
+ */
112
+ async function captureTerminal() {
113
+ console.log('\nGenerating terminal screenshots...');
114
+
115
+ // Import the generator script
116
+ const generateScript = path.join(__dirname, 'generate-screenshots.js');
117
+ if (fs.existsSync(generateScript)) {
118
+ require(generateScript);
119
+ } else {
120
+ console.log(' ⚠ generate-screenshots.js not found, skipping terminal screenshots');
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Main function
126
+ */
127
+ async function main() {
128
+ const mode = process.argv[2] || 'all';
129
+ console.log(`Screenshot capture mode: ${mode}`);
130
+
131
+ let browser;
132
+ let dashboardStarted = false;
133
+
134
+ try {
135
+ if (mode === 'all' || mode === 'dashboard') {
136
+ // Check if dashboard is running
137
+ const running = await isDashboardRunning();
138
+
139
+ if (!running) {
140
+ console.log('Dashboard not running. Starting...');
141
+ try {
142
+ await startDashboard();
143
+ dashboardStarted = true;
144
+ } catch (e) {
145
+ console.log('Could not start dashboard, will use terminal mockups only');
146
+ }
147
+ }
148
+
149
+ if (await isDashboardRunning()) {
150
+ browser = await chromium.launch({ headless: true });
151
+ await captureDashboard(browser);
152
+ }
153
+ }
154
+
155
+ if (mode === 'all' || mode === 'terminal') {
156
+ await captureTerminal();
157
+ }
158
+
159
+ console.log('\n✓ Screenshot capture complete!');
160
+ } catch (error) {
161
+ console.error('Screenshot capture failed:', error.message);
162
+ process.exit(1);
163
+ } finally {
164
+ if (browser) {
165
+ await browser.close();
166
+ }
167
+ }
168
+ }
169
+
170
+ main();
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Documentation maintenance script
4
+ *
5
+ * Automatically:
6
+ * - Updates version references in docs
7
+ * - Checks for missing command documentation
8
+ * - Validates internal links
9
+ * - Regenerates screenshots if needed
10
+ *
11
+ * Usage:
12
+ * node scripts/docs-update.js # Full update
13
+ * node scripts/docs-update.js --check # Check only, no changes
14
+ * node scripts/docs-update.js --screenshots # Regenerate screenshots
15
+ */
16
+
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+
20
+ const ROOT = path.join(__dirname, '..');
21
+ const DOCS_DIR = path.join(ROOT, 'docs/wiki');
22
+ const COMMANDS_DIR = path.join(ROOT, '.claude/commands/tlc');
23
+ const PACKAGE_JSON = path.join(ROOT, 'package.json');
24
+
25
+ const args = process.argv.slice(2);
26
+ const checkOnly = args.includes('--check');
27
+ const screenshotsOnly = args.includes('--screenshots');
28
+
29
+ /**
30
+ * Get current version from package.json
31
+ */
32
+ function getVersion() {
33
+ const pkg = JSON.parse(fs.readFileSync(PACKAGE_JSON, 'utf-8'));
34
+ return pkg.version;
35
+ }
36
+
37
+ /**
38
+ * Get all command files
39
+ */
40
+ function getCommands() {
41
+ if (!fs.existsSync(COMMANDS_DIR)) return [];
42
+
43
+ return fs.readdirSync(COMMANDS_DIR)
44
+ .filter(f => f.endsWith('.md'))
45
+ .map(f => {
46
+ const content = fs.readFileSync(path.join(COMMANDS_DIR, f), 'utf-8');
47
+ const name = f.replace('.md', '');
48
+ const titleMatch = content.match(/^#\s+(.+)/m);
49
+ const title = titleMatch ? titleMatch[1] : name;
50
+
51
+ return { name, title, file: f };
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Check command-reference.md for missing commands
57
+ */
58
+ function checkCommandDocs() {
59
+ const commandRefPath = path.join(DOCS_DIR, 'command-reference.md');
60
+ if (!fs.existsSync(commandRefPath)) {
61
+ console.log('⚠ command-reference.md not found');
62
+ return { missing: [], outdated: [] };
63
+ }
64
+
65
+ const content = fs.readFileSync(commandRefPath, 'utf-8');
66
+ const commands = getCommands();
67
+
68
+ const missing = commands.filter(cmd => {
69
+ const pattern = new RegExp(`/tlc:${cmd.name}[^a-z]`, 'i');
70
+ return !pattern.test(content) && cmd.name !== 'tlc';
71
+ });
72
+
73
+ return { missing, total: commands.length };
74
+ }
75
+
76
+ /**
77
+ * Update version references in docs
78
+ */
79
+ function updateVersions() {
80
+ const version = getVersion();
81
+ const versionPattern = /v\d+\.\d+\.\d+/g;
82
+
83
+ let updated = 0;
84
+
85
+ const files = fs.readdirSync(DOCS_DIR)
86
+ .filter(f => f.endsWith('.md'));
87
+
88
+ for (const file of files) {
89
+ const filepath = path.join(DOCS_DIR, file);
90
+ let content = fs.readFileSync(filepath, 'utf-8');
91
+
92
+ const matches = content.match(versionPattern);
93
+ if (matches) {
94
+ const oldContent = content;
95
+ content = content.replace(versionPattern, `v${version}`);
96
+
97
+ if (content !== oldContent && !checkOnly) {
98
+ fs.writeFileSync(filepath, content);
99
+ updated++;
100
+ }
101
+ }
102
+ }
103
+
104
+ return updated;
105
+ }
106
+
107
+ /**
108
+ * Check for broken internal links
109
+ */
110
+ function checkLinks() {
111
+ const broken = [];
112
+
113
+ const files = fs.readdirSync(DOCS_DIR)
114
+ .filter(f => f.endsWith('.md'));
115
+
116
+ const existingPages = files.map(f => f.replace('.md', ''));
117
+
118
+ for (const file of files) {
119
+ const filepath = path.join(DOCS_DIR, file);
120
+ const content = fs.readFileSync(filepath, 'utf-8');
121
+
122
+ // Find markdown links
123
+ const linkPattern = /\[([^\]]+)\]\(([^)]+)\)/g;
124
+ let match;
125
+
126
+ while ((match = linkPattern.exec(content)) !== null) {
127
+ const [, , link] = match;
128
+
129
+ // Check internal links (no http/https)
130
+ if (!link.startsWith('http') && !link.startsWith('#')) {
131
+ const targetPage = link.replace(/\.md$/, '').replace(/^\//, '');
132
+
133
+ if (!existingPages.includes(targetPage) &&
134
+ !fs.existsSync(path.join(DOCS_DIR, link)) &&
135
+ !link.startsWith('images/')) {
136
+ broken.push({ file, link });
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ return broken;
143
+ }
144
+
145
+ /**
146
+ * Check screenshots exist
147
+ */
148
+ function checkScreenshots() {
149
+ const imagesDir = path.join(DOCS_DIR, 'images');
150
+ const missing = [];
151
+
152
+ if (!fs.existsSync(imagesDir)) {
153
+ return { missing: ['images directory not found'], existing: 0 };
154
+ }
155
+
156
+ const files = fs.readdirSync(DOCS_DIR)
157
+ .filter(f => f.endsWith('.md'));
158
+
159
+ const existingImages = fs.readdirSync(imagesDir)
160
+ .filter(f => f.endsWith('.png') || f.endsWith('.jpg'));
161
+
162
+ for (const file of files) {
163
+ const filepath = path.join(DOCS_DIR, file);
164
+ const content = fs.readFileSync(filepath, 'utf-8');
165
+
166
+ // Find image references
167
+ const imagePattern = /!\[([^\]]*)\]\(images\/([^)]+)\)/g;
168
+ let match;
169
+
170
+ while ((match = imagePattern.exec(content)) !== null) {
171
+ const [, , imagePath] = match;
172
+ if (!existingImages.includes(imagePath)) {
173
+ missing.push({ file, image: imagePath });
174
+ }
175
+ }
176
+ }
177
+
178
+ return { missing, existing: existingImages.length };
179
+ }
180
+
181
+ /**
182
+ * Main function
183
+ */
184
+ async function main() {
185
+ console.log('TLC Documentation Maintenance\n');
186
+ console.log('═'.repeat(50));
187
+
188
+ const version = getVersion();
189
+ console.log(`\nVersion: ${version}`);
190
+
191
+ // Screenshots only mode
192
+ if (screenshotsOnly) {
193
+ console.log('\nRegenerating screenshots...');
194
+ require('./generate-screenshots.js');
195
+ return;
196
+ }
197
+
198
+ // Check commands
199
+ console.log('\n📚 Commands');
200
+ const { missing, total } = checkCommandDocs();
201
+ console.log(` ${total} commands found`);
202
+ if (missing.length > 0) {
203
+ console.log(` ⚠ ${missing.length} missing from command-reference.md:`);
204
+ missing.forEach(cmd => console.log(` - /tlc:${cmd.name}`));
205
+ } else {
206
+ console.log(' ✓ All commands documented');
207
+ }
208
+
209
+ // Check versions
210
+ console.log('\n🏷️ Versions');
211
+ const updated = updateVersions();
212
+ if (checkOnly) {
213
+ console.log(` ${updated > 0 ? '⚠' : '✓'} ${updated} files need version update`);
214
+ } else {
215
+ console.log(` ✓ ${updated} files updated to v${version}`);
216
+ }
217
+
218
+ // Check links
219
+ console.log('\n🔗 Links');
220
+ const brokenLinks = checkLinks();
221
+ if (brokenLinks.length > 0) {
222
+ console.log(` ⚠ ${brokenLinks.length} broken links:`);
223
+ brokenLinks.forEach(({ file, link }) => console.log(` ${file}: ${link}`));
224
+ } else {
225
+ console.log(' ✓ All internal links valid');
226
+ }
227
+
228
+ // Check screenshots
229
+ console.log('\n📸 Screenshots');
230
+ const { missing: missingImages, existing } = checkScreenshots();
231
+ console.log(` ${existing} screenshots found`);
232
+ if (missingImages.length > 0) {
233
+ console.log(` ⚠ ${missingImages.length} missing screenshots:`);
234
+ missingImages.forEach(({ file, image }) => console.log(` ${file}: ${image}`));
235
+ } else {
236
+ console.log(' ✓ All referenced screenshots exist');
237
+ }
238
+
239
+ // Summary
240
+ console.log('\n' + '═'.repeat(50));
241
+
242
+ const issues = missing.length + brokenLinks.length + missingImages.length;
243
+ if (issues > 0) {
244
+ console.log(`\n⚠ ${issues} issue(s) found`);
245
+ if (!checkOnly) {
246
+ console.log('Run with --check to see issues without making changes');
247
+ }
248
+ } else {
249
+ console.log('\n✓ Documentation is up to date!');
250
+ }
251
+ }
252
+
253
+ main().catch(console.error);