roguelike-cli 1.0.0 → 1.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/dist/ai/claude.js +1 -0
- package/dist/interactive/commands.js +79 -10
- package/dist/interactive/startup.js +2 -1
- package/package.json +1 -1
- package/src/ai/claude.ts +2 -0
- package/src/interactive/commands.ts +100 -16
- package/src/interactive/startup.ts +2 -1
package/dist/ai/claude.js
CHANGED
|
@@ -41,6 +41,58 @@ const child_process_1 = require("child_process");
|
|
|
41
41
|
const storage_1 = require("../storage/storage");
|
|
42
42
|
const nodeConfig_1 = require("../storage/nodeConfig");
|
|
43
43
|
const claude_1 = require("../ai/claude");
|
|
44
|
+
// Parse tree ASCII art and create folder structure
|
|
45
|
+
function createFoldersFromTree(rootPath, treeContent) {
|
|
46
|
+
// Create root folder
|
|
47
|
+
if (!fs.existsSync(rootPath)) {
|
|
48
|
+
fs.mkdirSync(rootPath, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
// Parse tree lines
|
|
51
|
+
const lines = treeContent.split('\n');
|
|
52
|
+
const stack = [{ path: rootPath, indent: -1 }];
|
|
53
|
+
for (const line of lines) {
|
|
54
|
+
// Skip empty lines
|
|
55
|
+
if (!line.trim())
|
|
56
|
+
continue;
|
|
57
|
+
// Extract node name from tree line
|
|
58
|
+
// Patterns: "├── Name", "└── Name", "│ ├── Name", etc.
|
|
59
|
+
const match = line.match(/^([\s│]*)[├└]──\s*(.+)$/);
|
|
60
|
+
if (!match)
|
|
61
|
+
continue;
|
|
62
|
+
const prefix = match[1];
|
|
63
|
+
let nodeName = match[2].trim();
|
|
64
|
+
// Calculate indent level (each │ or space block = 1 level)
|
|
65
|
+
const indent = Math.floor(prefix.replace(/│/g, ' ').length / 4);
|
|
66
|
+
// Clean node name - remove extra info in parentheses, brackets
|
|
67
|
+
nodeName = nodeName.replace(/\s*\([^)]*\)\s*/g, '').trim();
|
|
68
|
+
nodeName = nodeName.replace(/\s*\[[^\]]*\]\s*/g, '').trim();
|
|
69
|
+
// Create safe folder name
|
|
70
|
+
const safeName = nodeName
|
|
71
|
+
.toLowerCase()
|
|
72
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
73
|
+
.replace(/^-+|-+$/g, '');
|
|
74
|
+
if (!safeName)
|
|
75
|
+
continue;
|
|
76
|
+
// Pop stack until we find parent
|
|
77
|
+
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
|
|
78
|
+
stack.pop();
|
|
79
|
+
}
|
|
80
|
+
const parentPath = stack[stack.length - 1].path;
|
|
81
|
+
const folderPath = path.join(parentPath, safeName);
|
|
82
|
+
// Create folder
|
|
83
|
+
if (!fs.existsSync(folderPath)) {
|
|
84
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
// Create node config
|
|
87
|
+
(0, nodeConfig_1.writeNodeConfig)(folderPath, {
|
|
88
|
+
name: nodeName,
|
|
89
|
+
createdAt: new Date().toISOString(),
|
|
90
|
+
updatedAt: new Date().toISOString(),
|
|
91
|
+
});
|
|
92
|
+
// Push to stack
|
|
93
|
+
stack.push({ path: folderPath, indent });
|
|
94
|
+
}
|
|
95
|
+
}
|
|
44
96
|
// Global session state
|
|
45
97
|
exports.sessionState = {
|
|
46
98
|
pending: null,
|
|
@@ -485,18 +537,31 @@ Examples:
|
|
|
485
537
|
www.rlc.rocks`
|
|
486
538
|
});
|
|
487
539
|
}
|
|
488
|
-
// Save command - save pending schema
|
|
540
|
+
// Save command - save pending schema/todo
|
|
489
541
|
if (command === 'save') {
|
|
490
542
|
if (!exports.sessionState.pending) {
|
|
491
543
|
return wrapResult({ output: 'Nothing to save. Create a schema first.' });
|
|
492
544
|
}
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
545
|
+
const pending = exports.sessionState.pending;
|
|
546
|
+
const safeName = pending.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
547
|
+
if (pending.format === 'tree') {
|
|
548
|
+
// Create folder structure from tree ASCII art
|
|
549
|
+
const rootPath = path.join(currentPath, safeName);
|
|
550
|
+
createFoldersFromTree(rootPath, pending.content);
|
|
551
|
+
// Clear session
|
|
552
|
+
exports.sessionState.pending = null;
|
|
553
|
+
exports.sessionState.history = [];
|
|
554
|
+
return wrapResult({ output: `Created todo folder: ${safeName}/` });
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
// Save as .rlc.schema file
|
|
558
|
+
const schemaPath = (0, nodeConfig_1.saveSchemaFile)(currentPath, pending.title, pending.content);
|
|
559
|
+
const filename = path.basename(schemaPath);
|
|
560
|
+
// Clear session
|
|
561
|
+
exports.sessionState.pending = null;
|
|
562
|
+
exports.sessionState.history = [];
|
|
563
|
+
return wrapResult({ output: `Saved: ${filename}` });
|
|
564
|
+
}
|
|
500
565
|
}
|
|
501
566
|
// Cancel command - discard pending schema
|
|
502
567
|
if (command === 'cancel') {
|
|
@@ -539,13 +604,17 @@ www.rlc.rocks`
|
|
|
539
604
|
exports.sessionState.pending = {
|
|
540
605
|
title: schema.title,
|
|
541
606
|
content: schema.content,
|
|
607
|
+
format: schema.format,
|
|
542
608
|
tree: schema.tree
|
|
543
609
|
};
|
|
544
610
|
// Add assistant response to history
|
|
545
611
|
exports.sessionState.history.push({ role: 'assistant', content: schema.content });
|
|
546
|
-
const
|
|
612
|
+
const safeName = schema.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
613
|
+
const saveHint = schema.format === 'tree'
|
|
614
|
+
? `[Type "save" to create folder ${safeName}/, or refine with more instructions]`
|
|
615
|
+
: `[Type "save" to save as ${safeName}.rlc.schema, or refine with more instructions]`;
|
|
547
616
|
return wrapResult({
|
|
548
|
-
output: `\n${schema.content}\n\n
|
|
617
|
+
output: `\n${schema.content}\n\n${saveHint}`
|
|
549
618
|
});
|
|
550
619
|
}
|
|
551
620
|
return wrapResult({ output: 'Could not generate schema. Make sure API key is set.' });
|
|
@@ -17,7 +17,6 @@ const ASCII_ART = [
|
|
|
17
17
|
'',
|
|
18
18
|
'╔═════════════════════════╗',
|
|
19
19
|
'║ Roguelike CLI v1.0 ║',
|
|
20
|
-
'║ www.rlc.rocks ║',
|
|
21
20
|
'╚═════════════════════════╝',
|
|
22
21
|
'',
|
|
23
22
|
' Commands: ls, cd, mkdir, open, cp, mv, rm, tree, pwd, clean',
|
|
@@ -26,6 +25,8 @@ const ASCII_ART = [
|
|
|
26
25
|
' Workflow: <description> -> refine -> save',
|
|
27
26
|
' init - setup, config - settings, help - examples',
|
|
28
27
|
'',
|
|
28
|
+
' www.rlc.rocks',
|
|
29
|
+
'',
|
|
29
30
|
' Ready...',
|
|
30
31
|
'',
|
|
31
32
|
];
|
package/package.json
CHANGED
package/src/ai/claude.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Config } from '../config/config';
|
|
|
4
4
|
export interface GeneratedSchema {
|
|
5
5
|
title: string;
|
|
6
6
|
content: string;
|
|
7
|
+
format: 'block' | 'tree';
|
|
7
8
|
tree?: any[];
|
|
8
9
|
}
|
|
9
10
|
|
|
@@ -130,6 +131,7 @@ export async function generateSchemaWithAI(
|
|
|
130
131
|
return {
|
|
131
132
|
title: parsed.title || 'schema',
|
|
132
133
|
content: schemaContent,
|
|
134
|
+
format: parsed.format || 'block',
|
|
133
135
|
};
|
|
134
136
|
} catch (error: any) {
|
|
135
137
|
console.error('AI Error:', error.message);
|
|
@@ -3,9 +3,72 @@ import * as fs from 'fs';
|
|
|
3
3
|
import { execSync } from 'child_process';
|
|
4
4
|
import { Config } from '../config/config';
|
|
5
5
|
import { listSchemas, navigateToNode, getTree } from '../storage/storage';
|
|
6
|
-
import { saveSchemaFile } from '../storage/nodeConfig';
|
|
6
|
+
import { saveSchemaFile, writeNodeConfig } from '../storage/nodeConfig';
|
|
7
7
|
import { generateSchemaWithAI } from '../ai/claude';
|
|
8
8
|
|
|
9
|
+
// Parse tree ASCII art and create folder structure
|
|
10
|
+
function createFoldersFromTree(rootPath: string, treeContent: string): void {
|
|
11
|
+
// Create root folder
|
|
12
|
+
if (!fs.existsSync(rootPath)) {
|
|
13
|
+
fs.mkdirSync(rootPath, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Parse tree lines
|
|
17
|
+
const lines = treeContent.split('\n');
|
|
18
|
+
const stack: { path: string; indent: number }[] = [{ path: rootPath, indent: -1 }];
|
|
19
|
+
|
|
20
|
+
for (const line of lines) {
|
|
21
|
+
// Skip empty lines
|
|
22
|
+
if (!line.trim()) continue;
|
|
23
|
+
|
|
24
|
+
// Extract node name from tree line
|
|
25
|
+
// Patterns: "├── Name", "└── Name", "│ ├── Name", etc.
|
|
26
|
+
const match = line.match(/^([\s│]*)[├└]──\s*(.+)$/);
|
|
27
|
+
if (!match) continue;
|
|
28
|
+
|
|
29
|
+
const prefix = match[1];
|
|
30
|
+
let nodeName = match[2].trim();
|
|
31
|
+
|
|
32
|
+
// Calculate indent level (each │ or space block = 1 level)
|
|
33
|
+
const indent = Math.floor(prefix.replace(/│/g, ' ').length / 4);
|
|
34
|
+
|
|
35
|
+
// Clean node name - remove extra info in parentheses, brackets
|
|
36
|
+
nodeName = nodeName.replace(/\s*\([^)]*\)\s*/g, '').trim();
|
|
37
|
+
nodeName = nodeName.replace(/\s*\[[^\]]*\]\s*/g, '').trim();
|
|
38
|
+
|
|
39
|
+
// Create safe folder name
|
|
40
|
+
const safeName = nodeName
|
|
41
|
+
.toLowerCase()
|
|
42
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
43
|
+
.replace(/^-+|-+$/g, '');
|
|
44
|
+
|
|
45
|
+
if (!safeName) continue;
|
|
46
|
+
|
|
47
|
+
// Pop stack until we find parent
|
|
48
|
+
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
|
|
49
|
+
stack.pop();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const parentPath = stack[stack.length - 1].path;
|
|
53
|
+
const folderPath = path.join(parentPath, safeName);
|
|
54
|
+
|
|
55
|
+
// Create folder
|
|
56
|
+
if (!fs.existsSync(folderPath)) {
|
|
57
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Create node config
|
|
61
|
+
writeNodeConfig(folderPath, {
|
|
62
|
+
name: nodeName,
|
|
63
|
+
createdAt: new Date().toISOString(),
|
|
64
|
+
updatedAt: new Date().toISOString(),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Push to stack
|
|
68
|
+
stack.push({ path: folderPath, indent });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
9
72
|
export interface CommandResult {
|
|
10
73
|
output?: string;
|
|
11
74
|
newPath?: string;
|
|
@@ -16,6 +79,7 @@ export interface CommandResult {
|
|
|
16
79
|
export interface PendingSchema {
|
|
17
80
|
title: string;
|
|
18
81
|
content: string;
|
|
82
|
+
format: 'block' | 'tree';
|
|
19
83
|
tree?: any[];
|
|
20
84
|
}
|
|
21
85
|
|
|
@@ -533,25 +597,40 @@ www.rlc.rocks`
|
|
|
533
597
|
});
|
|
534
598
|
}
|
|
535
599
|
|
|
536
|
-
// Save command - save pending schema
|
|
600
|
+
// Save command - save pending schema/todo
|
|
537
601
|
if (command === 'save') {
|
|
538
602
|
if (!sessionState.pending) {
|
|
539
603
|
return wrapResult({ output: 'Nothing to save. Create a schema first.' });
|
|
540
604
|
}
|
|
541
605
|
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
sessionState.pending.title,
|
|
545
|
-
sessionState.pending.content
|
|
546
|
-
);
|
|
547
|
-
const relativePath = path.relative(config.storagePath, schemaPath);
|
|
548
|
-
const filename = path.basename(schemaPath);
|
|
549
|
-
|
|
550
|
-
// Clear session
|
|
551
|
-
sessionState.pending = null;
|
|
552
|
-
sessionState.history = [];
|
|
606
|
+
const pending = sessionState.pending;
|
|
607
|
+
const safeName = pending.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
553
608
|
|
|
554
|
-
|
|
609
|
+
if (pending.format === 'tree') {
|
|
610
|
+
// Create folder structure from tree ASCII art
|
|
611
|
+
const rootPath = path.join(currentPath, safeName);
|
|
612
|
+
createFoldersFromTree(rootPath, pending.content);
|
|
613
|
+
|
|
614
|
+
// Clear session
|
|
615
|
+
sessionState.pending = null;
|
|
616
|
+
sessionState.history = [];
|
|
617
|
+
|
|
618
|
+
return wrapResult({ output: `Created todo folder: ${safeName}/` });
|
|
619
|
+
} else {
|
|
620
|
+
// Save as .rlc.schema file
|
|
621
|
+
const schemaPath = saveSchemaFile(
|
|
622
|
+
currentPath,
|
|
623
|
+
pending.title,
|
|
624
|
+
pending.content
|
|
625
|
+
);
|
|
626
|
+
const filename = path.basename(schemaPath);
|
|
627
|
+
|
|
628
|
+
// Clear session
|
|
629
|
+
sessionState.pending = null;
|
|
630
|
+
sessionState.history = [];
|
|
631
|
+
|
|
632
|
+
return wrapResult({ output: `Saved: ${filename}` });
|
|
633
|
+
}
|
|
555
634
|
}
|
|
556
635
|
|
|
557
636
|
// Cancel command - discard pending schema
|
|
@@ -607,16 +686,21 @@ www.rlc.rocks`
|
|
|
607
686
|
sessionState.pending = {
|
|
608
687
|
title: schema.title,
|
|
609
688
|
content: schema.content,
|
|
689
|
+
format: schema.format,
|
|
610
690
|
tree: schema.tree
|
|
611
691
|
};
|
|
612
692
|
|
|
613
693
|
// Add assistant response to history
|
|
614
694
|
sessionState.history.push({ role: 'assistant', content: schema.content });
|
|
615
695
|
|
|
616
|
-
const
|
|
696
|
+
const safeName = schema.title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
|
697
|
+
|
|
698
|
+
const saveHint = schema.format === 'tree'
|
|
699
|
+
? `[Type "save" to create folder ${safeName}/, or refine with more instructions]`
|
|
700
|
+
: `[Type "save" to save as ${safeName}.rlc.schema, or refine with more instructions]`;
|
|
617
701
|
|
|
618
702
|
return wrapResult({
|
|
619
|
-
output: `\n${schema.content}\n\n
|
|
703
|
+
output: `\n${schema.content}\n\n${saveHint}`
|
|
620
704
|
});
|
|
621
705
|
}
|
|
622
706
|
|
|
@@ -15,7 +15,6 @@ const ASCII_ART = [
|
|
|
15
15
|
'',
|
|
16
16
|
'╔═════════════════════════╗',
|
|
17
17
|
'║ Roguelike CLI v1.0 ║',
|
|
18
|
-
'║ www.rlc.rocks ║',
|
|
19
18
|
'╚═════════════════════════╝',
|
|
20
19
|
'',
|
|
21
20
|
' Commands: ls, cd, mkdir, open, cp, mv, rm, tree, pwd, clean',
|
|
@@ -24,6 +23,8 @@ const ASCII_ART = [
|
|
|
24
23
|
' Workflow: <description> -> refine -> save',
|
|
25
24
|
' init - setup, config - settings, help - examples',
|
|
26
25
|
'',
|
|
26
|
+
' www.rlc.rocks',
|
|
27
|
+
'',
|
|
27
28
|
' Ready...',
|
|
28
29
|
'',
|
|
29
30
|
];
|