threewzrd 1.0.6 → 1.0.7
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/dist/core/AgentEngine.js +6 -14
- package/dist/core/ThreeJsWizard.d.ts +0 -1
- package/dist/core/ThreeJsWizard.js +2 -10
- package/dist/core/types.d.ts +0 -2
- package/dist/tools/ToolExecutor.js +25 -21
- package/dist/ui/TerminalUI.d.ts +2 -13
- package/dist/ui/TerminalUI.js +3 -137
- package/dist/ui/onboarding.js +4 -10
- package/package.json +1 -1
package/dist/core/AgentEngine.js
CHANGED
|
@@ -58,17 +58,14 @@ export class AgentEngine {
|
|
|
58
58
|
}
|
|
59
59
|
async runAgentLoop() {
|
|
60
60
|
let continueLoop = true;
|
|
61
|
-
let turn = 1;
|
|
62
61
|
while (continueLoop) {
|
|
63
|
-
continueLoop = await this.runSingleTurn(
|
|
64
|
-
turn++;
|
|
62
|
+
continueLoop = await this.runSingleTurn();
|
|
65
63
|
}
|
|
66
64
|
}
|
|
67
|
-
async runSingleTurn(
|
|
65
|
+
async runSingleTurn(retryCount = 0) {
|
|
68
66
|
try {
|
|
69
|
-
// Show thinking indicator
|
|
70
|
-
|
|
71
|
-
this.ui.startThinking(thinkingMessage, turn);
|
|
67
|
+
// Show thinking indicator
|
|
68
|
+
this.ui.startThinking('Thinking');
|
|
72
69
|
// Create the API request with streaming
|
|
73
70
|
const stream = this.client.messages.stream({
|
|
74
71
|
model: MODEL_MAP[this.model],
|
|
@@ -113,13 +110,8 @@ export class AgentEngine {
|
|
|
113
110
|
}
|
|
114
111
|
// Execute all tool calls
|
|
115
112
|
const toolResults = [];
|
|
116
|
-
const
|
|
117
|
-
for (let i = 0; i < toolUseBlocks.length; i++) {
|
|
118
|
-
const toolUse = toolUseBlocks[i];
|
|
119
|
-
const toolProgress = totalTools > 1 ? ` (${i + 1}/${totalTools})` : '';
|
|
120
|
-
this.ui.startThinking(`Executing ${toolUse.name}${toolProgress}`, turn);
|
|
113
|
+
for (const toolUse of toolUseBlocks) {
|
|
121
114
|
const result = await this.toolExecutor.execute(toolUse.name, toolUse.input);
|
|
122
|
-
this.ui.stopThinking();
|
|
123
115
|
// Truncate large outputs to save tokens
|
|
124
116
|
let output = result.success ? result.output : `Error: ${result.error}`;
|
|
125
117
|
if (output.length > 2000) {
|
|
@@ -148,7 +140,7 @@ export class AgentEngine {
|
|
|
148
140
|
const delay = RETRY_DELAY_MS * Math.pow(2, retryCount);
|
|
149
141
|
this.ui.printWarning(`Rate limited. Retrying in ${delay / 1000}s...`);
|
|
150
142
|
await this.sleep(delay);
|
|
151
|
-
return this.runSingleTurn(
|
|
143
|
+
return this.runSingleTurn(retryCount + 1);
|
|
152
144
|
}
|
|
153
145
|
this.ui.printError('Rate limit exceeded. Please wait a moment and try again.');
|
|
154
146
|
return false;
|
|
@@ -9,7 +9,6 @@ export class ThreeJsWizard {
|
|
|
9
9
|
workingDirectory;
|
|
10
10
|
isRunning = false;
|
|
11
11
|
hasOnboarded = false;
|
|
12
|
-
currentMode = 'single-shot';
|
|
13
12
|
constructor(options) {
|
|
14
13
|
this.workingDirectory = process.cwd();
|
|
15
14
|
this.ui = new TerminalUI();
|
|
@@ -43,7 +42,6 @@ export class ThreeJsWizard {
|
|
|
43
42
|
if (!this.hasOnboarded && isEmptyDir) {
|
|
44
43
|
const preferences = await runOnboarding(this.ui);
|
|
45
44
|
this.hasOnboarded = true;
|
|
46
|
-
this.currentMode = preferences.mode;
|
|
47
45
|
// Process the initial project request
|
|
48
46
|
const contextMessage = buildContextMessage(preferences);
|
|
49
47
|
await this.engine.processMessage(contextMessage);
|
|
@@ -55,8 +53,7 @@ export class ThreeJsWizard {
|
|
|
55
53
|
// Main REPL loop
|
|
56
54
|
while (this.isRunning) {
|
|
57
55
|
try {
|
|
58
|
-
const
|
|
59
|
-
this.currentMode = mode;
|
|
56
|
+
const input = await this.ui.prompt();
|
|
60
57
|
if (!input) {
|
|
61
58
|
continue;
|
|
62
59
|
}
|
|
@@ -65,13 +62,8 @@ export class ThreeJsWizard {
|
|
|
65
62
|
await this.handleCommand(input);
|
|
66
63
|
continue;
|
|
67
64
|
}
|
|
68
|
-
// Build message with mode context
|
|
69
|
-
const modePrefix = mode === 'planning'
|
|
70
|
-
? '[PLANNING MODE] Output a detailed implementation plan before coding.\n\n'
|
|
71
|
-
: '';
|
|
72
|
-
const fullMessage = modePrefix + input;
|
|
73
65
|
// Process user message through agent
|
|
74
|
-
await this.engine.processMessage(
|
|
66
|
+
await this.engine.processMessage(input);
|
|
75
67
|
// Track created files
|
|
76
68
|
for (const file of this.engine.getCreatedFiles()) {
|
|
77
69
|
this.projectManager.addFile(file);
|
package/dist/core/types.d.ts
CHANGED
|
@@ -4,12 +4,10 @@ export declare const MODEL_MAP: Record<ModelId, string>;
|
|
|
4
4
|
export declare const DEFAULT_MODEL: ModelId;
|
|
5
5
|
export type ProjectLanguage = 'javascript' | 'typescript';
|
|
6
6
|
export type ProjectTarget = 'browser' | 'mobile' | 'desktop';
|
|
7
|
-
export type ExecutionMode = 'single-shot' | 'planning';
|
|
8
7
|
export interface ProjectPreferences {
|
|
9
8
|
language: ProjectLanguage;
|
|
10
9
|
target: ProjectTarget;
|
|
11
10
|
description: string;
|
|
12
|
-
mode: ExecutionMode;
|
|
13
11
|
}
|
|
14
12
|
export interface ProjectConfig {
|
|
15
13
|
name: string;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs/promises';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { spawn } from 'child_process';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
4
|
import { shouldValidate, validate } from './CodeValidator.js';
|
|
6
5
|
// Whitelist of allowed commands for security
|
|
7
6
|
const ALLOWED_COMMANDS = new Set([
|
|
@@ -233,7 +232,6 @@ export class ToolExecutor {
|
|
|
233
232
|
try {
|
|
234
233
|
// Validate input structure
|
|
235
234
|
const validatedInput = this.validateWriteFileInput(input);
|
|
236
|
-
this.ui.startToolSpinner('write_file', `Writing ${validatedInput.path}`);
|
|
237
235
|
// Validate path doesn't escape working directory
|
|
238
236
|
const fullPath = this.validatePath(validatedInput.path);
|
|
239
237
|
const dir = path.dirname(fullPath);
|
|
@@ -243,7 +241,8 @@ export class ToolExecutor {
|
|
|
243
241
|
// If there are errors, don't write the file
|
|
244
242
|
if (!validationResult.valid) {
|
|
245
243
|
const errorDetails = validationResult.errors.join('\n - ');
|
|
246
|
-
this.ui.
|
|
244
|
+
this.ui.printToolCall('write_file', `Writing: ${validatedInput.path}`);
|
|
245
|
+
this.ui.printToolResult(false, 'Syntax validation failed');
|
|
247
246
|
return {
|
|
248
247
|
success: false,
|
|
249
248
|
output: '',
|
|
@@ -262,7 +261,8 @@ export class ToolExecutor {
|
|
|
262
261
|
// Write the file
|
|
263
262
|
await fs.writeFile(fullPath, validatedInput.content, 'utf-8');
|
|
264
263
|
this.createdFiles.add(validatedInput.path);
|
|
265
|
-
this.ui.
|
|
264
|
+
this.ui.printToolCall('write_file', `Writing: ${validatedInput.path}`);
|
|
265
|
+
this.ui.printToolResult(true, '');
|
|
266
266
|
return {
|
|
267
267
|
success: true,
|
|
268
268
|
output: `Successfully wrote ${validatedInput.path}`,
|
|
@@ -270,7 +270,9 @@ export class ToolExecutor {
|
|
|
270
270
|
}
|
|
271
271
|
catch (error) {
|
|
272
272
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
273
|
-
|
|
273
|
+
const displayPath = input?.path || 'unknown';
|
|
274
|
+
this.ui.printToolCall('write_file', `Writing: ${displayPath}`);
|
|
275
|
+
this.ui.printToolResult(false, errorMessage);
|
|
274
276
|
return {
|
|
275
277
|
success: false,
|
|
276
278
|
output: '',
|
|
@@ -282,11 +284,11 @@ export class ToolExecutor {
|
|
|
282
284
|
try {
|
|
283
285
|
// Validate input structure
|
|
284
286
|
const validatedInput = this.validateReadFileInput(input);
|
|
285
|
-
this.ui.startToolSpinner('read_file', `Reading ${validatedInput.path}`);
|
|
286
287
|
// Validate path doesn't escape working directory
|
|
287
288
|
const fullPath = this.validatePath(validatedInput.path);
|
|
288
289
|
const content = await fs.readFile(fullPath, 'utf-8');
|
|
289
|
-
this.ui.
|
|
290
|
+
this.ui.printToolCall('read_file', `Reading: ${validatedInput.path}`);
|
|
291
|
+
this.ui.printToolResult(true, '');
|
|
290
292
|
return {
|
|
291
293
|
success: true,
|
|
292
294
|
output: content,
|
|
@@ -294,7 +296,9 @@ export class ToolExecutor {
|
|
|
294
296
|
}
|
|
295
297
|
catch (error) {
|
|
296
298
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
297
|
-
|
|
299
|
+
const displayPath = input?.path || 'unknown';
|
|
300
|
+
this.ui.printToolCall('read_file', `Reading: ${displayPath}`);
|
|
301
|
+
this.ui.printToolResult(false, errorMessage);
|
|
298
302
|
return {
|
|
299
303
|
success: false,
|
|
300
304
|
output: '',
|
|
@@ -314,20 +318,17 @@ export class ToolExecutor {
|
|
|
314
318
|
if (validatedInput.cwd) {
|
|
315
319
|
cwd = this.validatePath(validatedInput.cwd);
|
|
316
320
|
}
|
|
317
|
-
|
|
318
|
-
console.log();
|
|
319
|
-
console.log(chalk.yellow(`[Command] `) + chalk.gray(validatedInput.command));
|
|
321
|
+
this.ui.printToolCall('run_command', `Command: ${validatedInput.command}`);
|
|
320
322
|
// Ask for user confirmation before running any command
|
|
321
323
|
const approved = await this.ui.confirm(`Run this command?`);
|
|
322
324
|
if (!approved) {
|
|
323
|
-
|
|
325
|
+
this.ui.printToolResult(false, 'User declined');
|
|
324
326
|
return {
|
|
325
327
|
success: false,
|
|
326
328
|
output: '',
|
|
327
329
|
error: 'User declined to run this command',
|
|
328
330
|
};
|
|
329
331
|
}
|
|
330
|
-
this.ui.startToolSpinner('run_command', `Running: ${validatedInput.command}`);
|
|
331
332
|
// For piped commands, use shell with pre-validated command string
|
|
332
333
|
// For non-piped commands, use spawn without shell for security
|
|
333
334
|
return new Promise((resolve) => {
|
|
@@ -354,14 +355,14 @@ export class ToolExecutor {
|
|
|
354
355
|
child.on('close', (code) => {
|
|
355
356
|
const output = stdout + (stderr ? `\nStderr: ${stderr}` : '');
|
|
356
357
|
if (code === 0) {
|
|
357
|
-
this.ui.
|
|
358
|
+
this.ui.printToolResult(true, '');
|
|
358
359
|
resolve({
|
|
359
360
|
success: true,
|
|
360
361
|
output: output || 'Command completed successfully',
|
|
361
362
|
});
|
|
362
363
|
}
|
|
363
364
|
else {
|
|
364
|
-
this.ui.
|
|
365
|
+
this.ui.printToolResult(false, `Exit code: ${code}`);
|
|
365
366
|
resolve({
|
|
366
367
|
success: false,
|
|
367
368
|
output: '',
|
|
@@ -370,7 +371,7 @@ export class ToolExecutor {
|
|
|
370
371
|
}
|
|
371
372
|
});
|
|
372
373
|
child.on('error', (error) => {
|
|
373
|
-
this.ui.
|
|
374
|
+
this.ui.printToolResult(false, error.message);
|
|
374
375
|
resolve({
|
|
375
376
|
success: false,
|
|
376
377
|
output: '',
|
|
@@ -381,7 +382,9 @@ export class ToolExecutor {
|
|
|
381
382
|
}
|
|
382
383
|
catch (error) {
|
|
383
384
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
384
|
-
|
|
385
|
+
const displayCmd = input?.command || 'unknown';
|
|
386
|
+
this.ui.printToolCall('run_command', `Command: ${displayCmd}`);
|
|
387
|
+
this.ui.printToolResult(false, errorMessage);
|
|
385
388
|
return {
|
|
386
389
|
success: false,
|
|
387
390
|
output: '',
|
|
@@ -393,16 +396,15 @@ export class ToolExecutor {
|
|
|
393
396
|
try {
|
|
394
397
|
// Validate input structure
|
|
395
398
|
const validatedInput = this.validateListFilesInput(input);
|
|
396
|
-
const displayPath = validatedInput.path || '.';
|
|
397
|
-
this.ui.startToolSpinner('list_files', `Listing ${displayPath}`);
|
|
398
399
|
// Validate path doesn't escape working directory
|
|
399
400
|
const targetPath = validatedInput.path
|
|
400
401
|
? this.validatePath(validatedInput.path)
|
|
401
402
|
: this.workingDirectory;
|
|
403
|
+
this.ui.printToolCall('list_files', `Listing: ${validatedInput.path || '.'}`);
|
|
402
404
|
const files = await this.listFilesRecursive(targetPath, validatedInput.recursive ?? false);
|
|
403
405
|
// Format output
|
|
404
406
|
const relativePaths = files.map(f => path.relative(this.workingDirectory, f));
|
|
405
|
-
this.ui.
|
|
407
|
+
this.ui.printToolResult(true, '');
|
|
406
408
|
return {
|
|
407
409
|
success: true,
|
|
408
410
|
output: relativePaths.length > 0
|
|
@@ -412,7 +414,9 @@ export class ToolExecutor {
|
|
|
412
414
|
}
|
|
413
415
|
catch (error) {
|
|
414
416
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
415
|
-
|
|
417
|
+
const displayPath = input?.path || '.';
|
|
418
|
+
this.ui.printToolCall('list_files', `Listing: ${displayPath}`);
|
|
419
|
+
this.ui.printToolResult(false, errorMessage);
|
|
416
420
|
return {
|
|
417
421
|
success: false,
|
|
418
422
|
output: '',
|
package/dist/ui/TerminalUI.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ModelId
|
|
1
|
+
import { ModelId } from '../core/types.js';
|
|
2
2
|
export interface SelectOption {
|
|
3
3
|
label: string;
|
|
4
4
|
value: string;
|
|
@@ -7,15 +7,9 @@ export declare class TerminalUI {
|
|
|
7
7
|
private rl;
|
|
8
8
|
private isStreaming;
|
|
9
9
|
private thinkingSpinner;
|
|
10
|
-
private toolSpinner;
|
|
11
10
|
constructor();
|
|
12
|
-
startThinking(message?: string
|
|
13
|
-
updateThinking(message: string, turn?: number): void;
|
|
11
|
+
startThinking(message?: string): void;
|
|
14
12
|
stopThinking(): void;
|
|
15
|
-
startToolSpinner(toolName: string, detail: string): void;
|
|
16
|
-
succeedToolSpinner(message?: string): void;
|
|
17
|
-
failToolSpinner(message: string): void;
|
|
18
|
-
stopToolSpinner(): void;
|
|
19
13
|
confirm(message: string): Promise<boolean>;
|
|
20
14
|
select(question: string, options: SelectOption[]): Promise<string>;
|
|
21
15
|
printBanner(): void;
|
|
@@ -37,11 +31,6 @@ export declare class TerminalUI {
|
|
|
37
31
|
streamText(text: string): void;
|
|
38
32
|
endStreaming(): void;
|
|
39
33
|
prompt(): Promise<string>;
|
|
40
|
-
promptWithMode(defaultMode?: ExecutionMode): Promise<{
|
|
41
|
-
text: string;
|
|
42
|
-
mode: ExecutionMode;
|
|
43
|
-
}>;
|
|
44
|
-
printModeInfo(mode: ExecutionMode): void;
|
|
45
34
|
close(): void;
|
|
46
35
|
clearScreen(): void;
|
|
47
36
|
}
|
package/dist/ui/TerminalUI.js
CHANGED
|
@@ -5,28 +5,20 @@ export class TerminalUI {
|
|
|
5
5
|
rl;
|
|
6
6
|
isStreaming = false;
|
|
7
7
|
thinkingSpinner = null;
|
|
8
|
-
toolSpinner = null;
|
|
9
8
|
constructor() {
|
|
10
9
|
this.rl = readline.createInterface({
|
|
11
10
|
input: process.stdin,
|
|
12
11
|
output: process.stdout,
|
|
13
12
|
});
|
|
14
13
|
}
|
|
15
|
-
// Thinking indicator
|
|
16
|
-
startThinking(message = 'Thinking'
|
|
17
|
-
const turnInfo = turn ? chalk.gray(` [Turn ${turn}]`) : '';
|
|
14
|
+
// Thinking indicator
|
|
15
|
+
startThinking(message = 'Thinking') {
|
|
18
16
|
this.thinkingSpinner = ora({
|
|
19
|
-
text: chalk.cyan(message)
|
|
17
|
+
text: chalk.cyan(message),
|
|
20
18
|
spinner: 'dots',
|
|
21
19
|
discardStdin: false, // Don't interfere with readline
|
|
22
20
|
}).start();
|
|
23
21
|
}
|
|
24
|
-
updateThinking(message, turn) {
|
|
25
|
-
if (this.thinkingSpinner) {
|
|
26
|
-
const turnInfo = turn ? chalk.gray(` [Turn ${turn}]`) : '';
|
|
27
|
-
this.thinkingSpinner.text = chalk.cyan(message) + turnInfo;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
22
|
stopThinking() {
|
|
31
23
|
if (this.thinkingSpinner) {
|
|
32
24
|
this.thinkingSpinner.stop();
|
|
@@ -37,32 +29,6 @@ export class TerminalUI {
|
|
|
37
29
|
process.stdin.resume();
|
|
38
30
|
}
|
|
39
31
|
}
|
|
40
|
-
// Tool execution spinner
|
|
41
|
-
startToolSpinner(toolName, detail) {
|
|
42
|
-
this.toolSpinner = ora({
|
|
43
|
-
text: chalk.yellow(`${toolName}: `) + chalk.gray(detail),
|
|
44
|
-
spinner: 'dots',
|
|
45
|
-
discardStdin: false,
|
|
46
|
-
}).start();
|
|
47
|
-
}
|
|
48
|
-
succeedToolSpinner(message) {
|
|
49
|
-
if (this.toolSpinner) {
|
|
50
|
-
this.toolSpinner.succeed(message ? chalk.green(message) : undefined);
|
|
51
|
-
this.toolSpinner = null;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
failToolSpinner(message) {
|
|
55
|
-
if (this.toolSpinner) {
|
|
56
|
-
this.toolSpinner.fail(chalk.red(message));
|
|
57
|
-
this.toolSpinner = null;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
stopToolSpinner() {
|
|
61
|
-
if (this.toolSpinner) {
|
|
62
|
-
this.toolSpinner.stop();
|
|
63
|
-
this.toolSpinner = null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
32
|
// Confirmation prompt for dangerous actions
|
|
67
33
|
async confirm(message) {
|
|
68
34
|
// Ensure any spinner is stopped
|
|
@@ -112,18 +78,12 @@ export class TerminalUI {
|
|
|
112
78
|
console.log(chalk.gray(' lighting, animations, and more - just describe what'));
|
|
113
79
|
console.log(chalk.gray(' you want in plain English.'));
|
|
114
80
|
console.log();
|
|
115
|
-
console.log(chalk.gray(' Modes: ') + chalk.cyan('⚡ Quick') + chalk.gray(' | ') + chalk.magenta('📋 Plan') + chalk.gray(' (Tab to switch)'));
|
|
116
81
|
console.log(chalk.gray(' Commands: ') + chalk.yellow('/help') + chalk.gray(' | ') + chalk.yellow('/clear') + chalk.gray(' | ') + chalk.yellow('/exit'));
|
|
117
82
|
console.log();
|
|
118
83
|
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
119
84
|
console.log();
|
|
120
85
|
}
|
|
121
86
|
printHelp() {
|
|
122
|
-
console.log();
|
|
123
|
-
console.log(chalk.yellow('Execution Modes:'));
|
|
124
|
-
console.log(chalk.cyan(' ⚡ Quick Mode') + chalk.gray(' - Direct implementation (default)'));
|
|
125
|
-
console.log(chalk.magenta(' 📋 Plan Mode') + chalk.gray(' - Detailed architecture plan first'));
|
|
126
|
-
console.log(chalk.gray(' Press Tab while typing to switch modes'));
|
|
127
87
|
console.log();
|
|
128
88
|
console.log(chalk.yellow('Commands:'));
|
|
129
89
|
console.log(chalk.cyan(' /help') + chalk.gray(' - Show this help message'));
|
|
@@ -216,100 +176,6 @@ export class TerminalUI {
|
|
|
216
176
|
});
|
|
217
177
|
});
|
|
218
178
|
}
|
|
219
|
-
// Mode-aware prompt with Tab toggle for execution mode
|
|
220
|
-
async promptWithMode(defaultMode = 'single-shot') {
|
|
221
|
-
let currentMode = defaultMode;
|
|
222
|
-
// Print mode indicator and instructions
|
|
223
|
-
const printModeBar = () => {
|
|
224
|
-
const singleShot = currentMode === 'single-shot'
|
|
225
|
-
? chalk.bgCyan.black(' ⚡ Quick ')
|
|
226
|
-
: chalk.gray(' ⚡ Quick ');
|
|
227
|
-
const planning = currentMode === 'planning'
|
|
228
|
-
? chalk.bgMagenta.white(' 📋 Plan ')
|
|
229
|
-
: chalk.gray(' 📋 Plan ');
|
|
230
|
-
// Clear line and reprint
|
|
231
|
-
process.stdout.write('\r\x1b[K');
|
|
232
|
-
process.stdout.write(` ${singleShot} ${planning} ${chalk.gray('Tab to switch')}\n`);
|
|
233
|
-
};
|
|
234
|
-
return new Promise((resolve) => {
|
|
235
|
-
printModeBar();
|
|
236
|
-
console.log();
|
|
237
|
-
// Close the existing readline interface to fully release stdin
|
|
238
|
-
this.rl.close();
|
|
239
|
-
// Store the current input
|
|
240
|
-
let inputBuffer = '';
|
|
241
|
-
// Set up raw mode for keypress detection
|
|
242
|
-
if (process.stdin.isTTY) {
|
|
243
|
-
process.stdin.setRawMode(true);
|
|
244
|
-
}
|
|
245
|
-
process.stdin.resume();
|
|
246
|
-
const promptPrefix = chalk.magenta(' › ');
|
|
247
|
-
process.stdout.write(promptPrefix);
|
|
248
|
-
const handleKeypress = (chunk) => {
|
|
249
|
-
const key = chunk.toString();
|
|
250
|
-
// Tab key - toggle mode
|
|
251
|
-
if (key === '\t') {
|
|
252
|
-
currentMode = currentMode === 'single-shot' ? 'planning' : 'single-shot';
|
|
253
|
-
// Move cursor up, clear the mode bar, reprint it, move back down
|
|
254
|
-
process.stdout.write('\x1b[2A'); // Move up 2 lines
|
|
255
|
-
printModeBar();
|
|
256
|
-
console.log();
|
|
257
|
-
process.stdout.write(promptPrefix + inputBuffer);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
// Enter key - submit
|
|
261
|
-
if (key === '\r' || key === '\n') {
|
|
262
|
-
process.stdout.write('\n');
|
|
263
|
-
cleanup();
|
|
264
|
-
resolve({ text: inputBuffer.trim(), mode: currentMode });
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
// Ctrl+C - exit
|
|
268
|
-
if (key === '\x03') {
|
|
269
|
-
cleanup();
|
|
270
|
-
process.exit(0);
|
|
271
|
-
}
|
|
272
|
-
// Backspace
|
|
273
|
-
if (key === '\x7f' || key === '\b') {
|
|
274
|
-
if (inputBuffer.length > 0) {
|
|
275
|
-
inputBuffer = inputBuffer.slice(0, -1);
|
|
276
|
-
process.stdout.write('\b \b');
|
|
277
|
-
}
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
// Escape sequences (arrow keys, etc.) - ignore for simplicity
|
|
281
|
-
if (key.startsWith('\x1b')) {
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
// Regular character
|
|
285
|
-
if (key.length === 1 && key.charCodeAt(0) >= 32) {
|
|
286
|
-
inputBuffer += key;
|
|
287
|
-
process.stdout.write(key);
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
const cleanup = () => {
|
|
291
|
-
process.stdin.removeListener('data', handleKeypress);
|
|
292
|
-
if (process.stdin.isTTY) {
|
|
293
|
-
process.stdin.setRawMode(false);
|
|
294
|
-
}
|
|
295
|
-
// Recreate readline interface
|
|
296
|
-
this.rl = readline.createInterface({
|
|
297
|
-
input: process.stdin,
|
|
298
|
-
output: process.stdout,
|
|
299
|
-
});
|
|
300
|
-
};
|
|
301
|
-
process.stdin.on('data', handleKeypress);
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
printModeInfo(mode) {
|
|
305
|
-
if (mode === 'single-shot') {
|
|
306
|
-
console.log(chalk.cyan(' ⚡ Quick Mode: ') + chalk.gray('Direct implementation without formal planning'));
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
console.log(chalk.magenta(' 📋 Plan Mode: ') + chalk.gray('Detailed architecture plan before implementation'));
|
|
310
|
-
}
|
|
311
|
-
console.log();
|
|
312
|
-
}
|
|
313
179
|
close() {
|
|
314
180
|
this.rl.close();
|
|
315
181
|
}
|
package/dist/ui/onboarding.js
CHANGED
|
@@ -12,33 +12,27 @@ export async function runOnboarding(ui) {
|
|
|
12
12
|
{ label: 'Mobile (React Native, etc.)', value: 'mobile' },
|
|
13
13
|
{ label: 'Desktop (Electron, etc.)', value: 'desktop' },
|
|
14
14
|
]);
|
|
15
|
-
// Ask for project description
|
|
15
|
+
// Ask for project description
|
|
16
16
|
console.log();
|
|
17
|
-
console.log(chalk.cyan('
|
|
17
|
+
console.log(chalk.cyan(' Now describe what you\'d like to build:'));
|
|
18
18
|
console.log(chalk.gray(' (e.g., "A 3D solar system with orbiting planets")'));
|
|
19
19
|
console.log();
|
|
20
|
-
const
|
|
20
|
+
const description = await ui.prompt();
|
|
21
21
|
console.log();
|
|
22
|
-
ui.printModeInfo(mode);
|
|
23
22
|
console.log(chalk.gray(' ─────────────────────────────────────────'));
|
|
24
23
|
console.log();
|
|
25
24
|
return {
|
|
26
25
|
language,
|
|
27
26
|
target,
|
|
28
27
|
description,
|
|
29
|
-
mode,
|
|
30
28
|
};
|
|
31
29
|
}
|
|
32
30
|
export function buildContextMessage(prefs) {
|
|
33
|
-
const modeInstruction = prefs.mode === 'planning'
|
|
34
|
-
? `\n\nIMPORTANT: The user has requested Planning Mode. You MUST output a detailed implementation plan with Architecture Overview, Dependencies, File Structure, and Execution Steps BEFORE writing any code.`
|
|
35
|
-
: `\n\nThe user has requested Single-Shot Mode. Plan internally and proceed directly to implementation without outputting a formal plan.`;
|
|
36
31
|
return `The user wants to create a Three.js project with these preferences:
|
|
37
32
|
- Language: ${prefs.language}
|
|
38
33
|
- Target platform: ${prefs.target}
|
|
39
|
-
- Execution mode: ${prefs.mode === 'planning' ? 'Planning Mode (detailed plan first)' : 'Single-Shot Mode (direct implementation)'}
|
|
40
34
|
|
|
41
|
-
Their project description: ${prefs.description}
|
|
35
|
+
Their project description: ${prefs.description}
|
|
42
36
|
|
|
43
37
|
Please create the project structure and initial files based on these requirements. Start by setting up the basic Three.js scene.`;
|
|
44
38
|
}
|