roguelike-cli 1.0.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/INSTALL.md +108 -0
- package/bin/rlc +4 -0
- package/dist/ai/claude.js +115 -0
- package/dist/commands/init.js +175 -0
- package/dist/config/config.js +85 -0
- package/dist/index.js +29 -0
- package/dist/interactive/commands.js +552 -0
- package/dist/interactive/index.js +180 -0
- package/dist/interactive/startup.js +38 -0
- package/dist/storage/nodeConfig.js +109 -0
- package/dist/storage/storage.js +155 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/schemaParser.js +217 -0
- package/package.json +45 -0
- package/src/ai/claude.ts +139 -0
- package/src/commands/init.ts +159 -0
- package/src/config/config.ts +69 -0
- package/src/index.ts +37 -0
- package/src/interactive/commands.ts +625 -0
- package/src/interactive/index.ts +174 -0
- package/src/interactive/startup.ts +38 -0
- package/src/storage/nodeConfig.ts +109 -0
- package/src/storage/storage.ts +143 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/schemaParser.ts +259 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.sessionState = void 0;
|
|
37
|
+
exports.processCommand = processCommand;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const storage_1 = require("../storage/storage");
|
|
42
|
+
const nodeConfig_1 = require("../storage/nodeConfig");
|
|
43
|
+
const claude_1 = require("../ai/claude");
|
|
44
|
+
// Global session state
|
|
45
|
+
exports.sessionState = {
|
|
46
|
+
pending: null,
|
|
47
|
+
history: []
|
|
48
|
+
};
|
|
49
|
+
// Format items in columns like native ls
|
|
50
|
+
function formatColumns(items, termWidth = 80) {
|
|
51
|
+
if (items.length === 0)
|
|
52
|
+
return '';
|
|
53
|
+
const maxLen = Math.max(...items.map(s => s.length)) + 2;
|
|
54
|
+
const cols = Math.max(1, Math.floor(termWidth / maxLen));
|
|
55
|
+
const rows = [];
|
|
56
|
+
for (let i = 0; i < items.length; i += cols) {
|
|
57
|
+
const row = items.slice(i, i + cols);
|
|
58
|
+
rows.push(row.map(item => item.padEnd(maxLen)).join('').trimEnd());
|
|
59
|
+
}
|
|
60
|
+
return rows.join('\n');
|
|
61
|
+
}
|
|
62
|
+
// Copy to clipboard (cross-platform)
|
|
63
|
+
function copyToClipboard(text) {
|
|
64
|
+
const platform = process.platform;
|
|
65
|
+
try {
|
|
66
|
+
if (platform === 'darwin') {
|
|
67
|
+
(0, child_process_1.execSync)('pbcopy', { input: text });
|
|
68
|
+
}
|
|
69
|
+
else if (platform === 'win32') {
|
|
70
|
+
(0, child_process_1.execSync)('clip', { input: text });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// Linux - try xclip or xsel
|
|
74
|
+
try {
|
|
75
|
+
(0, child_process_1.execSync)('xclip -selection clipboard', { input: text });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
(0, child_process_1.execSync)('xsel --clipboard --input', { input: text });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
// Silently fail if clipboard not available
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Helper function for recursive copy
|
|
87
|
+
function copyRecursive(src, dest) {
|
|
88
|
+
const stat = fs.statSync(src);
|
|
89
|
+
if (stat.isDirectory()) {
|
|
90
|
+
if (!fs.existsSync(dest)) {
|
|
91
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
92
|
+
}
|
|
93
|
+
const entries = fs.readdirSync(src);
|
|
94
|
+
for (const entry of entries) {
|
|
95
|
+
const srcPath = path.join(src, entry);
|
|
96
|
+
const destPath = path.join(dest, entry);
|
|
97
|
+
copyRecursive(srcPath, destPath);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
fs.copyFileSync(src, dest);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function processCommand(input, currentPath, config, signal) {
|
|
105
|
+
// Check for clipboard pipe
|
|
106
|
+
const clipboardPipe = /\s*\|\s*(pbcopy|copy|clip)\s*$/i;
|
|
107
|
+
const shouldCopy = clipboardPipe.test(input);
|
|
108
|
+
const cleanInput = input.replace(clipboardPipe, '').trim();
|
|
109
|
+
const parts = cleanInput.split(' ').filter(p => p.length > 0);
|
|
110
|
+
const command = parts[0].toLowerCase();
|
|
111
|
+
// Helper to wrap result with clipboard copy
|
|
112
|
+
const wrapResult = (result) => {
|
|
113
|
+
if (shouldCopy && result.output) {
|
|
114
|
+
copyToClipboard(result.output);
|
|
115
|
+
result.output += '\n\n[copied to clipboard]';
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
};
|
|
119
|
+
if (command === 'ls') {
|
|
120
|
+
if (!fs.existsSync(currentPath)) {
|
|
121
|
+
return wrapResult({ output: 'Directory does not exist.' });
|
|
122
|
+
}
|
|
123
|
+
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
124
|
+
const items = [];
|
|
125
|
+
for (const entry of entries) {
|
|
126
|
+
if (entry.name.startsWith('.'))
|
|
127
|
+
continue;
|
|
128
|
+
if (entry.isDirectory()) {
|
|
129
|
+
items.push(entry.name + '/');
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
items.push(entry.name);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (items.length === 0) {
|
|
136
|
+
return wrapResult({ output: '' });
|
|
137
|
+
}
|
|
138
|
+
const termWidth = process.stdout.columns || 80;
|
|
139
|
+
return wrapResult({ output: formatColumns(items, termWidth) });
|
|
140
|
+
}
|
|
141
|
+
if (command === 'tree') {
|
|
142
|
+
const showFiles = parts.includes('-A') || parts.includes('--all');
|
|
143
|
+
// Parse depth: --depth=N or -d N
|
|
144
|
+
let maxDepth = 10;
|
|
145
|
+
const depthFlag = parts.find(p => p.startsWith('--depth='));
|
|
146
|
+
if (depthFlag) {
|
|
147
|
+
maxDepth = parseInt(depthFlag.split('=')[1]) || 10;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const dIndex = parts.indexOf('-d');
|
|
151
|
+
if (dIndex !== -1 && parts[dIndex + 1]) {
|
|
152
|
+
maxDepth = parseInt(parts[dIndex + 1]) || 10;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const treeLines = (0, storage_1.getTree)(currentPath, '', true, maxDepth, 0, showFiles);
|
|
156
|
+
if (treeLines.length === 0) {
|
|
157
|
+
return wrapResult({ output: 'No items found.' });
|
|
158
|
+
}
|
|
159
|
+
return wrapResult({ output: treeLines.join('\n') });
|
|
160
|
+
}
|
|
161
|
+
// Handle navigation without 'cd' command (.., ...)
|
|
162
|
+
if (command === '..' || command === '...') {
|
|
163
|
+
let levels = command === '...' ? 2 : 1;
|
|
164
|
+
let targetPath = currentPath;
|
|
165
|
+
for (let i = 0; i < levels; i++) {
|
|
166
|
+
const parentPath = path.dirname(targetPath);
|
|
167
|
+
if (parentPath === config.storagePath || parentPath.length < config.storagePath.length) {
|
|
168
|
+
return { output: 'Already at root.' };
|
|
169
|
+
}
|
|
170
|
+
targetPath = parentPath;
|
|
171
|
+
}
|
|
172
|
+
return { newPath: targetPath, output: '' };
|
|
173
|
+
}
|
|
174
|
+
if (command === 'mkdir') {
|
|
175
|
+
if (parts.length < 2) {
|
|
176
|
+
return { output: 'Usage: mkdir <name>' };
|
|
177
|
+
}
|
|
178
|
+
const name = parts.slice(1).join(' ');
|
|
179
|
+
const { createNode } = require('../storage/nodeConfig');
|
|
180
|
+
try {
|
|
181
|
+
const nodePath = createNode(currentPath, name);
|
|
182
|
+
return { output: `Created: ${name}` };
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
return { output: `Error: ${error.message}` };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (command === 'cp') {
|
|
189
|
+
if (parts.length < 3) {
|
|
190
|
+
return { output: 'Usage: cp <source> <destination>' };
|
|
191
|
+
}
|
|
192
|
+
const source = parts[1];
|
|
193
|
+
const dest = parts[2];
|
|
194
|
+
const sourcePath = path.isAbsolute(source) ? source : path.join(currentPath, source);
|
|
195
|
+
const destPath = path.isAbsolute(dest) ? dest : path.join(currentPath, dest);
|
|
196
|
+
if (!fs.existsSync(sourcePath)) {
|
|
197
|
+
return { output: `Source not found: ${source}` };
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
copyRecursive(sourcePath, destPath);
|
|
201
|
+
return { output: `Copied: ${source} -> ${dest}` };
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
return { output: `Error: ${error.message}` };
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (command === 'mv' || command === 'move') {
|
|
208
|
+
if (parts.length < 3) {
|
|
209
|
+
return { output: 'Usage: mv <source> <destination>' };
|
|
210
|
+
}
|
|
211
|
+
const source = parts[1];
|
|
212
|
+
const dest = parts[2];
|
|
213
|
+
const sourcePath = path.isAbsolute(source) ? source : path.join(currentPath, source);
|
|
214
|
+
const destPath = path.isAbsolute(dest) ? dest : path.join(currentPath, dest);
|
|
215
|
+
if (!fs.existsSync(sourcePath)) {
|
|
216
|
+
return { output: `Source not found: ${source}` };
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
fs.renameSync(sourcePath, destPath);
|
|
220
|
+
return { output: `Moved: ${source} -> ${dest}` };
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
// If rename fails (cross-device), copy then delete
|
|
224
|
+
try {
|
|
225
|
+
copyRecursive(sourcePath, destPath);
|
|
226
|
+
fs.rmSync(sourcePath, { recursive: true, force: true });
|
|
227
|
+
return { output: `Moved: ${source} -> ${dest}` };
|
|
228
|
+
}
|
|
229
|
+
catch (e) {
|
|
230
|
+
return { output: `Error: ${e.message}` };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (command === 'open') {
|
|
235
|
+
const { exec } = require('child_process');
|
|
236
|
+
// open or open . - open current folder in system file manager
|
|
237
|
+
if (parts.length < 2 || parts[1] === '.') {
|
|
238
|
+
exec(`open "${currentPath}"`);
|
|
239
|
+
return { output: `Opening: ${currentPath}` };
|
|
240
|
+
}
|
|
241
|
+
const name = parts.slice(1).join(' ');
|
|
242
|
+
const targetPath = path.join(currentPath, name);
|
|
243
|
+
// Check if target exists
|
|
244
|
+
if (fs.existsSync(targetPath)) {
|
|
245
|
+
const stat = fs.statSync(targetPath);
|
|
246
|
+
if (stat.isDirectory()) {
|
|
247
|
+
// It's a folder, open in file manager
|
|
248
|
+
exec(`open "${targetPath}"`);
|
|
249
|
+
return { output: `Opening: ${targetPath}` };
|
|
250
|
+
}
|
|
251
|
+
if (stat.isFile()) {
|
|
252
|
+
// It's a file, show its content (supports | pbcopy)
|
|
253
|
+
const content = fs.readFileSync(targetPath, 'utf-8');
|
|
254
|
+
return wrapResult({ output: content });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return wrapResult({ output: `Not found: ${name}` });
|
|
258
|
+
}
|
|
259
|
+
if (command === 'rm') {
|
|
260
|
+
if (parts.length < 2) {
|
|
261
|
+
return { output: 'Usage: rm <name> or rm -rf <name>' };
|
|
262
|
+
}
|
|
263
|
+
const isRecursive = parts[1] === '-rf' || parts[1] === '-r';
|
|
264
|
+
const targetName = isRecursive ? parts.slice(2).join(' ') : parts.slice(1).join(' ');
|
|
265
|
+
if (!targetName) {
|
|
266
|
+
return { output: 'Usage: rm <name> or rm -rf <name>' };
|
|
267
|
+
}
|
|
268
|
+
const targetPath = path.isAbsolute(targetName) ? targetName : path.join(currentPath, targetName);
|
|
269
|
+
if (!fs.existsSync(targetPath)) {
|
|
270
|
+
return { output: `Not found: ${targetName}` };
|
|
271
|
+
}
|
|
272
|
+
try {
|
|
273
|
+
if (isRecursive) {
|
|
274
|
+
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
const stat = fs.statSync(targetPath);
|
|
278
|
+
if (stat.isDirectory()) {
|
|
279
|
+
return { output: `Error: ${targetName} is a directory. Use rm -rf to remove directories.` };
|
|
280
|
+
}
|
|
281
|
+
fs.unlinkSync(targetPath);
|
|
282
|
+
}
|
|
283
|
+
return { output: `Removed: ${targetName}` };
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
return { output: `Error: ${error.message}` };
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (command === 'init') {
|
|
290
|
+
const { initCommand } = await Promise.resolve().then(() => __importStar(require('../commands/init')));
|
|
291
|
+
await initCommand();
|
|
292
|
+
return { output: 'Initialization complete. You can now use rlc.\n', reloadConfig: true };
|
|
293
|
+
}
|
|
294
|
+
if (command === 'cd') {
|
|
295
|
+
if (parts.length < 2) {
|
|
296
|
+
return { output: 'Usage: cd <node> or cd .. or cd <path>' };
|
|
297
|
+
}
|
|
298
|
+
const target = parts.slice(1).join(' ');
|
|
299
|
+
if (target === '..') {
|
|
300
|
+
const parentPath = path.dirname(currentPath);
|
|
301
|
+
if (parentPath === config.storagePath || parentPath.length < config.storagePath.length) {
|
|
302
|
+
return { output: 'Already at root.' };
|
|
303
|
+
}
|
|
304
|
+
return { newPath: parentPath, output: '' };
|
|
305
|
+
}
|
|
306
|
+
if (target === '...') {
|
|
307
|
+
let targetPath = path.dirname(currentPath);
|
|
308
|
+
targetPath = path.dirname(targetPath);
|
|
309
|
+
if (targetPath.length < config.storagePath.length) {
|
|
310
|
+
return { output: 'Already at root.' };
|
|
311
|
+
}
|
|
312
|
+
return { newPath: targetPath, output: '' };
|
|
313
|
+
}
|
|
314
|
+
// Handle paths like "cd bank/account" or "cd ../other"
|
|
315
|
+
if (target.includes('/')) {
|
|
316
|
+
let targetPath = currentPath;
|
|
317
|
+
const pathParts = target.split('/');
|
|
318
|
+
for (const part of pathParts) {
|
|
319
|
+
if (part === '..') {
|
|
320
|
+
targetPath = path.dirname(targetPath);
|
|
321
|
+
}
|
|
322
|
+
else if (part === '.') {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
const newPath = (0, storage_1.navigateToNode)(targetPath, part);
|
|
327
|
+
if (!newPath) {
|
|
328
|
+
return { output: `Path "${target}" not found.` };
|
|
329
|
+
}
|
|
330
|
+
targetPath = newPath;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return { newPath: targetPath, output: '' };
|
|
334
|
+
}
|
|
335
|
+
const newPath = (0, storage_1.navigateToNode)(currentPath, target);
|
|
336
|
+
if (!newPath) {
|
|
337
|
+
return { output: `Node "${target}" not found.` };
|
|
338
|
+
}
|
|
339
|
+
return { newPath, output: '' };
|
|
340
|
+
}
|
|
341
|
+
if (command === 'pwd') {
|
|
342
|
+
return wrapResult({ output: currentPath });
|
|
343
|
+
}
|
|
344
|
+
if (command === 'config') {
|
|
345
|
+
const maskedKey = config.apiKey
|
|
346
|
+
? config.apiKey.slice(0, 8) + '...' + config.apiKey.slice(-4)
|
|
347
|
+
: '(not set)';
|
|
348
|
+
const output = `
|
|
349
|
+
Provider: ${config.aiProvider}
|
|
350
|
+
Model: ${config.model || '(default)'}
|
|
351
|
+
API Key: ${maskedKey}
|
|
352
|
+
Storage: ${config.storagePath}
|
|
353
|
+
`.trim();
|
|
354
|
+
return wrapResult({ output });
|
|
355
|
+
}
|
|
356
|
+
if (command.startsWith('config:')) {
|
|
357
|
+
const configParts = input.split(':').slice(1).join(':').trim().split('=');
|
|
358
|
+
if (configParts.length !== 2) {
|
|
359
|
+
return { output: 'Usage: config:key=value' };
|
|
360
|
+
}
|
|
361
|
+
const key = configParts[0].trim();
|
|
362
|
+
const value = configParts[1].trim();
|
|
363
|
+
if (key === 'apiKey') {
|
|
364
|
+
const { updateConfig } = await Promise.resolve().then(() => __importStar(require('../config/config')));
|
|
365
|
+
updateConfig({ apiKey: value });
|
|
366
|
+
return { output: 'API key updated.' };
|
|
367
|
+
}
|
|
368
|
+
if (key === 'storagePath') {
|
|
369
|
+
const { updateConfig } = await Promise.resolve().then(() => __importStar(require('../config/config')));
|
|
370
|
+
updateConfig({ storagePath: value, currentPath: value });
|
|
371
|
+
return { output: `Storage path updated to: ${value}` };
|
|
372
|
+
}
|
|
373
|
+
return { output: `Unknown config key: ${key}` };
|
|
374
|
+
}
|
|
375
|
+
if (command === 'help') {
|
|
376
|
+
return wrapResult({
|
|
377
|
+
output: `Commands:
|
|
378
|
+
init - Initialize rlc (first time setup)
|
|
379
|
+
ls - List all schemas, todos, and notes
|
|
380
|
+
tree - Show directory tree structure
|
|
381
|
+
tree -A - Show tree with files
|
|
382
|
+
tree --depth=N - Limit tree depth (e.g., --depth=2)
|
|
383
|
+
cd <node> - Navigate into a node
|
|
384
|
+
cd .. - Go back to parent
|
|
385
|
+
pwd - Show current path
|
|
386
|
+
open - Open current folder in Finder
|
|
387
|
+
open <folder> - Open specific folder in Finder
|
|
388
|
+
mkdir <name> - Create new folder
|
|
389
|
+
cp <src> <dest> - Copy file or folder
|
|
390
|
+
mv <src> <dest> - Move/rename file or folder
|
|
391
|
+
rm <name> - Delete file
|
|
392
|
+
rm -rf <name> - Delete folder recursively
|
|
393
|
+
config - Show configuration
|
|
394
|
+
config:apiKey=<key> - Set API key
|
|
395
|
+
<description> - Create schema/todo (AI generates preview)
|
|
396
|
+
save - Save pending schema to disk
|
|
397
|
+
cancel - Discard pending schema
|
|
398
|
+
clean - Show items to delete in current folder
|
|
399
|
+
clean --yes - Delete all items in current folder
|
|
400
|
+
exit/quit - Exit the program
|
|
401
|
+
|
|
402
|
+
Clipboard:
|
|
403
|
+
ls | pbcopy - Copy output to clipboard (macOS)
|
|
404
|
+
tree | pbcopy - Works with any command
|
|
405
|
+
config | copy - Alternative for Windows
|
|
406
|
+
|
|
407
|
+
Workflow:
|
|
408
|
+
1. Type description (e.g., "todo: deploy app")
|
|
409
|
+
2. AI generates schema preview
|
|
410
|
+
3. Refine with more instructions if needed
|
|
411
|
+
4. Type "save" to save or "cancel" to discard
|
|
412
|
+
|
|
413
|
+
Examples:
|
|
414
|
+
|
|
415
|
+
> todo opening company in delaware
|
|
416
|
+
|
|
417
|
+
┌─ TODO opening company in delaware ───────────────────────────┐
|
|
418
|
+
│ │
|
|
419
|
+
├── register business name │
|
|
420
|
+
├── file incorporation papers │
|
|
421
|
+
├── get EIN number │
|
|
422
|
+
└── Branch: legal │
|
|
423
|
+
└── open business bank account │
|
|
424
|
+
│ │
|
|
425
|
+
└───────────────────────────────────────────────────────────────┘
|
|
426
|
+
|
|
427
|
+
> yandex cloud production infrastructure
|
|
428
|
+
|
|
429
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
430
|
+
│ Yandex Cloud │
|
|
431
|
+
│ │
|
|
432
|
+
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
433
|
+
│ │ back-fastapi │ │ admin-next │ │
|
|
434
|
+
│ │ (VM) │ │ (VM) │ │
|
|
435
|
+
│ └────────┬─────────┘ └──────────────────┘ │
|
|
436
|
+
│ │ │
|
|
437
|
+
│ ├──────────────────┬─────────────────┐ │
|
|
438
|
+
│ │ │ │ │
|
|
439
|
+
│ ┌────────▼────────┐ ┌─────▼──────┐ ┌──────▼────────┐ │
|
|
440
|
+
│ │ PostgreSQL │ │ Redis │ │ Cloudflare │ │
|
|
441
|
+
│ │ (Existing DB) │ │ Cluster │ │ R2 Storage │ │
|
|
442
|
+
│ └─────────────────┘ └────────────┘ └───────────────┘ │
|
|
443
|
+
└─────────────────────────────────────────────────────────────┘
|
|
444
|
+
|
|
445
|
+
> architecture production redis web application
|
|
446
|
+
|
|
447
|
+
┌─ Architecture production redis web application ────────────┐
|
|
448
|
+
│ │
|
|
449
|
+
├── load-balancer │
|
|
450
|
+
├── web-servers │
|
|
451
|
+
│ ├── app-server-1 │
|
|
452
|
+
│ ├── app-server-2 │
|
|
453
|
+
│ └── app-server-3 │
|
|
454
|
+
├── redis │
|
|
455
|
+
│ ├── cache-cluster │
|
|
456
|
+
│ └── session-store │
|
|
457
|
+
└── database │
|
|
458
|
+
├── postgres-primary │
|
|
459
|
+
└── postgres-replica │
|
|
460
|
+
│ │
|
|
461
|
+
└───────────────────────────────────────────────────────────────┘
|
|
462
|
+
|
|
463
|
+
> kubernetes cluster with clusters postgres and redis
|
|
464
|
+
|
|
465
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
466
|
+
│ Kubernetes cluster with clusters postgres │
|
|
467
|
+
│ │
|
|
468
|
+
│ ┌──────────────┐ ┌──────────────┐ │
|
|
469
|
+
│ │ postgres │ │ redis │ │
|
|
470
|
+
│ │ │ │ │ │
|
|
471
|
+
│ │ primary-pod │ │ cache-pod-1 │ │
|
|
472
|
+
│ │ replica-pod-1│ │ cache-pod-2 │ │
|
|
473
|
+
│ │ replica-pod-2│ │ │ │
|
|
474
|
+
│ └──────┬───────┘ └──────┬───────┘ │
|
|
475
|
+
│ │ │ │
|
|
476
|
+
│ └──────────┬───────────┘ │
|
|
477
|
+
│ │ │
|
|
478
|
+
│ ┌───────▼────────┐ │
|
|
479
|
+
│ │ worker-zones │ │
|
|
480
|
+
│ │ zone-1 │ │
|
|
481
|
+
│ │ zone-2 │ │
|
|
482
|
+
│ └────────────────┘ │
|
|
483
|
+
└─────────────────────────────────────────────────────────────┘
|
|
484
|
+
|
|
485
|
+
www.rlc.rocks`
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
// Save command - save pending schema to .rlc.schema file
|
|
489
|
+
if (command === 'save') {
|
|
490
|
+
if (!exports.sessionState.pending) {
|
|
491
|
+
return wrapResult({ output: 'Nothing to save. Create a schema first.' });
|
|
492
|
+
}
|
|
493
|
+
const schemaPath = (0, nodeConfig_1.saveSchemaFile)(currentPath, exports.sessionState.pending.title, exports.sessionState.pending.content);
|
|
494
|
+
const relativePath = path.relative(config.storagePath, schemaPath);
|
|
495
|
+
const filename = path.basename(schemaPath);
|
|
496
|
+
// Clear session
|
|
497
|
+
exports.sessionState.pending = null;
|
|
498
|
+
exports.sessionState.history = [];
|
|
499
|
+
return wrapResult({ output: `Saved: ${filename}` });
|
|
500
|
+
}
|
|
501
|
+
// Cancel command - discard pending schema
|
|
502
|
+
if (command === 'cancel') {
|
|
503
|
+
if (!exports.sessionState.pending) {
|
|
504
|
+
return wrapResult({ output: 'Nothing to cancel.' });
|
|
505
|
+
}
|
|
506
|
+
exports.sessionState.pending = null;
|
|
507
|
+
exports.sessionState.history = [];
|
|
508
|
+
return wrapResult({ output: 'Discarded pending schema.' });
|
|
509
|
+
}
|
|
510
|
+
// Clean command - clear current directory
|
|
511
|
+
if (command === 'clean') {
|
|
512
|
+
const entries = fs.readdirSync(currentPath);
|
|
513
|
+
const toDelete = entries.filter(e => !e.startsWith('.'));
|
|
514
|
+
if (toDelete.length === 0) {
|
|
515
|
+
return wrapResult({ output: 'Directory is already empty.' });
|
|
516
|
+
}
|
|
517
|
+
// Check for --yes flag to skip confirmation
|
|
518
|
+
if (!parts.includes('--yes') && !parts.includes('-y')) {
|
|
519
|
+
return wrapResult({
|
|
520
|
+
output: `Will delete ${toDelete.length} items:\n${toDelete.join('\n')}\n\nRun "clean --yes" to confirm.`
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
for (const entry of toDelete) {
|
|
524
|
+
const entryPath = path.join(currentPath, entry);
|
|
525
|
+
fs.rmSync(entryPath, { recursive: true, force: true });
|
|
526
|
+
}
|
|
527
|
+
return wrapResult({ output: `Deleted ${toDelete.length} items.` });
|
|
528
|
+
}
|
|
529
|
+
// AI generation - store in pending, don't save immediately
|
|
530
|
+
const fullInput = cleanInput;
|
|
531
|
+
// Add user message to history
|
|
532
|
+
exports.sessionState.history.push({ role: 'user', content: fullInput });
|
|
533
|
+
const schema = await (0, claude_1.generateSchemaWithAI)(fullInput, config, signal, exports.sessionState.history);
|
|
534
|
+
if (signal?.aborted) {
|
|
535
|
+
return { output: 'Command cancelled.' };
|
|
536
|
+
}
|
|
537
|
+
if (schema) {
|
|
538
|
+
// Store in pending
|
|
539
|
+
exports.sessionState.pending = {
|
|
540
|
+
title: schema.title,
|
|
541
|
+
content: schema.content,
|
|
542
|
+
tree: schema.tree
|
|
543
|
+
};
|
|
544
|
+
// Add assistant response to history
|
|
545
|
+
exports.sessionState.history.push({ role: 'assistant', content: schema.content });
|
|
546
|
+
const filename = schema.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
547
|
+
return wrapResult({
|
|
548
|
+
output: `\n${schema.content}\n\n[Type "save" to save as ${filename}.rlc.schema, or refine with more instructions]`
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
return wrapResult({ output: 'Could not generate schema. Make sure API key is set.' });
|
|
552
|
+
}
|