wiggum-cli 0.8.0 → 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/dist/ai/conversation/conversation-manager.d.ts +11 -0
- package/dist/ai/conversation/conversation-manager.d.ts.map +1 -1
- package/dist/ai/conversation/conversation-manager.js +14 -0
- package/dist/ai/conversation/conversation-manager.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/new.d.ts +2 -0
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +63 -22
- package/dist/commands/new.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +97 -22
- package/dist/index.js.map +1 -1
- package/dist/tui/app.d.ts +46 -36
- package/dist/tui/app.d.ts.map +1 -1
- package/dist/tui/app.js +136 -37
- package/dist/tui/app.js.map +1 -1
- package/dist/tui/components/WiggumBanner.d.ts +30 -0
- package/dist/tui/components/WiggumBanner.d.ts.map +1 -0
- package/dist/tui/components/WiggumBanner.js +34 -0
- package/dist/tui/components/WiggumBanner.js.map +1 -0
- package/dist/tui/demo.d.ts +8 -0
- package/dist/tui/demo.d.ts.map +1 -0
- package/dist/tui/demo.js +69 -0
- package/dist/tui/demo.js.map +1 -0
- package/dist/tui/hooks/useSpecGenerator.d.ts +16 -0
- package/dist/tui/hooks/useSpecGenerator.d.ts.map +1 -1
- package/dist/tui/hooks/useSpecGenerator.js +47 -0
- package/dist/tui/hooks/useSpecGenerator.js.map +1 -1
- package/dist/tui/orchestration/index.d.ts +6 -0
- package/dist/tui/orchestration/index.d.ts.map +1 -0
- package/dist/tui/orchestration/index.js +6 -0
- package/dist/tui/orchestration/index.js.map +1 -0
- package/dist/tui/orchestration/interview-orchestrator.d.ts +136 -0
- package/dist/tui/orchestration/interview-orchestrator.d.ts.map +1 -0
- package/dist/tui/orchestration/interview-orchestrator.js +437 -0
- package/dist/tui/orchestration/interview-orchestrator.js.map +1 -0
- package/dist/tui/screens/InitScreen.d.ts +26 -0
- package/dist/tui/screens/InitScreen.d.ts.map +1 -0
- package/dist/tui/screens/InitScreen.js +30 -0
- package/dist/tui/screens/InitScreen.js.map +1 -0
- package/dist/tui/screens/InterviewScreen.d.ts +2 -13
- package/dist/tui/screens/InterviewScreen.d.ts.map +1 -1
- package/dist/tui/screens/InterviewScreen.js +162 -34
- package/dist/tui/screens/InterviewScreen.js.map +1 -1
- package/dist/tui/screens/MainShell.d.ts +46 -0
- package/dist/tui/screens/MainShell.d.ts.map +1 -0
- package/dist/tui/screens/MainShell.js +196 -0
- package/dist/tui/screens/MainShell.js.map +1 -0
- package/dist/tui/screens/WelcomeScreen.d.ts +45 -0
- package/dist/tui/screens/WelcomeScreen.d.ts.map +1 -0
- package/dist/tui/screens/WelcomeScreen.js +56 -0
- package/dist/tui/screens/WelcomeScreen.js.map +1 -0
- package/dist/tui/theme.d.ts +4 -0
- package/dist/tui/theme.d.ts.map +1 -1
- package/dist/tui/theme.js +4 -0
- package/dist/tui/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/ai/conversation/conversation-manager.ts +22 -0
- package/src/cli.ts +4 -0
- package/src/commands/new.ts +79 -27
- package/src/index.ts +109 -27
- package/src/tui/app.tsx +222 -63
- package/src/tui/components/WiggumBanner.tsx +66 -0
- package/src/tui/demo.tsx +111 -0
- package/src/tui/hooks/useSpecGenerator.ts +73 -0
- package/src/tui/orchestration/index.ts +10 -0
- package/src/tui/orchestration/interview-orchestrator.ts +559 -0
- package/src/tui/screens/InitScreen.tsx +63 -0
- package/src/tui/screens/InterviewScreen.tsx +201 -46
- package/src/tui/screens/MainShell.tsx +290 -0
- package/src/tui/screens/WelcomeScreen.tsx +141 -0
- package/src/tui/theme.ts +4 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WelcomeScreen - Dexter-inspired welcome screen
|
|
3
|
+
*
|
|
4
|
+
* Displays the Wiggum CLI banner, version, and current model info.
|
|
5
|
+
* Press Enter to continue to the main shell.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import type { AIProvider } from '../../ai/providers.js';
|
|
9
|
+
/**
|
|
10
|
+
* Props for WelcomeScreen component
|
|
11
|
+
*/
|
|
12
|
+
export interface WelcomeScreenProps {
|
|
13
|
+
/** Current AI provider */
|
|
14
|
+
provider: AIProvider | null;
|
|
15
|
+
/** Current model ID */
|
|
16
|
+
model: string;
|
|
17
|
+
/** CLI version */
|
|
18
|
+
version: string;
|
|
19
|
+
/** Whether the project is initialized */
|
|
20
|
+
isInitialized: boolean;
|
|
21
|
+
/** Called when user presses Enter to continue */
|
|
22
|
+
onContinue: () => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* WelcomeScreen component
|
|
26
|
+
*
|
|
27
|
+
* Displays a Dexter-inspired welcome screen with:
|
|
28
|
+
* - ASCII art banner
|
|
29
|
+
* - Version info
|
|
30
|
+
* - Current model configuration
|
|
31
|
+
* - Instructions to continue
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <WelcomeScreen
|
|
36
|
+
* provider="anthropic"
|
|
37
|
+
* model="sonnet"
|
|
38
|
+
* version="0.8.0"
|
|
39
|
+
* isInitialized={true}
|
|
40
|
+
* onContinue={() => navigate('shell')}
|
|
41
|
+
* />
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export declare function WelcomeScreen({ provider, model, version, isInitialized, onContinue, }: WelcomeScreenProps): React.ReactElement;
|
|
45
|
+
//# sourceMappingURL=WelcomeScreen.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WelcomeScreen.d.ts","sourceRoot":"","sources":["../../../src/tui/screens/WelcomeScreen.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,uBAAuB;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,aAAa,EAAE,OAAO,CAAC;IACvB,iDAAiD;IACjD,UAAU,EAAE,MAAM,IAAI,CAAC;CACxB;AASD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,KAAK,EACL,OAAO,EACP,aAAa,EACb,UAAU,GACX,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CA8EzC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* WelcomeScreen - Dexter-inspired welcome screen
|
|
4
|
+
*
|
|
5
|
+
* Displays the Wiggum CLI banner, version, and current model info.
|
|
6
|
+
* Press Enter to continue to the main shell.
|
|
7
|
+
*/
|
|
8
|
+
import { useState, useEffect } from 'react';
|
|
9
|
+
import { Box, Text, useInput } from 'ink';
|
|
10
|
+
import { WiggumBanner } from '../components/WiggumBanner.js';
|
|
11
|
+
import { colors } from '../theme.js';
|
|
12
|
+
/**
|
|
13
|
+
* Get version from package.json (client-side compatible)
|
|
14
|
+
*/
|
|
15
|
+
function getVersionDisplay(version) {
|
|
16
|
+
return version || '0.5.0';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* WelcomeScreen component
|
|
20
|
+
*
|
|
21
|
+
* Displays a Dexter-inspired welcome screen with:
|
|
22
|
+
* - ASCII art banner
|
|
23
|
+
* - Version info
|
|
24
|
+
* - Current model configuration
|
|
25
|
+
* - Instructions to continue
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <WelcomeScreen
|
|
30
|
+
* provider="anthropic"
|
|
31
|
+
* model="sonnet"
|
|
32
|
+
* version="0.8.0"
|
|
33
|
+
* isInitialized={true}
|
|
34
|
+
* onContinue={() => navigate('shell')}
|
|
35
|
+
* />
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function WelcomeScreen({ provider, model, version, isInitialized, onContinue, }) {
|
|
39
|
+
const [blinkVisible, setBlinkVisible] = useState(true);
|
|
40
|
+
// Blink effect for "Press Enter"
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const interval = setInterval(() => {
|
|
43
|
+
setBlinkVisible((v) => !v);
|
|
44
|
+
}, 800);
|
|
45
|
+
return () => clearInterval(interval);
|
|
46
|
+
}, []);
|
|
47
|
+
// Handle Enter key to continue
|
|
48
|
+
useInput((input, key) => {
|
|
49
|
+
if (key.return) {
|
|
50
|
+
onContinue();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
const versionDisplay = getVersionDisplay(version);
|
|
54
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(WiggumBanner, {}), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.brown, children: "Your AI assistant for feature development" }) }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Version " }), _jsx(Text, { color: colors.pink, children: versionDisplay })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Model: " }), provider ? (_jsxs(Text, { color: colors.blue, children: [provider, "/", model] })) : (_jsx(Text, { color: colors.orange, children: "Not configured (run /init)" }))] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Project: " }), isInitialized ? (_jsx(Text, { color: colors.green, children: "Initialized" })) : (_jsx(Text, { color: colors.orange, children: "Not initialized" }))] }), _jsxs(Box, { marginTop: 2, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "Tips:" }), _jsx(Text, { dimColor: true, children: " /init - Initialize or reconfigure this project" }), _jsx(Text, { dimColor: true, children: " /new - Create a new feature specification" }), _jsx(Text, { dimColor: true, children: " /help - Show all available commands" })] }), _jsx(Box, { marginTop: 2, children: _jsx(Text, { color: blinkVisible ? colors.yellow : colors.brown, children: "Press Enter to continue..." }) })] }));
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=WelcomeScreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WelcomeScreen.js","sourceRoot":"","sources":["../../../src/tui/screens/WelcomeScreen.tsx"],"names":[],"mappings":"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"}
|
package/dist/tui/theme.d.ts
CHANGED
|
@@ -22,6 +22,10 @@ export declare const colors: {
|
|
|
22
22
|
readonly white: "#ffffff";
|
|
23
23
|
/** Danger/warnings/errors */
|
|
24
24
|
readonly pink: "#ff81c1";
|
|
25
|
+
/** Success/completion */
|
|
26
|
+
readonly green: "#4ade80";
|
|
27
|
+
/** Warnings/caution */
|
|
28
|
+
readonly orange: "#fb923c";
|
|
25
29
|
};
|
|
26
30
|
/**
|
|
27
31
|
* Box drawing characters for custom borders
|
package/dist/tui/theme.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/tui/theme.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,MAAM;IACjB,oCAAoC;;IAEpC,gDAAgD;;IAEhD,kDAAkD;;IAElD,8BAA8B;;IAE9B,6BAA6B;;
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/tui/theme.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,MAAM;IACjB,oCAAoC;;IAEpC,gDAAgD;;IAEhD,kDAAkD;;IAElD,8BAA8B;;IAE9B,6BAA6B;;IAE7B,yBAAyB;;IAEzB,uBAAuB;;CAEf,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,GAAG;;;;;;;CAON,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,KAAK;;;;;CAKR,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,KAAK,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,MAAM,CAAC;AAE5C;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAEhD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAExD"}
|
package/dist/tui/theme.js
CHANGED
|
@@ -22,6 +22,10 @@ export const colors = {
|
|
|
22
22
|
white: '#ffffff',
|
|
23
23
|
/** Danger/warnings/errors */
|
|
24
24
|
pink: '#ff81c1',
|
|
25
|
+
/** Success/completion */
|
|
26
|
+
green: '#4ade80',
|
|
27
|
+
/** Warnings/caution */
|
|
28
|
+
orange: '#fb923c',
|
|
25
29
|
};
|
|
26
30
|
/**
|
|
27
31
|
* Box drawing characters for custom borders
|
package/dist/tui/theme.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/tui/theme.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,oCAAoC;IACpC,IAAI,EAAE,SAAS;IACf,gDAAgD;IAChD,MAAM,EAAE,SAAS;IACjB,kDAAkD;IAClD,KAAK,EAAE,SAAS;IAChB,8BAA8B;IAC9B,KAAK,EAAE,SAAS;IAChB,6BAA6B;IAC7B,IAAI,EAAE,SAAS;
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/tui/theme.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,oCAAoC;IACpC,IAAI,EAAE,SAAS;IACf,gDAAgD;IAChD,MAAM,EAAE,SAAS;IACjB,kDAAkD;IAClD,KAAK,EAAE,SAAS;IAChB,8BAA8B;IAC9B,KAAK,EAAE,SAAS;IAChB,6BAA6B;IAC7B,IAAI,EAAE,SAAS;IACf,yBAAyB;IACzB,KAAK,EAAE,SAAS;IAChB,uBAAuB;IACvB,MAAM,EAAE,SAAS;CACT,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,QAAQ;IAClB,UAAU,EAAE,QAAQ;IACpB,WAAW,EAAE,QAAQ;IACrB,UAAU,EAAE,QAAQ;IACpB,QAAQ,EAAE,QAAQ;CACV,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,OAAO,EAAE,QAAQ,EAAE,IAAI;IACvB,MAAM,EAAE,QAAQ,EAAE,IAAI;IACtB,QAAQ,EAAE,QAAQ,EAAE,IAAI;IACxB,KAAK,EAAE,QAAQ,EAAE,IAAI;CACb,CAAC;AAYX;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAe;IACtC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAmB;IAC9C,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC;AACvB,CAAC"}
|
package/package.json
CHANGED
|
@@ -37,6 +37,11 @@ export interface ConversationContext {
|
|
|
37
37
|
*/
|
|
38
38
|
export type ToolUseCallback = (toolName: string, args: Record<string, unknown>) => void;
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Tool result callback for capturing tool completion
|
|
42
|
+
*/
|
|
43
|
+
export type ToolResultCallback = (toolName: string, result: unknown) => void;
|
|
44
|
+
|
|
40
45
|
/**
|
|
41
46
|
* Conversation manager options
|
|
42
47
|
*/
|
|
@@ -48,6 +53,8 @@ export interface ConversationManagerOptions {
|
|
|
48
53
|
tools?: Record<string, Tool>;
|
|
49
54
|
/** Callback when a tool is used */
|
|
50
55
|
onToolUse?: ToolUseCallback;
|
|
56
|
+
/** Callback when a tool completes with result */
|
|
57
|
+
onToolResult?: ToolResultCallback;
|
|
51
58
|
/** Maximum tool calling steps (default: 5) */
|
|
52
59
|
maxToolSteps?: number;
|
|
53
60
|
}
|
|
@@ -94,6 +101,7 @@ export class ConversationManager {
|
|
|
94
101
|
private systemPrompt: string;
|
|
95
102
|
private tools?: Record<string, Tool>;
|
|
96
103
|
private onToolUse?: ToolUseCallback;
|
|
104
|
+
private onToolResult?: ToolResultCallback;
|
|
97
105
|
private maxToolSteps: number;
|
|
98
106
|
|
|
99
107
|
constructor(options: ConversationManagerOptions) {
|
|
@@ -102,6 +110,7 @@ export class ConversationManager {
|
|
|
102
110
|
this.systemPrompt = options.systemPrompt || this.getDefaultSystemPrompt();
|
|
103
111
|
this.tools = options.tools;
|
|
104
112
|
this.onToolUse = options.onToolUse;
|
|
113
|
+
this.onToolResult = options.onToolResult;
|
|
105
114
|
this.maxToolSteps = options.maxToolSteps ?? 5;
|
|
106
115
|
}
|
|
107
116
|
|
|
@@ -119,6 +128,13 @@ export class ConversationManager {
|
|
|
119
128
|
this.onToolUse = callback;
|
|
120
129
|
}
|
|
121
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Set tool result callback
|
|
133
|
+
*/
|
|
134
|
+
setOnToolResult(callback: ToolResultCallback): void {
|
|
135
|
+
this.onToolResult = callback;
|
|
136
|
+
}
|
|
137
|
+
|
|
122
138
|
/**
|
|
123
139
|
* Update the system prompt
|
|
124
140
|
*/
|
|
@@ -230,6 +246,12 @@ Be concise but thorough. Focus on understanding the user's needs before proposin
|
|
|
230
246
|
this.onToolUse(toolCall.toolName, toolCall.input as Record<string, unknown>);
|
|
231
247
|
}
|
|
232
248
|
}
|
|
249
|
+
// Call onToolResult callback for each tool result
|
|
250
|
+
if (step.toolResults && this.onToolResult) {
|
|
251
|
+
for (const toolResult of step.toolResults) {
|
|
252
|
+
this.onToolResult(toolResult.toolName, toolResult.output);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
233
255
|
};
|
|
234
256
|
}
|
|
235
257
|
|
package/src/cli.ts
CHANGED
|
@@ -205,6 +205,7 @@ Dashboard Shows:
|
|
|
205
205
|
.option('-y, --yes', 'Skip confirmation prompts')
|
|
206
206
|
.option('-f, --force', 'Overwrite existing spec file without prompting')
|
|
207
207
|
.option('--ai', 'Use AI interview to generate the spec')
|
|
208
|
+
.option('--tui', 'Use Ink TUI for AI interview (with --ai)')
|
|
208
209
|
.option(
|
|
209
210
|
'--provider <name>',
|
|
210
211
|
'AI provider for spec generation (anthropic, openai, openrouter)'
|
|
@@ -216,6 +217,7 @@ Dashboard Shows:
|
|
|
216
217
|
Examples:
|
|
217
218
|
$ wiggum new user-dashboard Create spec from template
|
|
218
219
|
$ wiggum new user-dashboard --ai Use AI interview to generate spec
|
|
220
|
+
$ wiggum new user-dashboard --ai --tui Use AI with Ink TUI interface
|
|
219
221
|
$ wiggum new user-dashboard --edit Create and open in editor
|
|
220
222
|
$ wiggum new user-dashboard -e --editor vim Open in vim
|
|
221
223
|
$ wiggum new user-dashboard --yes Skip confirmations
|
|
@@ -228,6 +230,7 @@ AI Mode (--ai):
|
|
|
228
230
|
- Gathers context from URLs/files you provide
|
|
229
231
|
- Conducts an interview to understand your requirements
|
|
230
232
|
- Generates a detailed, project-specific specification
|
|
233
|
+
- Add --tui for a beautiful Ink-based terminal interface
|
|
231
234
|
|
|
232
235
|
Template Mode (default):
|
|
233
236
|
- Uses a standard template with sections for:
|
|
@@ -242,6 +245,7 @@ Template Mode (default):
|
|
|
242
245
|
yes: options.yes,
|
|
243
246
|
force: options.force,
|
|
244
247
|
ai: options.ai,
|
|
248
|
+
tui: options.tui,
|
|
245
249
|
provider: options.provider,
|
|
246
250
|
model: options.model,
|
|
247
251
|
};
|
package/src/commands/new.ts
CHANGED
|
@@ -15,6 +15,8 @@ import { Scanner, type ScanResult } from '../scanner/index.js';
|
|
|
15
15
|
import { flushTracing, traced, initTracing } from '../utils/tracing.js';
|
|
16
16
|
import pc from 'picocolors';
|
|
17
17
|
import * as prompts from '@clack/prompts';
|
|
18
|
+
import { renderApp } from '../tui/app.js';
|
|
19
|
+
import { createSessionState } from '../repl/session-state.js';
|
|
18
20
|
|
|
19
21
|
export interface NewOptions {
|
|
20
22
|
/** Open in editor after creation */
|
|
@@ -27,6 +29,8 @@ export interface NewOptions {
|
|
|
27
29
|
force?: boolean;
|
|
28
30
|
/** Use AI interview to generate spec */
|
|
29
31
|
ai?: boolean;
|
|
32
|
+
/** Use Ink TUI for AI interview (instead of readline) */
|
|
33
|
+
tui?: boolean;
|
|
30
34
|
/** AI provider (anthropic, openai, openrouter) */
|
|
31
35
|
provider?: AIProvider;
|
|
32
36
|
/** Model to use for AI generation */
|
|
@@ -273,37 +277,85 @@ export async function newCommand(feature: string, options: NewOptions = {}): Pro
|
|
|
273
277
|
scanResult = await scanner.scan(projectRoot);
|
|
274
278
|
}
|
|
275
279
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
280
|
+
// Check if TUI mode is requested
|
|
281
|
+
if (options.tui) {
|
|
282
|
+
// Use Ink TUI for interview
|
|
283
|
+
logger.info('Starting interactive TUI mode...');
|
|
284
|
+
console.log('');
|
|
285
|
+
|
|
286
|
+
// Create session state for TUI
|
|
287
|
+
const sessionState = createSessionState(
|
|
288
|
+
projectRoot,
|
|
289
|
+
provider,
|
|
290
|
+
model,
|
|
291
|
+
scanResult,
|
|
292
|
+
config,
|
|
293
|
+
true // initialized
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
// Wrap in a promise to get the generated spec
|
|
297
|
+
const generatedSpec = await new Promise<string | null>((resolve) => {
|
|
298
|
+
const instance = renderApp({
|
|
299
|
+
screen: 'interview',
|
|
300
|
+
initialSessionState: sessionState,
|
|
301
|
+
interviewProps: {
|
|
302
|
+
featureName: feature,
|
|
303
|
+
projectRoot,
|
|
304
|
+
provider,
|
|
305
|
+
model,
|
|
306
|
+
scanResult,
|
|
307
|
+
},
|
|
308
|
+
onComplete: (spec) => {
|
|
309
|
+
instance.unmount();
|
|
310
|
+
resolve(spec);
|
|
311
|
+
},
|
|
312
|
+
onExit: () => {
|
|
313
|
+
instance.unmount();
|
|
314
|
+
resolve(null);
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
if (!generatedSpec) {
|
|
320
|
+
logger.info('Spec generation cancelled');
|
|
321
|
+
return;
|
|
295
322
|
}
|
|
296
|
-
);
|
|
297
323
|
|
|
298
|
-
|
|
299
|
-
|
|
324
|
+
specContent = generatedSpec;
|
|
325
|
+
} else {
|
|
326
|
+
// Use readline-based SpecGenerator
|
|
327
|
+
const specGenerator = new SpecGenerator({
|
|
328
|
+
featureName: feature,
|
|
329
|
+
projectRoot,
|
|
330
|
+
provider,
|
|
331
|
+
model,
|
|
332
|
+
scanResult,
|
|
333
|
+
});
|
|
300
334
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
335
|
+
// Initialize tracing BEFORE creating parent span (ensures logger is ready)
|
|
336
|
+
initTracing();
|
|
337
|
+
|
|
338
|
+
const generatedSpec = await traced(
|
|
339
|
+
async () => {
|
|
340
|
+
// All AI calls inside run() automatically become child spans
|
|
341
|
+
return await specGenerator.run();
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: `generate-spec-${feature}`,
|
|
345
|
+
type: 'task',
|
|
346
|
+
}
|
|
347
|
+
);
|
|
305
348
|
|
|
306
|
-
|
|
349
|
+
// Flush any pending Braintrust tracing spans
|
|
350
|
+
await flushTracing();
|
|
351
|
+
|
|
352
|
+
if (!generatedSpec) {
|
|
353
|
+
logger.info('Spec generation cancelled');
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
specContent = generatedSpec;
|
|
358
|
+
}
|
|
307
359
|
|
|
308
360
|
// Confirm saving (unless --yes)
|
|
309
361
|
if (!options.yes) {
|
package/src/index.ts
CHANGED
|
@@ -1,45 +1,124 @@
|
|
|
1
1
|
import { createCli } from './cli.js';
|
|
2
|
-
import {
|
|
2
|
+
import { createSessionState } from './repl/index.js';
|
|
3
3
|
import { hasConfig, loadConfigWithDefaults } from './utils/config.js';
|
|
4
4
|
import { getAvailableProvider } from './ai/providers.js';
|
|
5
|
-
import { displayHeader } from './utils/header.js';
|
|
6
5
|
import { notifyIfUpdateAvailable } from './utils/update-check.js';
|
|
6
|
+
import { renderApp } from './tui/app.js';
|
|
7
|
+
import { runInitWorkflow } from './commands/init.js';
|
|
8
|
+
import { logger } from './utils/logger.js';
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
import { dirname, join } from 'path';
|
|
12
|
+
import type { Instance } from 'ink';
|
|
7
13
|
|
|
8
14
|
/**
|
|
9
|
-
*
|
|
15
|
+
* Get version from package.json
|
|
16
|
+
*/
|
|
17
|
+
function getVersion(): string {
|
|
18
|
+
try {
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = dirname(__filename);
|
|
21
|
+
const packagePath = join(__dirname, '..', 'package.json');
|
|
22
|
+
const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
23
|
+
return pkg.version || '0.8.0';
|
|
24
|
+
} catch {
|
|
25
|
+
return '0.8.0';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Start Ink TUI mode
|
|
10
31
|
* Called when wiggum is invoked with no arguments
|
|
11
32
|
*/
|
|
12
|
-
async function
|
|
33
|
+
async function startInkTui(): Promise<void> {
|
|
13
34
|
const projectRoot = process.cwd();
|
|
14
|
-
const
|
|
35
|
+
const version = getVersion();
|
|
36
|
+
|
|
37
|
+
// Track current Ink instance for init workflow
|
|
38
|
+
let instance: Instance | null = null;
|
|
39
|
+
let shouldRestart = false;
|
|
15
40
|
|
|
16
|
-
|
|
17
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Create session state based on current project state
|
|
43
|
+
*/
|
|
44
|
+
async function createCurrentSessionState() {
|
|
45
|
+
const provider = getAvailableProvider();
|
|
46
|
+
const isInitialized = hasConfig(projectRoot);
|
|
47
|
+
let config = null;
|
|
18
48
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
49
|
+
if (isInitialized) {
|
|
50
|
+
config = await loadConfigWithDefaults(projectRoot);
|
|
51
|
+
}
|
|
22
52
|
|
|
23
|
-
|
|
24
|
-
|
|
53
|
+
return createSessionState(
|
|
54
|
+
projectRoot,
|
|
55
|
+
provider, // May be null if no API key
|
|
56
|
+
'sonnet', // Default model, will be updated after /init
|
|
57
|
+
undefined, // No scan result yet
|
|
58
|
+
config,
|
|
59
|
+
isInitialized
|
|
60
|
+
);
|
|
25
61
|
}
|
|
26
62
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Render the TUI app
|
|
65
|
+
*/
|
|
66
|
+
async function renderTui(screen: 'welcome' | 'shell' = 'welcome') {
|
|
67
|
+
const initialState = await createCurrentSessionState();
|
|
68
|
+
|
|
69
|
+
instance = renderApp({
|
|
70
|
+
screen,
|
|
71
|
+
initialSessionState: initialState,
|
|
72
|
+
version,
|
|
73
|
+
onComplete: (specPath) => {
|
|
74
|
+
// Spec was saved to disk by app.tsx
|
|
75
|
+
logger.success(`Created spec: ${specPath}`);
|
|
76
|
+
},
|
|
77
|
+
onExit: () => {
|
|
78
|
+
instance?.unmount();
|
|
79
|
+
if (!shouldRestart) {
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
onRunInit: async () => {
|
|
84
|
+
// Unmount Ink to run init workflow with readline prompts
|
|
85
|
+
shouldRestart = true;
|
|
86
|
+
instance?.unmount();
|
|
87
|
+
|
|
88
|
+
// Clear screen for init workflow
|
|
89
|
+
console.clear();
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const result = await runInitWorkflow(projectRoot, {});
|
|
93
|
+
|
|
94
|
+
if (result) {
|
|
95
|
+
logger.success('Initialization complete!');
|
|
96
|
+
console.log('');
|
|
97
|
+
} else {
|
|
98
|
+
logger.info('Initialization cancelled');
|
|
99
|
+
console.log('');
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
logger.error(`Init failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
103
|
+
console.log('');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Re-render TUI with updated state
|
|
107
|
+
shouldRestart = false;
|
|
108
|
+
await renderTui('shell');
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
await instance.waitUntilExit();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Start with welcome screen
|
|
116
|
+
await renderTui('welcome');
|
|
38
117
|
}
|
|
39
118
|
|
|
40
119
|
/**
|
|
41
|
-
* Main entry point for the
|
|
42
|
-
*
|
|
120
|
+
* Main entry point for the Wiggum CLI
|
|
121
|
+
* TUI-first: no args = start Ink TUI, otherwise use CLI
|
|
43
122
|
*/
|
|
44
123
|
export async function main(): Promise<void> {
|
|
45
124
|
const args = process.argv.slice(2);
|
|
@@ -47,9 +126,9 @@ export async function main(): Promise<void> {
|
|
|
47
126
|
// Check for updates (non-blocking, fails silently)
|
|
48
127
|
await notifyIfUpdateAvailable();
|
|
49
128
|
|
|
50
|
-
//
|
|
129
|
+
// TUI-first: no args = start Ink TUI
|
|
51
130
|
if (args.length === 0) {
|
|
52
|
-
await
|
|
131
|
+
await startInkTui();
|
|
53
132
|
return;
|
|
54
133
|
}
|
|
55
134
|
|
|
@@ -89,3 +168,6 @@ export type {
|
|
|
89
168
|
WriteOptions,
|
|
90
169
|
WriteSummary,
|
|
91
170
|
} from './generator/index.js';
|
|
171
|
+
|
|
172
|
+
// Export TUI components for programmatic use
|
|
173
|
+
export { renderApp, type RenderAppOptions, type AppScreen } from './tui/app.js';
|