@claudiv/cli 0.1.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/claudiv.js ADDED
@@ -0,0 +1,407 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Claudiv CLI — Universal Declarative Generation Platform
5
+ *
6
+ * Usage:
7
+ * npx @claudiv/cli <command> [options]
8
+ * claudiv <command> [options]
9
+ *
10
+ * Commands:
11
+ * new <name> Create a new .cdml file
12
+ * gen <name> Generate code from .cdml file
13
+ * reverse <file> Reverse-engineer file to .cdml
14
+ * watch <name> Watch .cdml file for changes
15
+ * help Show this help message
16
+ */
17
+
18
+ import { spawn } from 'child_process';
19
+ import { existsSync, writeFileSync, readdirSync } from 'fs';
20
+ import { join, basename, extname } from 'path';
21
+ import { fileURLToPath } from 'url';
22
+
23
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
24
+ const VERSION = '0.1.0';
25
+
26
+ // ─── Parse Arguments ───────────────────────────────────────────
27
+
28
+ const args = process.argv.slice(2);
29
+
30
+ // Parse flags
31
+ const flags = {};
32
+ const positional = [];
33
+ for (let i = 0; i < args.length; i++) {
34
+ const arg = args[i];
35
+ if (arg === '-s' || arg === '--spec') {
36
+ flags.spec = args[++i];
37
+ } else if (arg === '-g' || arg === '--gen') {
38
+ flags.gen = true;
39
+ } else if (arg === '-t' || arg === '--target') {
40
+ flags.target = args[++i];
41
+ } else if (arg === '-f' || arg === '--framework') {
42
+ flags.framework = args[++i];
43
+ } else if (arg === '-w' || arg === '--watch') {
44
+ flags.watch = true;
45
+ } else if (arg === '-o' || arg === '--output') {
46
+ flags.output = args[++i];
47
+ } else if (arg === '--dry-run') {
48
+ flags.dryRun = true;
49
+ } else if (arg === '-v' || arg === '--version') {
50
+ console.log(`claudiv ${VERSION}`);
51
+ process.exit(0);
52
+ } else if (arg === '-h' || arg === '--help') {
53
+ showHelp();
54
+ process.exit(0);
55
+ } else if (!arg.startsWith('-')) {
56
+ positional.push(arg);
57
+ }
58
+ }
59
+
60
+ // ─── Route Commands ────────────────────────────────────────────
61
+
62
+ switch (positional[0]) {
63
+ case 'new':
64
+ cmdNew(positional[1], flags);
65
+ break;
66
+ case 'gen':
67
+ cmdGen(positional[1], flags);
68
+ break;
69
+ case 'reverse':
70
+ cmdReverse(positional[1], flags);
71
+ break;
72
+ case 'watch':
73
+ cmdWatch(positional[1], flags);
74
+ break;
75
+ case 'help':
76
+ showHelp();
77
+ break;
78
+ case undefined:
79
+ // No command: look for .cdml in current directory and watch
80
+ cmdDefault(flags);
81
+ break;
82
+ default:
83
+ // Check if it's a .cdml file path
84
+ if (positional[0].endsWith('.cdml')) {
85
+ cmdGen(positional[0], flags);
86
+ } else {
87
+ console.error(`Unknown command: ${positional[0]}`);
88
+ console.log('Run "claudiv help" for usage information.');
89
+ process.exit(1);
90
+ }
91
+ }
92
+
93
+ // ─── Commands ──────────────────────────────────────────────────
94
+
95
+ /**
96
+ * claudiv new <name> [-s|--spec '<xml>'] [-g|--gen] [-t|--target <lang>]
97
+ */
98
+ function cmdNew(name, flags) {
99
+ if (!name) {
100
+ console.error('Usage: claudiv new <name> [options]');
101
+ console.log('');
102
+ console.log('Examples:');
103
+ console.log(' claudiv new myapp');
104
+ console.log(' claudiv new txt2img -s \'<txt2img lang="python" type="cli" gen="">...</txt2img>\'');
105
+ console.log(' claudiv new api -t python -f fastapi');
106
+ console.log(' claudiv new myapp -s \'<spec>\' -g');
107
+ process.exit(1);
108
+ }
109
+
110
+ // Strip .cdml extension if provided
111
+ const baseName = name.replace(/\.cdml$/, '');
112
+ const cdmlFile = `${baseName}.cdml`;
113
+ const cdmlPath = join(process.cwd(), cdmlFile);
114
+
115
+ if (existsSync(cdmlPath)) {
116
+ console.error(`File already exists: ${cdmlFile}`);
117
+ console.log(`Use "claudiv gen ${cdmlFile}" to generate from it.`);
118
+ process.exit(1);
119
+ }
120
+
121
+ let content;
122
+
123
+ if (flags.spec) {
124
+ // Use provided spec
125
+ content = flags.spec;
126
+ } else {
127
+ // Generate default template
128
+ const target = flags.target || 'html';
129
+ const framework = flags.framework ? ` framework="${flags.framework}"` : '';
130
+ content = `<${baseName} target="${target}"${framework} gen>
131
+ <!-- Describe what you want here -->
132
+ </${baseName}>
133
+ `;
134
+ }
135
+
136
+ writeFileSync(cdmlPath, content, 'utf-8');
137
+ console.log(`Created ${cdmlFile}`);
138
+
139
+ if (flags.gen) {
140
+ console.log(`Generating from ${cdmlFile}...`);
141
+ startEngine(cdmlPath, flags);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * claudiv gen <name> [-t|--target <component>] [-w|--watch] [-o|--output <file>]
147
+ *
148
+ * Examples:
149
+ * claudiv gen myapp # generate all from myapp.cdml
150
+ * claudiv gen myapp -t config # generate config component only
151
+ * claudiv gen txt2img -t config # generate txt2img.config
152
+ * claudiv gen myapp.cdml # explicit .cdml path
153
+ * claudiv gen myapp -w # generate + watch for changes
154
+ * claudiv gen myapp -o output.py # generate to specific file
155
+ */
156
+ function cmdGen(name, flags) {
157
+ if (!name) {
158
+ console.error('Usage: claudiv gen <name> [options]');
159
+ console.log('');
160
+ console.log('Examples:');
161
+ console.log(' claudiv gen myapp');
162
+ console.log(' claudiv gen myapp -t config');
163
+ console.log(' claudiv gen myapp -w');
164
+ console.log(' claudiv gen myapp -o output.py');
165
+ process.exit(1);
166
+ }
167
+
168
+ // Resolve .cdml file
169
+ const cdmlFile = name.endsWith('.cdml') ? name : `${name}.cdml`;
170
+ const cdmlPath = join(process.cwd(), cdmlFile);
171
+
172
+ if (!existsSync(cdmlPath)) {
173
+ console.error(`File not found: ${cdmlFile}`);
174
+ console.log(`Create it with: claudiv new ${name}`);
175
+ process.exit(1);
176
+ }
177
+
178
+ // If -t flag, it selects a component/target within the .cdml
179
+ if (flags.target) {
180
+ console.log(`Generating "${flags.target}" from ${cdmlFile}...`);
181
+ } else {
182
+ console.log(`Generating from ${cdmlFile}...`);
183
+ }
184
+
185
+ startEngine(cdmlPath, flags);
186
+ }
187
+
188
+ /**
189
+ * claudiv reverse <file> [-o|--output <name.cdml>]
190
+ *
191
+ * Examples:
192
+ * claudiv reverse api.py # → api.cdml
193
+ * claudiv reverse Button.tsx # → Button.cdml
194
+ * claudiv reverse backup.sh # → backup.cdml
195
+ * claudiv reverse styles.css # → styles.cdml
196
+ * claudiv reverse api.py -o spec.cdml # → spec.cdml
197
+ */
198
+ function cmdReverse(file, flags) {
199
+ if (!file) {
200
+ console.error('Usage: claudiv reverse <file> [options]');
201
+ console.log('');
202
+ console.log('Examples:');
203
+ console.log(' claudiv reverse api.py');
204
+ console.log(' claudiv reverse Button.tsx');
205
+ console.log(' claudiv reverse backup.sh');
206
+ console.log(' claudiv reverse api.py -o spec.cdml');
207
+ process.exit(1);
208
+ }
209
+
210
+ const filePath = join(process.cwd(), file);
211
+ if (!existsSync(filePath)) {
212
+ console.error(`File not found: ${file}`);
213
+ process.exit(1);
214
+ }
215
+
216
+ const base = basename(file, extname(file));
217
+ const outputFile = flags.output || `${base}.cdml`;
218
+ console.log(`Reverse engineering: ${file} → ${outputFile}`);
219
+
220
+ // TODO: Implement reverse generation via Claude
221
+ console.log('Reverse generation coming soon.');
222
+ }
223
+
224
+ /**
225
+ * claudiv watch <name>
226
+ *
227
+ * Watch .cdml file for changes and regenerate automatically.
228
+ */
229
+ function cmdWatch(name, flags) {
230
+ if (!name) {
231
+ console.error('Usage: claudiv watch <name>');
232
+ process.exit(1);
233
+ }
234
+
235
+ flags.watch = true;
236
+ cmdGen(name, flags);
237
+ }
238
+
239
+ /**
240
+ * Default: no command given. Look for .cdml files in cwd.
241
+ */
242
+ function cmdDefault(flags) {
243
+ // Look for .cdml files in current directory
244
+ const files = readdirSync(process.cwd());
245
+ const cdmlFiles = files.filter(f => f.endsWith('.cdml'));
246
+
247
+ if (cdmlFiles.length === 0) {
248
+ console.log('Claudiv — Claude in a Div');
249
+ console.log('');
250
+ console.log('No .cdml files found. Get started:');
251
+ console.log('');
252
+ console.log(' claudiv new myapp # Create myapp.cdml');
253
+ console.log(' claudiv new myapp -t python # Python project');
254
+ console.log(' claudiv new myapp -s \'<app gen>...</app>\' # With inline spec');
255
+ console.log(' claudiv new myapp -s \'<app gen>...</app>\' -g # Create + generate');
256
+ console.log('');
257
+ console.log(' claudiv gen myapp # Generate from myapp.cdml');
258
+ console.log(' claudiv gen myapp -t config # Generate specific target');
259
+ console.log(' claudiv gen myapp -w # Watch mode');
260
+ console.log('');
261
+ console.log(' claudiv reverse api.py # Reverse → api.cdml');
262
+ console.log('');
263
+ console.log(' claudiv help # Full help');
264
+ process.exit(0);
265
+ }
266
+
267
+ if (cdmlFiles.length === 1) {
268
+ console.log(`Found ${cdmlFiles[0]}, starting...`);
269
+ startEngine(join(process.cwd(), cdmlFiles[0]), flags);
270
+ } else {
271
+ console.log('Multiple .cdml files found:');
272
+ cdmlFiles.forEach((f, i) => console.log(` ${i + 1}. ${f}`));
273
+ console.log('');
274
+ console.log('Specify which file to use:');
275
+ console.log(` claudiv gen ${cdmlFiles[0]}`);
276
+ process.exit(0);
277
+ }
278
+ }
279
+
280
+ // ─── Engine ────────────────────────────────────────────────────
281
+
282
+ function startEngine(cdmlPath, flags) {
283
+ const mode = process.env.MODE || 'cli';
284
+ const envVars = { ...process.env, MODE: mode };
285
+
286
+ if (flags.target) envVars.CLAUDIV_TARGET = flags.target;
287
+ if (flags.output) envVars.CLAUDIV_OUTPUT = flags.output;
288
+ if (flags.watch) envVars.CLAUDIV_WATCH = '1';
289
+
290
+ const editor = spawn('node', [join(__dirname, '../dist/index.js'), cdmlPath], {
291
+ stdio: 'inherit',
292
+ cwd: process.cwd(),
293
+ env: envVars,
294
+ });
295
+
296
+ editor.on('exit', (exitCode) => {
297
+ process.exit(exitCode || 0);
298
+ });
299
+
300
+ editor.on('error', (err) => {
301
+ console.error(`Failed to start: ${err.message}`);
302
+ process.exit(1);
303
+ });
304
+ }
305
+
306
+ // ─── Help ──────────────────────────────────────────────────────
307
+
308
+ function showHelp() {
309
+ console.log(`
310
+ Claudiv ${VERSION} — Claude in a Div
311
+ Universal Declarative Generation Platform
312
+
313
+ USAGE
314
+ claudiv <command> [name] [options]
315
+ npx @claudiv/cli <command> [name] [options]
316
+
317
+ COMMANDS
318
+ new <name> Create a new .cdml file
319
+ gen <name> Generate code from .cdml file
320
+ reverse <file> Reverse-engineer existing file to .cdml
321
+ watch <name> Watch .cdml file and regenerate on changes
322
+ help Show this help message
323
+
324
+ OPTIONS
325
+ -s, --spec <xml> Inline .cdml content for 'new' command
326
+ -g, --gen Immediately generate after 'new'
327
+ -t, --target <name> Target component or language
328
+ -f, --framework <fw> Framework (fastapi, express, nextjs, etc.)
329
+ -o, --output <file> Output file path
330
+ -w, --watch Watch mode (regenerate on save)
331
+ --dry-run Preview without writing files
332
+ -v, --version Show version
333
+ -h, --help Show help
334
+
335
+ EXAMPLES
336
+
337
+ Getting Started:
338
+ claudiv new myapp Create myapp.cdml
339
+ claudiv new myapp -t python Create Python project spec
340
+ claudiv new myapp -g Create and generate immediately
341
+
342
+ Inline Spec:
343
+ claudiv new txt2img -s '<txt2img lang="python" type="cli" gen="">
344
+ <ai provider="openai" />
345
+ <config apikey organizationid />
346
+ <args input="text|file, size" output="filename, format" />
347
+ </txt2img>'
348
+
349
+ claudiv new txt2img -s '<txt2img lang="python" type="cli" gen="">
350
+ <ai provider="openai" />
351
+ <config apikey organizationid />
352
+ <args input="text|file, size" output="filename, format" />
353
+ </txt2img>' -g Create + generate immediately
354
+
355
+ Generate:
356
+ claudiv gen myapp Generate all from myapp.cdml
357
+ claudiv gen myapp.cdml Explicit .cdml path
358
+ claudiv gen txt2img -t config Generate only config component
359
+ claudiv gen myapp -w Watch mode
360
+ claudiv gen myapp -o output.py Custom output file
361
+
362
+ Component Target (-t):
363
+ claudiv gen txt2img -t config Generates txt2img.config
364
+ claudiv gen myapp -t api Generates myapp.api
365
+ claudiv gen myapp -t database Generates myapp.database
366
+
367
+ Reverse Engineering:
368
+ claudiv reverse api.py → api.cdml
369
+ claudiv reverse Button.tsx → Button.cdml
370
+ claudiv reverse backup.sh → backup.cdml
371
+ claudiv reverse styles.css → styles.cdml
372
+ claudiv reverse api.py -o spec.cdml Custom output name
373
+
374
+ Watch Mode:
375
+ claudiv watch myapp Watch myapp.cdml for changes
376
+ claudiv gen myapp -w Same as above
377
+
378
+ Run in Empty Folder:
379
+ npx @claudiv/cli new myapp -t python -g
380
+
381
+ FILE FORMAT
382
+ Input: <name>.cdml
383
+ Output: <name>.<ext> (based on target language)
384
+
385
+ Examples:
386
+ app.cdml → app.html (target=html)
387
+ api.cdml → api.py (target=python)
388
+ backup.cdml → backup.sh (target=bash)
389
+ deploy.cdml → deploy.yaml (target=kubernetes)
390
+
391
+ SPEC SYNTAX
392
+ <element-name [attributes] gen[="instructions"]>
393
+ Natural language description
394
+ </element-name>
395
+
396
+ Action attributes:
397
+ gen Generate new code
398
+ gen="..." Generate with specific instructions
399
+ retry Regenerate (not satisfied)
400
+ undo Revert to previous version
401
+ lock Protect from regeneration
402
+ unlock Allow regeneration of locked element
403
+
404
+ MORE INFO
405
+ https://github.com/claudiv-ai/claudiv
406
+ `);
407
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Anthropic API integration for direct API access
3
+ */
4
+ import type { HierarchyContext } from '@claudiv/core';
5
+ export declare class ClaudeAPIClient {
6
+ private client;
7
+ constructor(apiKey: string);
8
+ /**
9
+ * Send prompt to Claude API and stream response
10
+ */
11
+ sendPrompt(userMessage: string, context: HierarchyContext): AsyncGenerator<string>;
12
+ /**
13
+ * Check if API is available
14
+ */
15
+ checkAvailable(): Promise<boolean>;
16
+ /**
17
+ * Build system prompt with context
18
+ */
19
+ private buildSystemPrompt;
20
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Anthropic API integration for direct API access
3
+ */
4
+ import Anthropic from '@anthropic-ai/sdk';
5
+ import { logger } from './utils/logger.js';
6
+ import { buildPromptContext } from '@claudiv/core';
7
+ export class ClaudeAPIClient {
8
+ client;
9
+ constructor(apiKey) {
10
+ this.client = new Anthropic({
11
+ apiKey,
12
+ });
13
+ }
14
+ /**
15
+ * Send prompt to Claude API and stream response
16
+ */
17
+ async *sendPrompt(userMessage, context) {
18
+ logger.processing('Sending request to Claude API...');
19
+ // Build system prompt with hierarchy context
20
+ const systemPrompt = this.buildSystemPrompt(context);
21
+ try {
22
+ // Stream from API
23
+ const stream = await this.client.messages.stream({
24
+ model: 'claude-opus-4-6',
25
+ max_tokens: 4096,
26
+ system: systemPrompt,
27
+ messages: [
28
+ {
29
+ role: 'user',
30
+ content: userMessage,
31
+ },
32
+ ],
33
+ });
34
+ let chunkCount = 0;
35
+ for await (const chunk of stream) {
36
+ if (chunk.type === 'content_block_delta' &&
37
+ chunk.delta.type === 'text_delta') {
38
+ chunkCount++;
39
+ yield chunk.delta.text;
40
+ }
41
+ }
42
+ logger.debug(`Received ${chunkCount} chunks from API`);
43
+ }
44
+ catch (error) {
45
+ const err = error;
46
+ logger.error(`Claude API error: ${err.message}`);
47
+ throw error;
48
+ }
49
+ }
50
+ /**
51
+ * Check if API is available
52
+ */
53
+ async checkAvailable() {
54
+ try {
55
+ // Test with a minimal request
56
+ await this.client.messages.create({
57
+ model: 'claude-opus-4-6',
58
+ max_tokens: 10,
59
+ messages: [{ role: 'user', content: 'test' }],
60
+ });
61
+ return true;
62
+ }
63
+ catch (error) {
64
+ logger.error('Claude API not available');
65
+ return false;
66
+ }
67
+ }
68
+ /**
69
+ * Build system prompt with context
70
+ */
71
+ buildSystemPrompt(context) {
72
+ const contextStr = buildPromptContext(context);
73
+ return `You are an AI assistant helping generate HTML/CSS code from natural language requests.
74
+
75
+ ${contextStr}
76
+
77
+ **CRITICAL IMPLEMENTATION REQUIREMENTS:**
78
+ ⚠️ NEVER use placeholder comments like "<!-- Child components render here -->" or "<!-- TODO: implement X -->"
79
+ ⚠️ If the user request lists NESTED COMPONENTS TO IMPLEMENT, you MUST generate complete, working HTML/CSS for EVERY single one
80
+ ⚠️ Each nested component specification MUST result in actual HTML elements with proper styling, NOT comments
81
+ ⚠️ If you see a list like "1. <nav-menu>", "2. <pages>", etc., these are REQUIRED components that need full implementations
82
+
83
+ **Instructions:**
84
+ 1. Structure your response as XML elements (NOT plain text)
85
+ 2. ${context.existingCode ? 'Modify ONLY what the user requested, keeping everything else exactly the same' : 'Generate the necessary HTML/CSS code appropriate for this context'}
86
+ 3. Consider the hierarchy - you're working on: ${context.elementPath}
87
+ 4. If referenced elements are provided above, use their definitions to inform your implementation
88
+ 5. If the user request contains a section "NESTED COMPONENTS TO IMPLEMENT", you MUST implement EACH ONE with complete HTML/CSS:
89
+ - Create actual HTML elements for each component
90
+ - Add proper CSS styling for each component
91
+ - Use the attributes as specifications for styling and behavior
92
+ - Use the content as guidance for what to include
93
+ - Do NOT use placeholder comments - implement the actual functionality
94
+ - **CRITICAL EXCEPTION - LOCKED COMPONENTS**:
95
+ - Components marked with "[LOCKED - DO NOT REGENERATE]" are already implemented
96
+ - You MUST preserve locked components exactly as they are
97
+ - Do NOT regenerate, modify, or replace locked components
98
+ - Include them as-is or reference them in your generated code
99
+ - Only implement components that are NOT marked as locked
100
+
101
+ **Response Format (use semantic XML tags, NOT plain text):**
102
+ <changes>Brief description of what was created/modified</changes>
103
+ <details>
104
+ <tag-name>Specific detail about the implementation</tag-name>
105
+ <another-tag>Another detail</another-tag>
106
+ </details>
107
+ <summary>Overall summary</summary>
108
+
109
+ IMPORTANT:
110
+ - Use semantic tag names that describe the content (e.g., <styling>, <background>, <effect>, <feature>, etc.)
111
+ - DO NOT use plain text or bullet points
112
+ - Structure your response with nested XML elements
113
+ - Still include code blocks with \`\`\`html and \`\`\`css for implementation
114
+ - EVERY nested component must be fully implemented, not just mentioned in comments
115
+ `;
116
+ }
117
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Claude Code CLI integration via subprocess
3
+ */
4
+ import type { HierarchyContext } from '@claudiv/core';
5
+ export declare class ClaudeCLIClient {
6
+ /**
7
+ * Send prompt to Claude Code CLI and stream response
8
+ */
9
+ sendPrompt(userMessage: string, context: HierarchyContext): AsyncGenerator<string>;
10
+ /**
11
+ * Check if Claude CLI is installed
12
+ */
13
+ checkAvailable(): Promise<boolean>;
14
+ /**
15
+ * Build prompt with hierarchy context
16
+ */
17
+ private buildPrompt;
18
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Claude Code CLI integration via subprocess
3
+ */
4
+ import { spawn } from 'child_process';
5
+ import { logger } from './utils/logger.js';
6
+ import { buildPromptContext } from '@claudiv/core';
7
+ export class ClaudeCLIClient {
8
+ /**
9
+ * Send prompt to Claude Code CLI and stream response
10
+ */
11
+ async *sendPrompt(userMessage, context) {
12
+ logger.processing('Sending request to Claude CLI...');
13
+ // Build full prompt with context
14
+ const fullPrompt = this.buildPrompt(userMessage, context);
15
+ // Spawn Claude process with --print for non-interactive mode
16
+ // Unset CLAUDECODE to allow nested invocation
17
+ const claude = spawn('claude', ['--print', fullPrompt], {
18
+ stdio: ['ignore', 'pipe', 'pipe'],
19
+ env: {
20
+ ...process.env,
21
+ CLAUDECODE: '', // Unset to allow nested sessions
22
+ },
23
+ });
24
+ let hasOutput = false;
25
+ // Stream stdout chunks
26
+ for await (const chunk of claude.stdout) {
27
+ hasOutput = true;
28
+ yield chunk.toString('utf-8');
29
+ }
30
+ // Collect errors
31
+ let errorOutput = '';
32
+ claude.stderr.on('data', (data) => {
33
+ errorOutput += data.toString();
34
+ });
35
+ // Wait for process to complete
36
+ const exitCode = await new Promise((resolve) => {
37
+ claude.on('close', resolve);
38
+ });
39
+ if (exitCode !== 0) {
40
+ logger.error(`Claude CLI exited with code ${exitCode}`);
41
+ if (errorOutput) {
42
+ logger.error(`Error output: ${errorOutput}`);
43
+ }
44
+ throw new Error(`Claude CLI failed with exit code ${exitCode}`);
45
+ }
46
+ if (!hasOutput) {
47
+ logger.warn('No output received from Claude CLI');
48
+ }
49
+ logger.debug('Claude CLI request completed');
50
+ }
51
+ /**
52
+ * Check if Claude CLI is installed
53
+ */
54
+ async checkAvailable() {
55
+ try {
56
+ const check = spawn('claude', ['--version'], {
57
+ env: {
58
+ ...process.env,
59
+ CLAUDECODE: '', // Unset to allow check
60
+ },
61
+ });
62
+ const exitCode = await new Promise((resolve) => {
63
+ check.on('close', resolve);
64
+ check.on('error', () => resolve(null));
65
+ });
66
+ return exitCode === 0;
67
+ }
68
+ catch (error) {
69
+ return false;
70
+ }
71
+ }
72
+ /**
73
+ * Build prompt with hierarchy context
74
+ */
75
+ buildPrompt(userMessage, context) {
76
+ const contextStr = buildPromptContext(context);
77
+ return `You are an AI assistant helping generate HTML/CSS code from natural language requests.
78
+
79
+ ${contextStr}
80
+
81
+ **User Request:** ${userMessage}
82
+
83
+ **CRITICAL IMPLEMENTATION REQUIREMENTS:**
84
+ ⚠️ NEVER use placeholder comments like "<!-- Child components render here -->" or "<!-- TODO: implement X -->"
85
+ ⚠️ If the user request lists NESTED COMPONENTS TO IMPLEMENT, you MUST generate complete, working HTML/CSS for EVERY single one
86
+ ⚠️ Each nested component specification MUST result in actual HTML elements with proper styling, NOT comments
87
+ ⚠️ If you see a list like "1. <nav-menu>", "2. <pages>", etc., these are REQUIRED components that need full implementations
88
+
89
+ **Instructions:**
90
+ 1. Structure your response as XML elements (NOT plain text)
91
+ 2. ${context.existingCode ? 'Modify ONLY what the user requested, keeping everything else exactly the same' : 'Generate the necessary HTML/CSS code appropriate for this context'}
92
+ 3. Consider the hierarchy - you're working on: ${context.elementPath}
93
+ 4. If referenced elements are provided above, use their definitions to inform your implementation
94
+ 5. If the user request contains a section "NESTED COMPONENTS TO IMPLEMENT", you MUST implement EACH ONE with complete HTML/CSS:
95
+ - Create actual HTML elements for each component
96
+ - Add proper CSS styling for each component
97
+ - Use the attributes as specifications for styling and behavior
98
+ - Use the content as guidance for what to include
99
+ - Do NOT use placeholder comments - implement the actual functionality
100
+ - **CRITICAL EXCEPTION - LOCKED COMPONENTS**:
101
+ - Components marked with "[LOCKED - DO NOT REGENERATE]" are already implemented
102
+ - You MUST preserve locked components exactly as they are
103
+ - Do NOT regenerate, modify, or replace locked components
104
+ - Include them as-is or reference them in your generated code
105
+ - Only implement components that are NOT marked as locked
106
+
107
+ **Response Format (use semantic XML tags, NOT plain text):**
108
+ <changes>Brief description of what was created/modified</changes>
109
+ <details>
110
+ <tag-name>Specific detail about the implementation</tag-name>
111
+ <another-tag>Another detail</another-tag>
112
+ </details>
113
+ <summary>Overall summary</summary>
114
+
115
+ IMPORTANT:
116
+ - Use semantic tag names that describe the content (e.g., <styling>, <background>, <effect>, <feature>, etc.)
117
+ - DO NOT use plain text or bullet points
118
+ - Structure your response with nested XML elements
119
+ - Still include code blocks with \`\`\`html and \`\`\`css for implementation
120
+ - EVERY nested component must be fully implemented, not just mentioned in comments
121
+
122
+ Please respond now.`;
123
+ }
124
+ }