@compilr-dev/cli 0.4.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/README.md +110 -0
- package/dist/agent.d.ts +62 -0
- package/dist/agent.js +317 -0
- package/dist/agents/registry.d.ts +66 -0
- package/dist/agents/registry.js +238 -0
- package/dist/agents/types.d.ts +40 -0
- package/dist/agents/types.js +94 -0
- package/dist/commands/custom-registry.d.ts +69 -0
- package/dist/commands/custom-registry.js +246 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/types.d.ts +31 -0
- package/dist/commands/types.js +26 -0
- package/dist/commands.d.ts +63 -0
- package/dist/commands.js +324 -0
- package/dist/db/index.d.ts +42 -0
- package/dist/db/index.js +146 -0
- package/dist/db/repositories/document-repository.d.ts +63 -0
- package/dist/db/repositories/document-repository.js +184 -0
- package/dist/db/repositories/index.d.ts +9 -0
- package/dist/db/repositories/index.js +6 -0
- package/dist/db/repositories/project-repository.d.ts +132 -0
- package/dist/db/repositories/project-repository.js +337 -0
- package/dist/db/repositories/work-item-repository.d.ts +115 -0
- package/dist/db/repositories/work-item-repository.js +389 -0
- package/dist/db/schema.d.ts +83 -0
- package/dist/db/schema.js +143 -0
- package/dist/debug.d.ts +8 -0
- package/dist/debug.js +48 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +348 -0
- package/dist/index.old.d.ts +7 -0
- package/dist/index.old.js +1014 -0
- package/dist/repl.d.ts +121 -0
- package/dist/repl.js +1878 -0
- package/dist/settings/index.d.ts +80 -0
- package/dist/settings/index.js +195 -0
- package/dist/shared-handlers.d.ts +63 -0
- package/dist/shared-handlers.js +57 -0
- package/dist/slash-autocomplete.d.ts +41 -0
- package/dist/slash-autocomplete.js +638 -0
- package/dist/state.d.ts +75 -0
- package/dist/state.js +130 -0
- package/dist/tabbed-menu.d.ts +11 -0
- package/dist/tabbed-menu.js +328 -0
- package/dist/templates/backlog-md.d.ts +7 -0
- package/dist/templates/backlog-md.js +94 -0
- package/dist/templates/claude-md.d.ts +7 -0
- package/dist/templates/claude-md.js +189 -0
- package/dist/templates/coding-standards.d.ts +7 -0
- package/dist/templates/coding-standards.js +299 -0
- package/dist/templates/compilr-md.d.ts +7 -0
- package/dist/templates/compilr-md.js +189 -0
- package/dist/templates/config-json.d.ts +38 -0
- package/dist/templates/config-json.js +39 -0
- package/dist/templates/gitignore.d.ts +7 -0
- package/dist/templates/gitignore.js +85 -0
- package/dist/templates/index.d.ts +19 -0
- package/dist/templates/index.js +302 -0
- package/dist/templates/package-json.d.ts +7 -0
- package/dist/templates/package-json.js +111 -0
- package/dist/templates/readme-md.d.ts +7 -0
- package/dist/templates/readme-md.js +161 -0
- package/dist/templates/tsconfig.d.ts +7 -0
- package/dist/templates/tsconfig.js +61 -0
- package/dist/templates/types.d.ts +33 -0
- package/dist/templates/types.js +24 -0
- package/dist/test-autocomplete.d.ts +7 -0
- package/dist/test-autocomplete.js +85 -0
- package/dist/test-tabbed-menu.d.ts +7 -0
- package/dist/test-tabbed-menu.js +25 -0
- package/dist/themes/colors.d.ts +49 -0
- package/dist/themes/colors.js +135 -0
- package/dist/themes/index.d.ts +23 -0
- package/dist/themes/index.js +24 -0
- package/dist/themes/registry.d.ts +60 -0
- package/dist/themes/registry.js +195 -0
- package/dist/themes/types.d.ts +82 -0
- package/dist/themes/types.js +7 -0
- package/dist/tool-selector.d.ts +71 -0
- package/dist/tool-selector.js +184 -0
- package/dist/tools/ask-user-simple.d.ts +19 -0
- package/dist/tools/ask-user-simple.js +86 -0
- package/dist/tools/ask-user.d.ts +32 -0
- package/dist/tools/ask-user.js +113 -0
- package/dist/tools/backlog.d.ts +53 -0
- package/dist/tools/backlog.js +709 -0
- package/dist/tools.d.ts +15 -0
- package/dist/tools.js +121 -0
- package/dist/ui/agents-overlay.d.ts +12 -0
- package/dist/ui/agents-overlay.js +501 -0
- package/dist/ui/arch-type-overlay.d.ts +20 -0
- package/dist/ui/arch-type-overlay.js +229 -0
- package/dist/ui/ask-user-overlay.d.ts +26 -0
- package/dist/ui/ask-user-overlay.js +647 -0
- package/dist/ui/ask-user-simple-overlay.d.ts +25 -0
- package/dist/ui/ask-user-simple-overlay.js +242 -0
- package/dist/ui/backlog-overlay.d.ts +17 -0
- package/dist/ui/backlog-overlay.js +786 -0
- package/dist/ui/commands-overlay.d.ts +11 -0
- package/dist/ui/commands-overlay.js +410 -0
- package/dist/ui/config-overlay.d.ts +34 -0
- package/dist/ui/config-overlay.js +977 -0
- package/dist/ui/conversation.d.ts +82 -0
- package/dist/ui/conversation.js +508 -0
- package/dist/ui/diff.d.ts +38 -0
- package/dist/ui/diff.js +182 -0
- package/dist/ui/ephemeral.d.ts +111 -0
- package/dist/ui/ephemeral.js +413 -0
- package/dist/ui/file-autocomplete.d.ts +45 -0
- package/dist/ui/file-autocomplete.js +237 -0
- package/dist/ui/footer.d.ts +153 -0
- package/dist/ui/footer.js +422 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.js +15 -0
- package/dist/ui/init-overlay.d.ts +24 -0
- package/dist/ui/init-overlay.js +525 -0
- package/dist/ui/input-prompt-v2.d.ts +179 -0
- package/dist/ui/input-prompt-v2.js +991 -0
- package/dist/ui/input-prompt.d.ts +97 -0
- package/dist/ui/input-prompt.js +800 -0
- package/dist/ui/iteration-limit-overlay.d.ts +21 -0
- package/dist/ui/iteration-limit-overlay.js +150 -0
- package/dist/ui/keys-overlay.d.ts +14 -0
- package/dist/ui/keys-overlay.js +181 -0
- package/dist/ui/model-warning-overlay.d.ts +30 -0
- package/dist/ui/model-warning-overlay.js +171 -0
- package/dist/ui/overlay-controller.d.ts +25 -0
- package/dist/ui/overlay-controller.js +35 -0
- package/dist/ui/overlays.d.ts +47 -0
- package/dist/ui/overlays.js +627 -0
- package/dist/ui/permission-overlay.d.ts +16 -0
- package/dist/ui/permission-overlay.js +494 -0
- package/dist/ui/terminal.d.ts +117 -0
- package/dist/ui/terminal.js +237 -0
- package/dist/ui/todo-zone.d.ts +112 -0
- package/dist/ui/todo-zone.js +353 -0
- package/dist/ui/tools-overlay.d.ts +26 -0
- package/dist/ui/tools-overlay.js +278 -0
- package/dist/ui/tutorial-overlay.d.ts +10 -0
- package/dist/ui/tutorial-overlay.js +936 -0
- package/dist/ui/types.d.ts +103 -0
- package/dist/ui/types.js +33 -0
- package/dist/utils/credentials.d.ts +55 -0
- package/dist/utils/credentials.js +268 -0
- package/dist/utils/model-tiers.d.ts +37 -0
- package/dist/utils/model-tiers.js +118 -0
- package/dist/utils/project-memory.d.ts +47 -0
- package/dist/utils/project-memory.js +117 -0
- package/dist/utils/project-status.d.ts +56 -0
- package/dist/utils/project-status.js +237 -0
- package/package.json +66 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Init Overlay
|
|
3
|
+
*
|
|
4
|
+
* Modal overlay for the /init wizard - creates new projects with structured workflow.
|
|
5
|
+
* Steps:
|
|
6
|
+
* 0. Project type (new vs existing)
|
|
7
|
+
* 1. Project name
|
|
8
|
+
* 2. Description
|
|
9
|
+
* 3. Repo pattern (single vs two-repo)
|
|
10
|
+
* 4. Tech stack
|
|
11
|
+
* 5. Coding standards
|
|
12
|
+
* 6. Initialize git?
|
|
13
|
+
* 7. Confirmation
|
|
14
|
+
*/
|
|
15
|
+
import chalk from 'chalk';
|
|
16
|
+
import * as terminal from './terminal.js';
|
|
17
|
+
import { getStyles } from '../themes/index.js';
|
|
18
|
+
import { TECH_STACK_LABELS, CODING_STANDARDS_LABELS, REPO_PATTERN_LABELS, generateProject, isValidProjectName, projectExists, } from '../templates/index.js';
|
|
19
|
+
// =============================================================================
|
|
20
|
+
// Constants
|
|
21
|
+
// =============================================================================
|
|
22
|
+
const TECH_STACK_OPTIONS = ['react-node-pg', 'react-python-pg', 'vue-node-pg', 'custom'];
|
|
23
|
+
const CODING_STANDARDS_OPTIONS = ['strict', 'relaxed', 'custom'];
|
|
24
|
+
const REPO_PATTERN_OPTIONS = ['single', 'two-repo'];
|
|
25
|
+
// =============================================================================
|
|
26
|
+
// Rendering Helpers
|
|
27
|
+
// =============================================================================
|
|
28
|
+
function renderHeader(step) {
|
|
29
|
+
const s = getStyles();
|
|
30
|
+
const lines = [];
|
|
31
|
+
const cols = terminal.getTerminalWidth();
|
|
32
|
+
lines.push(s.muted('─'.repeat(Math.max(1, cols - 1))));
|
|
33
|
+
lines.push(' ' + s.primaryBold('Initialize New Project') + s.muted(` [${String(step)}/7]`));
|
|
34
|
+
lines.push('');
|
|
35
|
+
return lines;
|
|
36
|
+
}
|
|
37
|
+
function renderFooter(step) {
|
|
38
|
+
const s = getStyles();
|
|
39
|
+
const lines = [];
|
|
40
|
+
lines.push('');
|
|
41
|
+
if (step === 0) {
|
|
42
|
+
lines.push(s.muted(' ↑↓ Navigate · Enter Select · Esc Cancel'));
|
|
43
|
+
}
|
|
44
|
+
else if (step === 7) {
|
|
45
|
+
lines.push(s.muted(' ↑↓ Navigate · Enter Confirm · Esc Go back'));
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
lines.push(s.muted(' Enter Confirm · Esc Go back'));
|
|
49
|
+
}
|
|
50
|
+
return lines;
|
|
51
|
+
}
|
|
52
|
+
function renderSelectionOption(index, selectedIndex, label, disabled = false) {
|
|
53
|
+
const s = getStyles();
|
|
54
|
+
const isSelected = index === selectedIndex;
|
|
55
|
+
const prefix = isSelected ? ' ❯ ' : ' ';
|
|
56
|
+
if (disabled) {
|
|
57
|
+
return s.muted(prefix + label + ' (coming soon)');
|
|
58
|
+
}
|
|
59
|
+
return isSelected ? s.primary(prefix + label) : s.muted(prefix + label);
|
|
60
|
+
}
|
|
61
|
+
// =============================================================================
|
|
62
|
+
// Step Renderers
|
|
63
|
+
// =============================================================================
|
|
64
|
+
function renderStep0(state) {
|
|
65
|
+
const s = getStyles();
|
|
66
|
+
const lines = [];
|
|
67
|
+
lines.push(chalk.bold(' What would you like to do?'));
|
|
68
|
+
lines.push('');
|
|
69
|
+
lines.push(renderSelectionOption(0, state.selectedIndex, '1. Start a new project'));
|
|
70
|
+
lines.push(renderSelectionOption(1, state.selectedIndex, '2. Add workflow to existing project', true));
|
|
71
|
+
lines.push('');
|
|
72
|
+
lines.push(s.muted(' Start fresh with a structured project layout'));
|
|
73
|
+
lines.push(s.muted(' optimized for AI-assisted development.'));
|
|
74
|
+
return lines;
|
|
75
|
+
}
|
|
76
|
+
function renderStep1(state) {
|
|
77
|
+
const s = getStyles();
|
|
78
|
+
const lines = [];
|
|
79
|
+
lines.push(chalk.bold(' Project name'));
|
|
80
|
+
lines.push('');
|
|
81
|
+
lines.push(` > ${state.inputBuffer}█`);
|
|
82
|
+
lines.push('');
|
|
83
|
+
if (state.error) {
|
|
84
|
+
lines.push(s.error(` ${state.error}`));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
lines.push(s.muted(' Use lowercase letters, numbers, hyphens (e.g., my-saas-app)'));
|
|
88
|
+
lines.push(s.muted(' 2-50 characters, must start with a letter'));
|
|
89
|
+
}
|
|
90
|
+
return lines;
|
|
91
|
+
}
|
|
92
|
+
function renderStep2(state) {
|
|
93
|
+
const s = getStyles();
|
|
94
|
+
const lines = [];
|
|
95
|
+
lines.push(chalk.bold(' What are you building?'));
|
|
96
|
+
lines.push('');
|
|
97
|
+
lines.push(` > ${state.inputBuffer}█`);
|
|
98
|
+
lines.push('');
|
|
99
|
+
if (state.error) {
|
|
100
|
+
lines.push(s.error(` ${state.error}`));
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
lines.push(s.muted(' Brief description (1-2 sentences)'));
|
|
104
|
+
lines.push(s.muted(' Example: "A task management SaaS for small teams"'));
|
|
105
|
+
}
|
|
106
|
+
return lines;
|
|
107
|
+
}
|
|
108
|
+
function renderStep3(state) {
|
|
109
|
+
const s = getStyles();
|
|
110
|
+
const lines = [];
|
|
111
|
+
lines.push(chalk.bold(' Documentation structure'));
|
|
112
|
+
lines.push('');
|
|
113
|
+
for (let i = 0; i < REPO_PATTERN_OPTIONS.length; i++) {
|
|
114
|
+
const pattern = REPO_PATTERN_OPTIONS[i];
|
|
115
|
+
lines.push(renderSelectionOption(i, state.selectedIndex, `${String(i + 1)}. ${REPO_PATTERN_LABELS[pattern]}`));
|
|
116
|
+
}
|
|
117
|
+
lines.push('');
|
|
118
|
+
lines.push(s.muted(' Single repo: All files in one repository'));
|
|
119
|
+
lines.push(s.muted(' Two repos: Code and docs in separate repositories'));
|
|
120
|
+
return lines;
|
|
121
|
+
}
|
|
122
|
+
function renderStep4(state) {
|
|
123
|
+
const s = getStyles();
|
|
124
|
+
const lines = [];
|
|
125
|
+
lines.push(chalk.bold(' Tech stack'));
|
|
126
|
+
lines.push('');
|
|
127
|
+
for (let i = 0; i < TECH_STACK_OPTIONS.length; i++) {
|
|
128
|
+
const stack = TECH_STACK_OPTIONS[i];
|
|
129
|
+
lines.push(renderSelectionOption(i, state.selectedIndex, `${String(i + 1)}. ${TECH_STACK_LABELS[stack]}`));
|
|
130
|
+
}
|
|
131
|
+
lines.push('');
|
|
132
|
+
lines.push(s.muted(' Choose a preset or configure later'));
|
|
133
|
+
return lines;
|
|
134
|
+
}
|
|
135
|
+
function renderStep5(state) {
|
|
136
|
+
const s = getStyles();
|
|
137
|
+
const lines = [];
|
|
138
|
+
lines.push(chalk.bold(' Coding standards preset'));
|
|
139
|
+
lines.push('');
|
|
140
|
+
for (let i = 0; i < CODING_STANDARDS_OPTIONS.length; i++) {
|
|
141
|
+
const standard = CODING_STANDARDS_OPTIONS[i];
|
|
142
|
+
lines.push(renderSelectionOption(i, state.selectedIndex, `${String(i + 1)}. ${CODING_STANDARDS_LABELS[standard]}`));
|
|
143
|
+
}
|
|
144
|
+
lines.push('');
|
|
145
|
+
lines.push(s.muted(' Strict: Full TypeScript with strict mode + ESLint'));
|
|
146
|
+
lines.push(s.muted(' Relaxed: TypeScript with basic strict mode'));
|
|
147
|
+
lines.push(s.muted(' Custom: Configure your own standards later'));
|
|
148
|
+
return lines;
|
|
149
|
+
}
|
|
150
|
+
function renderStep6(state) {
|
|
151
|
+
const s = getStyles();
|
|
152
|
+
const lines = [];
|
|
153
|
+
lines.push(chalk.bold(' Initialize git repository?'));
|
|
154
|
+
lines.push('');
|
|
155
|
+
lines.push(renderSelectionOption(0, state.selectedIndex, 'Yes'));
|
|
156
|
+
lines.push(renderSelectionOption(1, state.selectedIndex, 'No'));
|
|
157
|
+
lines.push('');
|
|
158
|
+
lines.push(s.muted(' Creates git repo with initial commit'));
|
|
159
|
+
return lines;
|
|
160
|
+
}
|
|
161
|
+
function renderStep7(state) {
|
|
162
|
+
const s = getStyles();
|
|
163
|
+
const lines = [];
|
|
164
|
+
lines.push(chalk.bold(' Ready to create project?'));
|
|
165
|
+
lines.push('');
|
|
166
|
+
lines.push(` Project: ${s.primary(state.projectName)}`);
|
|
167
|
+
lines.push(` Location: ${s.primary('./' + state.projectName + '/')}`);
|
|
168
|
+
lines.push(` Structure: ${s.primary(state.repoPattern === 'single' ? 'Single repo' : 'Two repos')}`);
|
|
169
|
+
lines.push(` Tech: ${s.primary(state.techStack ? TECH_STACK_LABELS[state.techStack] : 'Not set')}`);
|
|
170
|
+
lines.push(` Standards: ${s.primary(state.codingStandards ? CODING_STANDARDS_LABELS[state.codingStandards] : 'Not set')}`);
|
|
171
|
+
lines.push(` Git: ${s.primary(state.initGit ? 'Yes' : 'No')}`);
|
|
172
|
+
if (state.repoPattern === 'two-repo') {
|
|
173
|
+
lines.push('');
|
|
174
|
+
lines.push(s.muted(` Docs repo: ./${state.projectName}-docs/`));
|
|
175
|
+
}
|
|
176
|
+
lines.push('');
|
|
177
|
+
if (state.error) {
|
|
178
|
+
lines.push(s.error(` ${state.error}`));
|
|
179
|
+
lines.push('');
|
|
180
|
+
}
|
|
181
|
+
lines.push(renderSelectionOption(0, state.selectedIndex, 'Create'));
|
|
182
|
+
lines.push(renderSelectionOption(1, state.selectedIndex, 'Cancel'));
|
|
183
|
+
return lines;
|
|
184
|
+
}
|
|
185
|
+
// =============================================================================
|
|
186
|
+
// Unified Rendering
|
|
187
|
+
// =============================================================================
|
|
188
|
+
function buildLines(state) {
|
|
189
|
+
const lines = [];
|
|
190
|
+
lines.push(...renderHeader(state.step));
|
|
191
|
+
switch (state.step) {
|
|
192
|
+
case 0:
|
|
193
|
+
lines.push(...renderStep0(state));
|
|
194
|
+
break;
|
|
195
|
+
case 1:
|
|
196
|
+
lines.push(...renderStep1(state));
|
|
197
|
+
break;
|
|
198
|
+
case 2:
|
|
199
|
+
lines.push(...renderStep2(state));
|
|
200
|
+
break;
|
|
201
|
+
case 3:
|
|
202
|
+
lines.push(...renderStep3(state));
|
|
203
|
+
break;
|
|
204
|
+
case 4:
|
|
205
|
+
lines.push(...renderStep4(state));
|
|
206
|
+
break;
|
|
207
|
+
case 5:
|
|
208
|
+
lines.push(...renderStep5(state));
|
|
209
|
+
break;
|
|
210
|
+
case 6:
|
|
211
|
+
lines.push(...renderStep6(state));
|
|
212
|
+
break;
|
|
213
|
+
case 7:
|
|
214
|
+
lines.push(...renderStep7(state));
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
lines.push(...renderFooter(state.step));
|
|
218
|
+
return lines;
|
|
219
|
+
}
|
|
220
|
+
function render(state, prevLineCount) {
|
|
221
|
+
const lines = buildLines(state);
|
|
222
|
+
// Clear previous content
|
|
223
|
+
terminal.clearLinesAbove(prevLineCount);
|
|
224
|
+
// Write new content
|
|
225
|
+
terminal.write(lines.join('\n'));
|
|
226
|
+
return lines.length;
|
|
227
|
+
}
|
|
228
|
+
// =============================================================================
|
|
229
|
+
// Step Navigation
|
|
230
|
+
// =============================================================================
|
|
231
|
+
function getMaxOptionsForStep(step) {
|
|
232
|
+
switch (step) {
|
|
233
|
+
case 0:
|
|
234
|
+
return 2;
|
|
235
|
+
case 3:
|
|
236
|
+
return REPO_PATTERN_OPTIONS.length;
|
|
237
|
+
case 4:
|
|
238
|
+
return TECH_STACK_OPTIONS.length;
|
|
239
|
+
case 5:
|
|
240
|
+
return CODING_STANDARDS_OPTIONS.length;
|
|
241
|
+
case 6:
|
|
242
|
+
return 2;
|
|
243
|
+
case 7:
|
|
244
|
+
return 2;
|
|
245
|
+
default:
|
|
246
|
+
return 0;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function isSelectionStep(step) {
|
|
250
|
+
return [0, 3, 4, 5, 6, 7].includes(step);
|
|
251
|
+
}
|
|
252
|
+
function isInputStep(step) {
|
|
253
|
+
return [1, 2].includes(step);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Show the init wizard overlay
|
|
257
|
+
*/
|
|
258
|
+
export async function showInitOverlay() {
|
|
259
|
+
const state = {
|
|
260
|
+
step: 0,
|
|
261
|
+
projectType: null,
|
|
262
|
+
projectName: '',
|
|
263
|
+
description: '',
|
|
264
|
+
repoPattern: null,
|
|
265
|
+
techStack: null,
|
|
266
|
+
codingStandards: null,
|
|
267
|
+
initGit: true,
|
|
268
|
+
selectedIndex: 0,
|
|
269
|
+
inputBuffer: '',
|
|
270
|
+
error: null,
|
|
271
|
+
};
|
|
272
|
+
let lineCount = 0;
|
|
273
|
+
terminal.writeLine('');
|
|
274
|
+
terminal.hideCursor();
|
|
275
|
+
const wasRawMode = process.stdin.isRaw;
|
|
276
|
+
terminal.enableRawMode();
|
|
277
|
+
lineCount = render(state, 0);
|
|
278
|
+
// Navigate to next step
|
|
279
|
+
const nextStep = () => {
|
|
280
|
+
switch (state.step) {
|
|
281
|
+
case 0:
|
|
282
|
+
// Only "new" option works (index 0)
|
|
283
|
+
if (state.selectedIndex === 0) {
|
|
284
|
+
state.projectType = 'new';
|
|
285
|
+
state.step = 1;
|
|
286
|
+
state.inputBuffer = '';
|
|
287
|
+
state.selectedIndex = 0;
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
case 1:
|
|
291
|
+
state.projectName = state.inputBuffer.trim();
|
|
292
|
+
state.step = 2;
|
|
293
|
+
state.inputBuffer = '';
|
|
294
|
+
break;
|
|
295
|
+
case 2:
|
|
296
|
+
state.description = state.inputBuffer.trim();
|
|
297
|
+
state.step = 3;
|
|
298
|
+
state.selectedIndex = 0;
|
|
299
|
+
break;
|
|
300
|
+
case 3:
|
|
301
|
+
state.repoPattern = REPO_PATTERN_OPTIONS[state.selectedIndex];
|
|
302
|
+
state.step = 4;
|
|
303
|
+
state.selectedIndex = 0;
|
|
304
|
+
break;
|
|
305
|
+
case 4:
|
|
306
|
+
state.techStack = TECH_STACK_OPTIONS[state.selectedIndex];
|
|
307
|
+
state.step = 5;
|
|
308
|
+
state.selectedIndex = 0;
|
|
309
|
+
break;
|
|
310
|
+
case 5:
|
|
311
|
+
state.codingStandards = CODING_STANDARDS_OPTIONS[state.selectedIndex];
|
|
312
|
+
state.step = 6;
|
|
313
|
+
state.selectedIndex = 0;
|
|
314
|
+
break;
|
|
315
|
+
case 6:
|
|
316
|
+
state.initGit = state.selectedIndex === 0;
|
|
317
|
+
state.step = 7;
|
|
318
|
+
state.selectedIndex = 0;
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
state.error = null;
|
|
322
|
+
};
|
|
323
|
+
// Navigate to previous step
|
|
324
|
+
const prevStep = () => {
|
|
325
|
+
switch (state.step) {
|
|
326
|
+
case 1:
|
|
327
|
+
state.step = 0;
|
|
328
|
+
state.selectedIndex = 0;
|
|
329
|
+
break;
|
|
330
|
+
case 2:
|
|
331
|
+
state.step = 1;
|
|
332
|
+
state.inputBuffer = state.projectName;
|
|
333
|
+
break;
|
|
334
|
+
case 3:
|
|
335
|
+
state.step = 2;
|
|
336
|
+
state.inputBuffer = state.description;
|
|
337
|
+
break;
|
|
338
|
+
case 4:
|
|
339
|
+
state.step = 3;
|
|
340
|
+
state.selectedIndex = state.repoPattern ? REPO_PATTERN_OPTIONS.indexOf(state.repoPattern) : 0;
|
|
341
|
+
break;
|
|
342
|
+
case 5:
|
|
343
|
+
state.step = 4;
|
|
344
|
+
state.selectedIndex = state.techStack ? TECH_STACK_OPTIONS.indexOf(state.techStack) : 0;
|
|
345
|
+
break;
|
|
346
|
+
case 6:
|
|
347
|
+
state.step = 5;
|
|
348
|
+
state.selectedIndex = state.codingStandards ? CODING_STANDARDS_OPTIONS.indexOf(state.codingStandards) : 0;
|
|
349
|
+
break;
|
|
350
|
+
case 7:
|
|
351
|
+
state.step = 6;
|
|
352
|
+
state.selectedIndex = state.initGit ? 0 : 1;
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
state.error = null;
|
|
356
|
+
};
|
|
357
|
+
// Validate input for current step
|
|
358
|
+
const validateInput = () => {
|
|
359
|
+
if (state.step === 1) {
|
|
360
|
+
const name = state.inputBuffer.trim();
|
|
361
|
+
if (!name) {
|
|
362
|
+
state.error = 'Project name is required';
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
if (!isValidProjectName(name)) {
|
|
366
|
+
state.error = 'Invalid name. Use lowercase letters, numbers, hyphens (2-50 chars, start with letter)';
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
if (projectExists(name)) {
|
|
370
|
+
state.error = `Directory "${name}" already exists`;
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (state.step === 2) {
|
|
375
|
+
const desc = state.inputBuffer.trim();
|
|
376
|
+
if (!desc) {
|
|
377
|
+
state.error = 'Description is required';
|
|
378
|
+
return false;
|
|
379
|
+
}
|
|
380
|
+
if (desc.length < 10) {
|
|
381
|
+
state.error = 'Description too short (min 10 characters)';
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return true;
|
|
386
|
+
};
|
|
387
|
+
return new Promise((resolve) => {
|
|
388
|
+
const cleanup = () => {
|
|
389
|
+
terminal.clearLinesAbove(lineCount);
|
|
390
|
+
terminal.writeLine('');
|
|
391
|
+
terminal.showCursor();
|
|
392
|
+
if (!wasRawMode) {
|
|
393
|
+
terminal.disableRawMode();
|
|
394
|
+
}
|
|
395
|
+
process.stdin.removeListener('data', handleData);
|
|
396
|
+
};
|
|
397
|
+
const handleData = (data) => {
|
|
398
|
+
void onData(data);
|
|
399
|
+
};
|
|
400
|
+
const onData = async (data) => {
|
|
401
|
+
const isEscape = data.length === 1 && data[0] === 0x1b;
|
|
402
|
+
const isUpArrow = data.length === 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x41;
|
|
403
|
+
const isDownArrow = data.length === 3 && data[0] === 0x1b && data[1] === 0x5b && data[2] === 0x42;
|
|
404
|
+
const isCtrlC = data.length === 1 && data[0] === 0x03;
|
|
405
|
+
const isEnter = data.length === 1 && (data[0] === 0x0d || data[0] === 0x0a);
|
|
406
|
+
const isBackspace = data.length === 1 && (data[0] === 0x7f || data[0] === 0x08);
|
|
407
|
+
// Ctrl+C always cancels
|
|
408
|
+
if (isCtrlC) {
|
|
409
|
+
cleanup();
|
|
410
|
+
resolve({ created: false });
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
// Escape on step 0 cancels, otherwise goes back
|
|
414
|
+
if (isEscape) {
|
|
415
|
+
if (state.step === 0) {
|
|
416
|
+
cleanup();
|
|
417
|
+
resolve({ created: false });
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
prevStep();
|
|
421
|
+
lineCount = render(state, lineCount);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
// Handle selection steps
|
|
425
|
+
if (isSelectionStep(state.step)) {
|
|
426
|
+
const maxOptions = getMaxOptionsForStep(state.step);
|
|
427
|
+
if (isUpArrow && state.selectedIndex > 0) {
|
|
428
|
+
state.selectedIndex--;
|
|
429
|
+
lineCount = render(state, lineCount);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (isDownArrow && state.selectedIndex < maxOptions - 1) {
|
|
433
|
+
state.selectedIndex++;
|
|
434
|
+
lineCount = render(state, lineCount);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
if (isEnter) {
|
|
438
|
+
// Step 0 - only allow "new" option
|
|
439
|
+
if (state.step === 0 && state.selectedIndex === 1) {
|
|
440
|
+
state.error = 'This option is coming soon';
|
|
441
|
+
lineCount = render(state, lineCount);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
// Step 7 - confirmation
|
|
445
|
+
if (state.step === 7) {
|
|
446
|
+
if (state.selectedIndex === 1) {
|
|
447
|
+
// Cancel
|
|
448
|
+
cleanup();
|
|
449
|
+
resolve({ created: false });
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
// Verify all required fields are set (they should be by step 7)
|
|
453
|
+
if (!state.repoPattern || !state.techStack || !state.codingStandards) {
|
|
454
|
+
state.error = 'Missing configuration values';
|
|
455
|
+
lineCount = render(state, lineCount);
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
// Create project
|
|
459
|
+
const config = {
|
|
460
|
+
name: state.projectName,
|
|
461
|
+
description: state.description,
|
|
462
|
+
repoPattern: state.repoPattern,
|
|
463
|
+
techStack: state.techStack,
|
|
464
|
+
codingStandards: state.codingStandards,
|
|
465
|
+
initGit: state.initGit,
|
|
466
|
+
};
|
|
467
|
+
cleanup();
|
|
468
|
+
// Show "creating..." message
|
|
469
|
+
terminal.writeLine(chalk.cyan(' Creating project...'));
|
|
470
|
+
try {
|
|
471
|
+
const result = await generateProject(config);
|
|
472
|
+
if (result.success) {
|
|
473
|
+
resolve({
|
|
474
|
+
created: true,
|
|
475
|
+
projectPath: result.projectPath,
|
|
476
|
+
docsPath: result.docsPath,
|
|
477
|
+
filesCreated: result.filesCreated,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
terminal.writeLine(chalk.red(` Error: ${result.error ?? 'Unknown error'}`));
|
|
482
|
+
resolve({ created: false });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
terminal.writeLine(chalk.red(` Error: ${error.message}`));
|
|
487
|
+
resolve({ created: false });
|
|
488
|
+
}
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
nextStep();
|
|
492
|
+
lineCount = render(state, lineCount);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
// Handle input steps
|
|
497
|
+
if (isInputStep(state.step)) {
|
|
498
|
+
if (isEnter) {
|
|
499
|
+
if (validateInput()) {
|
|
500
|
+
nextStep();
|
|
501
|
+
lineCount = render(state, lineCount);
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
lineCount = render(state, lineCount);
|
|
505
|
+
}
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (isBackspace) {
|
|
509
|
+
state.inputBuffer = state.inputBuffer.slice(0, -1);
|
|
510
|
+
state.error = null;
|
|
511
|
+
lineCount = render(state, lineCount);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
// Regular character input
|
|
515
|
+
const char = data.toString('utf-8');
|
|
516
|
+
if (char.length === 1 && char.charCodeAt(0) >= 32) {
|
|
517
|
+
state.inputBuffer += char;
|
|
518
|
+
state.error = null;
|
|
519
|
+
lineCount = render(state, lineCount);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
process.stdin.on('data', handleData);
|
|
524
|
+
});
|
|
525
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Prompt v2 (Event-Driven)
|
|
3
|
+
*
|
|
4
|
+
* Refactored input handling with event-driven architecture.
|
|
5
|
+
* - Emits events instead of blocking with getInput()
|
|
6
|
+
* - Supports queue mode for capturing input during agent execution
|
|
7
|
+
* - Always captures keystrokes (no blocking)
|
|
8
|
+
*
|
|
9
|
+
* Events:
|
|
10
|
+
* - 'submit' - User pressed Enter with input
|
|
11
|
+
* - 'command' - User submitted a slash command
|
|
12
|
+
* - 'cancel' - User pressed Ctrl+C
|
|
13
|
+
* - 'escape' - User pressed Esc (for aborting agent)
|
|
14
|
+
* - 'change' - Input text changed
|
|
15
|
+
*/
|
|
16
|
+
import { EventEmitter } from 'events';
|
|
17
|
+
import type { CommandOption } from './types.js';
|
|
18
|
+
export declare const DEFAULT_COMMANDS: CommandOption[];
|
|
19
|
+
/**
|
|
20
|
+
* Strip ANSI codes from string
|
|
21
|
+
*/
|
|
22
|
+
export declare function stripAnsi(str: string): string;
|
|
23
|
+
export interface InputPromptEvents {
|
|
24
|
+
submit: (input: string) => void;
|
|
25
|
+
command: (command: string, args: string) => void;
|
|
26
|
+
cancel: () => void;
|
|
27
|
+
escape: () => void;
|
|
28
|
+
change: (input: string) => void;
|
|
29
|
+
modeChange: () => void;
|
|
30
|
+
}
|
|
31
|
+
export interface InputPromptOptions {
|
|
32
|
+
prompt?: string;
|
|
33
|
+
showSeparators?: boolean;
|
|
34
|
+
commands?: CommandOption[];
|
|
35
|
+
}
|
|
36
|
+
export declare class InputPrompt extends EventEmitter {
|
|
37
|
+
private prompt;
|
|
38
|
+
private promptLen;
|
|
39
|
+
private readonly showSeparators;
|
|
40
|
+
private readonly commands;
|
|
41
|
+
private state;
|
|
42
|
+
private autocomplete;
|
|
43
|
+
private fileAutocomplete;
|
|
44
|
+
private readonly history;
|
|
45
|
+
private historyIndex;
|
|
46
|
+
private savedInput;
|
|
47
|
+
private queueMode;
|
|
48
|
+
private queuedInputs;
|
|
49
|
+
private renderedLines;
|
|
50
|
+
private dropdownLines;
|
|
51
|
+
private linesAboveCursor;
|
|
52
|
+
private hasSeparators;
|
|
53
|
+
private isRunning;
|
|
54
|
+
private lastEscTime;
|
|
55
|
+
private readonly DOUBLE_ESC_THRESHOLD_MS;
|
|
56
|
+
private suggestion;
|
|
57
|
+
constructor(options?: InputPromptOptions);
|
|
58
|
+
/**
|
|
59
|
+
* Update the prompt string (for dynamic theme changes)
|
|
60
|
+
*/
|
|
61
|
+
setPrompt(prompt: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Set a suggestion for the next action (ghost text)
|
|
64
|
+
*/
|
|
65
|
+
setSuggestion(action: string | null): void;
|
|
66
|
+
/**
|
|
67
|
+
* Get the current suggestion
|
|
68
|
+
*/
|
|
69
|
+
getSuggestion(): string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Clear the current suggestion
|
|
72
|
+
*/
|
|
73
|
+
clearSuggestion(): void;
|
|
74
|
+
/**
|
|
75
|
+
* Start input capture (non-blocking, event-driven)
|
|
76
|
+
*/
|
|
77
|
+
start(): void;
|
|
78
|
+
/**
|
|
79
|
+
* Stop input capture
|
|
80
|
+
*/
|
|
81
|
+
stop(): void;
|
|
82
|
+
/**
|
|
83
|
+
* Check if running
|
|
84
|
+
*/
|
|
85
|
+
isActive(): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Enable/disable queue mode
|
|
88
|
+
* In queue mode, Enter adds to queue instead of emitting 'submit'
|
|
89
|
+
*/
|
|
90
|
+
setQueueMode(enabled: boolean): void;
|
|
91
|
+
/**
|
|
92
|
+
* Check if in queue mode
|
|
93
|
+
*/
|
|
94
|
+
isQueueMode(): boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Get all queued inputs (FIFO order)
|
|
97
|
+
*/
|
|
98
|
+
getQueuedInputs(): string[];
|
|
99
|
+
/**
|
|
100
|
+
* Pop the first queued input
|
|
101
|
+
*/
|
|
102
|
+
popQueuedInput(): string | null;
|
|
103
|
+
/**
|
|
104
|
+
* Check if there are queued inputs
|
|
105
|
+
*/
|
|
106
|
+
hasQueuedInput(): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Get number of queued inputs
|
|
109
|
+
*/
|
|
110
|
+
getQueueLength(): number;
|
|
111
|
+
/**
|
|
112
|
+
* Clear the queue
|
|
113
|
+
*/
|
|
114
|
+
clearQueue(): void;
|
|
115
|
+
/**
|
|
116
|
+
* Get current buffer value
|
|
117
|
+
*/
|
|
118
|
+
getValue(): string;
|
|
119
|
+
/**
|
|
120
|
+
* Set buffer value
|
|
121
|
+
*/
|
|
122
|
+
setValue(value: string): void;
|
|
123
|
+
/**
|
|
124
|
+
* Clear input buffer
|
|
125
|
+
*/
|
|
126
|
+
clearInput(): void;
|
|
127
|
+
/**
|
|
128
|
+
* Add command to history
|
|
129
|
+
*/
|
|
130
|
+
addToHistory(input: string): void;
|
|
131
|
+
/**
|
|
132
|
+
* Render the input prompt - returns array of lines
|
|
133
|
+
* Does NOT write to terminal (Footer handles that)
|
|
134
|
+
*/
|
|
135
|
+
render(): string[];
|
|
136
|
+
/**
|
|
137
|
+
* Get the cursor position info for rendering
|
|
138
|
+
*/
|
|
139
|
+
getCursorInfo(): {
|
|
140
|
+
row: number;
|
|
141
|
+
col: number;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Get autocomplete dropdown lines (if active)
|
|
145
|
+
*/
|
|
146
|
+
getAutocompleteLines(): string[];
|
|
147
|
+
/**
|
|
148
|
+
* Check if autocomplete is showing
|
|
149
|
+
*/
|
|
150
|
+
isAutocompleteActive(): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Get height of rendered content
|
|
153
|
+
*/
|
|
154
|
+
getHeight(): number;
|
|
155
|
+
/**
|
|
156
|
+
* @deprecated Use event-driven start() instead
|
|
157
|
+
* Kept for backwards compatibility during migration
|
|
158
|
+
*/
|
|
159
|
+
getInput(): Promise<{
|
|
160
|
+
action: string;
|
|
161
|
+
value?: string;
|
|
162
|
+
command?: string;
|
|
163
|
+
args?: string;
|
|
164
|
+
}>;
|
|
165
|
+
private resetState;
|
|
166
|
+
private updateAutocomplete;
|
|
167
|
+
private getSeparatorLine;
|
|
168
|
+
private readonly handleData;
|
|
169
|
+
private handleKey;
|
|
170
|
+
private handleEnter;
|
|
171
|
+
private handleTab;
|
|
172
|
+
private handleArrowUp;
|
|
173
|
+
private handleArrowDown;
|
|
174
|
+
private handleArrowLeft;
|
|
175
|
+
private handleArrowRight;
|
|
176
|
+
private handleWordLeft;
|
|
177
|
+
private handleWordRight;
|
|
178
|
+
private handleBackspace;
|
|
179
|
+
}
|