@agile-vibe-coding/avc 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/LICENSE +21 -0
- package/README.md +3 -0
- package/cli/index.js +28 -0
- package/cli/init.js +321 -0
- package/cli/logger.js +138 -0
- package/cli/repl-ink.js +764 -0
- package/cli/repl-old.js +353 -0
- package/cli/template-processor.js +491 -0
- package/cli/templates/project.md +62 -0
- package/cli/update-checker.js +218 -0
- package/cli/update-installer.js +184 -0
- package/cli/update-notifier.js +170 -0
- package/package.json +58 -0
package/cli/repl-old.js
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
import readline from 'readline';
|
|
2
|
+
import { readFileSync } from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { ProjectInitiator } from './init.js';
|
|
6
|
+
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* AVC REPL - Interactive command-line interface
|
|
12
|
+
* Similar to Claude Code's interactive shell
|
|
13
|
+
*/
|
|
14
|
+
export class AvcRepl {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.running = false;
|
|
17
|
+
this.rl = null;
|
|
18
|
+
this.version = this.getVersion();
|
|
19
|
+
this.commands = [
|
|
20
|
+
{ cmd: '/init', desc: 'Initialize an AVC project (Sponsor Call ceremony)' },
|
|
21
|
+
{ cmd: '/status', desc: 'Show current project status' },
|
|
22
|
+
{ cmd: '/help', desc: 'Show this help message' },
|
|
23
|
+
{ cmd: '/version', desc: 'Show version information' },
|
|
24
|
+
{ cmd: '/exit', desc: 'Exit AVC interactive mode' }
|
|
25
|
+
];
|
|
26
|
+
this.lastInput = '';
|
|
27
|
+
this.commandListShown = false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
getVersion() {
|
|
31
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
32
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
|
|
33
|
+
return packageJson.version;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
showBanner() {
|
|
37
|
+
console.log('\n');
|
|
38
|
+
console.log('AGILE VIBE CODING');
|
|
39
|
+
console.log('═════════════════');
|
|
40
|
+
console.log(`Version: ${this.version}`);
|
|
41
|
+
console.log('Framework for AI-powered Agile development\n');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
showHelp() {
|
|
45
|
+
console.log('\n📚 Available Commands:\n');
|
|
46
|
+
this.commands.forEach(c => {
|
|
47
|
+
console.log(` ${c.cmd.padEnd(12)} ${c.desc}`);
|
|
48
|
+
});
|
|
49
|
+
console.log('\n');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
showVersion() {
|
|
53
|
+
console.log(`\n🎯 AVC Framework v${this.version}`);
|
|
54
|
+
console.log(' Agile Vibe Coding - AI-powered development framework');
|
|
55
|
+
console.log(' https://agilevibecoding.org\n');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async handleCommand(command) {
|
|
59
|
+
const cmd = command.trim().toLowerCase();
|
|
60
|
+
|
|
61
|
+
switch (cmd) {
|
|
62
|
+
case '/help':
|
|
63
|
+
case '/h':
|
|
64
|
+
this.showHelp();
|
|
65
|
+
break;
|
|
66
|
+
|
|
67
|
+
case '/version':
|
|
68
|
+
case '/v':
|
|
69
|
+
this.showVersion();
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
case '/exit':
|
|
73
|
+
case '/quit':
|
|
74
|
+
case '/q':
|
|
75
|
+
console.log('\n👋 Thanks for using AVC!\n');
|
|
76
|
+
this.stop();
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case '/init':
|
|
80
|
+
await this.runInit();
|
|
81
|
+
break;
|
|
82
|
+
|
|
83
|
+
case '/status':
|
|
84
|
+
await this.runStatus();
|
|
85
|
+
break;
|
|
86
|
+
|
|
87
|
+
case '':
|
|
88
|
+
// Empty command, just show prompt again
|
|
89
|
+
break;
|
|
90
|
+
|
|
91
|
+
default:
|
|
92
|
+
if (cmd.startsWith('/')) {
|
|
93
|
+
console.log(`\n❌ Unknown command: ${cmd}`);
|
|
94
|
+
console.log(' Type /help to see available commands\n');
|
|
95
|
+
} else {
|
|
96
|
+
console.log('\n💡 Commands must start with /');
|
|
97
|
+
console.log(' Example: /init, /status, /help\n');
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async runInit() {
|
|
104
|
+
console.log(''); // Empty line before init output
|
|
105
|
+
const initiator = new ProjectInitiator();
|
|
106
|
+
await initiator.init();
|
|
107
|
+
console.log(''); // Empty line after init output
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async runStatus() {
|
|
111
|
+
console.log(''); // Empty line before status output
|
|
112
|
+
const initiator = new ProjectInitiator();
|
|
113
|
+
initiator.status();
|
|
114
|
+
console.log(''); // Empty line after status output
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
showPrompt() {
|
|
118
|
+
process.stdout.write('> ');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
getTerminalWidth() {
|
|
122
|
+
// Try multiple ways to get terminal width
|
|
123
|
+
return process.stdout.columns ||
|
|
124
|
+
process.stderr.columns ||
|
|
125
|
+
(this.rl && this.rl.output && this.rl.output.columns) ||
|
|
126
|
+
80;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
showTopLine() {
|
|
130
|
+
const width = this.getTerminalWidth();
|
|
131
|
+
console.log('─'.repeat(width));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
showBottomLine() {
|
|
135
|
+
const width = this.getTerminalWidth();
|
|
136
|
+
console.log('─'.repeat(width));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
showInfoLine() {
|
|
140
|
+
console.log('Available: /init /status /help /version /exit | Type / to see commands');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
clearCommandList() {
|
|
144
|
+
if (this.commandListShown) {
|
|
145
|
+
// Clear the command list lines (they're between input line and bottom line)
|
|
146
|
+
// Move down 1 to the first command line
|
|
147
|
+
readline.moveCursor(process.stdout, 0, 1);
|
|
148
|
+
|
|
149
|
+
// Clear each command line
|
|
150
|
+
this.commands.forEach(() => {
|
|
151
|
+
readline.clearLine(process.stdout, 0);
|
|
152
|
+
readline.moveCursor(process.stdout, 0, 1);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Clear bottom line and redraw it
|
|
156
|
+
readline.clearLine(process.stdout, 0);
|
|
157
|
+
this.showBottomLine();
|
|
158
|
+
|
|
159
|
+
// Move cursor back up to input line (past commands + 1 for bottom line)
|
|
160
|
+
readline.moveCursor(process.stdout, 0, -(this.commands.length + 1));
|
|
161
|
+
readline.cursorTo(process.stdout, this.rl.cursor + 2); // +2 for "> "
|
|
162
|
+
|
|
163
|
+
this.commandListShown = false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
showCommandList(filter) {
|
|
168
|
+
// Clear any existing list first
|
|
169
|
+
this.clearCommandList();
|
|
170
|
+
|
|
171
|
+
// Don't show list if filter is empty or doesn't start with /
|
|
172
|
+
if (!filter || !filter.startsWith('/')) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Filter commands
|
|
177
|
+
const filtered = this.commands.filter(c => c.cmd.startsWith(filter));
|
|
178
|
+
|
|
179
|
+
if (filtered.length === 0) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Save cursor position
|
|
184
|
+
const cursorPos = this.rl.cursor;
|
|
185
|
+
|
|
186
|
+
// Move to line below input
|
|
187
|
+
readline.moveCursor(process.stdout, 0, 1);
|
|
188
|
+
|
|
189
|
+
// Clear the bottom line and write command list
|
|
190
|
+
readline.clearLine(process.stdout, 0);
|
|
191
|
+
filtered.forEach((c, i) => {
|
|
192
|
+
process.stdout.write(` ${c.cmd.padEnd(12)} ${c.desc}`);
|
|
193
|
+
if (i < filtered.length - 1) {
|
|
194
|
+
process.stdout.write('\n');
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Write bottom line after commands
|
|
199
|
+
process.stdout.write('\n');
|
|
200
|
+
this.showBottomLine();
|
|
201
|
+
|
|
202
|
+
// Move cursor back up to input position
|
|
203
|
+
readline.moveCursor(process.stdout, 0, -(filtered.length + 1));
|
|
204
|
+
readline.cursorTo(process.stdout, cursorPos + 2); // +2 for "> "
|
|
205
|
+
|
|
206
|
+
this.commandListShown = true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
updateCommandList() {
|
|
210
|
+
const currentInput = this.rl.line || '';
|
|
211
|
+
|
|
212
|
+
// Only update if input changed
|
|
213
|
+
if (currentInput !== this.lastInput) {
|
|
214
|
+
this.lastInput = currentInput;
|
|
215
|
+
|
|
216
|
+
if (currentInput.startsWith('/')) {
|
|
217
|
+
this.showCommandList(currentInput);
|
|
218
|
+
} else if (this.commandListShown) {
|
|
219
|
+
this.clearCommandList();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
completer(line) {
|
|
225
|
+
// Return command names for TAB completion
|
|
226
|
+
if (!line.startsWith('/')) {
|
|
227
|
+
return [[], line];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const hits = this.commands
|
|
231
|
+
.filter((c) => c.cmd.startsWith(line))
|
|
232
|
+
.map(c => c.cmd);
|
|
233
|
+
|
|
234
|
+
return [hits.length ? hits : this.commands.map(c => c.cmd), line];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
start() {
|
|
238
|
+
this.running = true;
|
|
239
|
+
this.showBanner();
|
|
240
|
+
this.showInfoLine();
|
|
241
|
+
console.log(''); // Empty line after info
|
|
242
|
+
|
|
243
|
+
this.rl = readline.createInterface({
|
|
244
|
+
input: process.stdin,
|
|
245
|
+
output: process.stdout,
|
|
246
|
+
prompt: '',
|
|
247
|
+
completer: (line) => this.completer(line),
|
|
248
|
+
tabSize: 4
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Enable keypress events for real-time command list
|
|
252
|
+
if (process.stdin.isTTY) {
|
|
253
|
+
readline.emitKeypressEvents(process.stdin);
|
|
254
|
+
process.stdin.setRawMode(true);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Listen for terminal resize events
|
|
258
|
+
process.stdout.on('resize', () => {
|
|
259
|
+
// Terminal was resized - next prompt will use new width automatically
|
|
260
|
+
// since we read width dynamically in getTerminalWidth()
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Listen for keypress events
|
|
264
|
+
process.stdin.on('keypress', (str, key) => {
|
|
265
|
+
if (!this.running) return;
|
|
266
|
+
|
|
267
|
+
// Handle Ctrl+C
|
|
268
|
+
if (key && key.ctrl && key.name === 'c') {
|
|
269
|
+
this.clearCommandList();
|
|
270
|
+
if (process.stdin.isTTY) {
|
|
271
|
+
process.stdin.setRawMode(false);
|
|
272
|
+
}
|
|
273
|
+
console.log('\n\n👋 Thanks for using AVC!\n');
|
|
274
|
+
process.exit(0);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Handle Enter - clear command list
|
|
278
|
+
if (key && key.name === 'return') {
|
|
279
|
+
this.clearCommandList();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Update command list after keystroke is processed
|
|
284
|
+
setImmediate(() => {
|
|
285
|
+
this.updateCommandList();
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Show initial prompt with bottom line
|
|
290
|
+
this.showTopLine();
|
|
291
|
+
this.showPrompt();
|
|
292
|
+
process.stdout.write('\n');
|
|
293
|
+
this.showBottomLine();
|
|
294
|
+
|
|
295
|
+
// Move cursor back up to input line
|
|
296
|
+
readline.moveCursor(process.stdout, 2, -1); // +2 for "> ", -1 line up
|
|
297
|
+
|
|
298
|
+
this.rl.on('line', async (line) => {
|
|
299
|
+
// Clear command list
|
|
300
|
+
this.clearCommandList();
|
|
301
|
+
|
|
302
|
+
// Move cursor to after bottom line
|
|
303
|
+
readline.moveCursor(process.stdout, 0, 1);
|
|
304
|
+
process.stdout.write('\n');
|
|
305
|
+
|
|
306
|
+
// Disable raw mode for command execution
|
|
307
|
+
if (process.stdin.isTTY) {
|
|
308
|
+
process.stdin.setRawMode(false);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Process command (output appears here)
|
|
312
|
+
await this.handleCommand(line);
|
|
313
|
+
|
|
314
|
+
// Re-enable raw mode
|
|
315
|
+
if (process.stdin.isTTY && this.running) {
|
|
316
|
+
process.stdin.setRawMode(true);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Show next prompt with bottom line
|
|
320
|
+
if (this.running) {
|
|
321
|
+
this.showTopLine();
|
|
322
|
+
this.showPrompt();
|
|
323
|
+
process.stdout.write('\n');
|
|
324
|
+
this.showBottomLine();
|
|
325
|
+
|
|
326
|
+
// Move cursor back up to input line
|
|
327
|
+
readline.moveCursor(process.stdout, 2, -1); // +2 for "> ", -1 line up
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
this.rl.on('close', () => {
|
|
332
|
+
if (process.stdin.isTTY) {
|
|
333
|
+
process.stdin.setRawMode(false);
|
|
334
|
+
}
|
|
335
|
+
if (this.running) {
|
|
336
|
+
console.log('\n👋 Thanks for using AVC!\n');
|
|
337
|
+
process.exit(0);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
stop() {
|
|
343
|
+
this.running = false;
|
|
344
|
+
this.clearCommandList();
|
|
345
|
+
if (process.stdin.isTTY) {
|
|
346
|
+
process.stdin.setRawMode(false);
|
|
347
|
+
}
|
|
348
|
+
if (this.rl) {
|
|
349
|
+
this.rl.close();
|
|
350
|
+
}
|
|
351
|
+
process.exit(0);
|
|
352
|
+
}
|
|
353
|
+
}
|