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 ADDED
@@ -0,0 +1,108 @@
1
+ # Roguelike CLI - Installation Guide
2
+
3
+ ## Local Development Setup
4
+
5
+ 1. Install dependencies:
6
+ ```bash
7
+ cd cli
8
+ npm install
9
+ ```
10
+
11
+ 2. Build the project:
12
+ ```bash
13
+ npm run build
14
+ ```
15
+
16
+ 3. Link locally for testing:
17
+ ```bash
18
+ npm link
19
+ ```
20
+
21
+ This will make `rlc` command available globally.
22
+
23
+ 4. Test the installation:
24
+ ```bash
25
+ rlc
26
+ ```
27
+
28
+ ## First Run
29
+
30
+ Run `init` to configure:
31
+ ```bash
32
+ rlc
33
+ > init
34
+ ```
35
+
36
+ Or set API key manually:
37
+ ```bash
38
+ > config:apiKey=your-api-key-here
39
+ ```
40
+
41
+ ## How to Use
42
+
43
+ ### View config
44
+ ```bash
45
+ > config
46
+ Provider: claude
47
+ Model: claude-sonnet-4-20250514
48
+ API Key: sk-ant-a...1234
49
+ Storage: /Users/you/.rlc/notes
50
+ ```
51
+
52
+ ### Create schema or todo
53
+ ```bash
54
+ > todo opening company in delaware
55
+ > architecture kubernetes cluster with postgres
56
+ ```
57
+
58
+ ### Navigation
59
+ ```bash
60
+ > ls # list files (columns)
61
+ > cd my-project # go into folder
62
+ > cd .. # go back
63
+ > pwd # current path
64
+ > tree # show tree structure
65
+ > tree -A # show tree with files
66
+ > tree --depth=2 # limit depth
67
+ > open # open folder in Finder
68
+ > open my-folder # open specific folder
69
+ ```
70
+
71
+ ### Copy to clipboard
72
+ Add `| pbcopy` (macOS), `| copy` or `| clip` (Windows) to any command:
73
+ ```bash
74
+ > ls | pbcopy
75
+ > tree | pbcopy
76
+ > config | pbcopy
77
+ > pwd | pbcopy
78
+ ```
79
+
80
+ ### File operations
81
+ ```bash
82
+ > mkdir my-note # create folder
83
+ > cp source dest # copy
84
+ > mv source dest # move/rename
85
+ > rm file # delete file
86
+ > rm -rf folder # delete folder
87
+ ```
88
+
89
+ ## Uninstall
90
+
91
+ ```bash
92
+ npm unlink -g roguelike-cli
93
+ ```
94
+
95
+ ## Troubleshooting
96
+
97
+ If you get "command not found":
98
+ - Make sure `npm link` was run successfully
99
+ - Check that `~/.npm-global/bin` or similar is in your PATH
100
+
101
+ If API errors occur:
102
+ - Verify your API key is set: `config`
103
+ - Check that you have credits/access to Claude API
104
+
105
+ ## Website
106
+
107
+ https://www.rlc.rocks
108
+
package/bin/rlc ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+
3
+ require('../dist/index.js');
4
+
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateSchemaWithAI = generateSchemaWithAI;
7
+ const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
8
+ const SYSTEM_PROMPT = `You are a schema generator. Based on user input, generate EITHER:
9
+
10
+ 1. **BLOCK DIAGRAM** - when user mentions: "schema", "architecture", "infrastructure", "diagram", "system"
11
+ Use box-drawing to create visual blocks with connections:
12
+
13
+ Example:
14
+ \`\`\`
15
+ ┌─────────────────────────────────────────────────────────────┐
16
+ │ Kubernetes Cluster │
17
+ │ │
18
+ │ ┌──────────────────┐ ┌──────────────────┐ │
19
+ │ │ Control Plane │ │ Worker Nodes │ │
20
+ │ │ │◄────►│ │ │
21
+ │ │ - API Server │ │ - Node Pool 1 │ │
22
+ │ │ - Scheduler │ │ - Node Pool 2 │ │
23
+ │ │ - etcd │ │ - GPU Pool │ │
24
+ │ └────────┬─────────┘ └────────┬─────────┘ │
25
+ │ │ │ │
26
+ │ └──────────┬───────────────┘ │
27
+ │ │ │
28
+ │ ┌──────────────────┐│┌──────────────────┐ │
29
+ │ │ PostgreSQL │││ Redis │ │
30
+ │ └──────────────────┘│└──────────────────┘ │
31
+ └─────────────────────────────────────────────────────────────┘
32
+ \`\`\`
33
+
34
+ 2. **TREE STRUCTURE** - when user mentions: "todo", "tasks", "list", "steps", "plan"
35
+ Use tree format:
36
+
37
+ Example:
38
+ \`\`\`
39
+ ├── Phase 1: Setup
40
+ │ ├── Create repository
41
+ │ ├── Setup CI/CD
42
+ │ └── Configure environment
43
+ ├── Phase 2: Development
44
+ │ ├── Backend API
45
+ │ └── Frontend UI
46
+ └── Phase 3: Deploy
47
+ \`\`\`
48
+
49
+ Rules:
50
+ 1. Extract a short title for filename
51
+ 2. If user says "schema" or "architecture" - ALWAYS use BLOCK DIAGRAM format
52
+ 3. If user says "todo" or "tasks" - use TREE format
53
+ 4. Keep context from previous messages
54
+
55
+ Respond with JSON:
56
+ {
57
+ "title": "short-title",
58
+ "format": "block" or "tree",
59
+ "content": "the actual ASCII art schema here"
60
+ }`;
61
+ async function generateSchemaWithAI(input, config, signal, history) {
62
+ if (!config.apiKey) {
63
+ throw new Error('API key not set. Use config:apiKey=<key> to set it.');
64
+ }
65
+ const client = new sdk_1.default({
66
+ apiKey: config.apiKey,
67
+ });
68
+ // Build messages from history or just the current input
69
+ const messages = [];
70
+ if (history && history.length > 0) {
71
+ // Add previous messages for context
72
+ for (const msg of history.slice(0, -1)) { // exclude the last one (current input)
73
+ messages.push({
74
+ role: msg.role,
75
+ content: msg.role === 'assistant'
76
+ ? `Previous schema generated:\n${msg.content}`
77
+ : msg.content
78
+ });
79
+ }
80
+ }
81
+ // Add current user input
82
+ messages.push({
83
+ role: 'user',
84
+ content: input
85
+ });
86
+ try {
87
+ const model = config.model || 'claude-sonnet-4-20250514';
88
+ const message = await client.messages.create({
89
+ model: model,
90
+ max_tokens: 2000,
91
+ system: SYSTEM_PROMPT,
92
+ messages: messages,
93
+ });
94
+ const content = message.content[0];
95
+ if (content.type !== 'text') {
96
+ return null;
97
+ }
98
+ const text = content.text.trim();
99
+ let jsonMatch = text.match(/\{[\s\S]*\}/);
100
+ if (!jsonMatch) {
101
+ return null;
102
+ }
103
+ const parsed = JSON.parse(jsonMatch[0]);
104
+ // AI now returns ready content
105
+ const schemaContent = parsed.content || '';
106
+ return {
107
+ title: parsed.title || 'schema',
108
+ content: schemaContent,
109
+ };
110
+ }
111
+ catch (error) {
112
+ console.error('AI Error:', error.message);
113
+ return null;
114
+ }
115
+ }
@@ -0,0 +1,175 @@
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.initCommand = initCommand;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const os = __importStar(require("os"));
40
+ const config_1 = require("../config/config");
41
+ function question(query) {
42
+ return new Promise((resolve) => {
43
+ process.stdout.write(query);
44
+ let input = '';
45
+ const onData = (chunk) => {
46
+ const str = chunk.toString();
47
+ for (const char of str) {
48
+ if (char === '\n' || char === '\r') {
49
+ process.stdin.removeListener('data', onData);
50
+ process.stdout.write('\n');
51
+ resolve(input);
52
+ return;
53
+ }
54
+ else if (char === '\x7f' || char === '\b') {
55
+ // Backspace
56
+ if (input.length > 0) {
57
+ input = input.slice(0, -1);
58
+ process.stdout.write('\b \b');
59
+ }
60
+ }
61
+ else if (char >= ' ') {
62
+ input += char;
63
+ process.stdout.write(char);
64
+ }
65
+ }
66
+ };
67
+ process.stdin.on('data', onData);
68
+ });
69
+ }
70
+ // Recursive copy function
71
+ function copyRecursive(src, dest) {
72
+ const stat = fs.statSync(src);
73
+ if (stat.isDirectory()) {
74
+ if (!fs.existsSync(dest)) {
75
+ fs.mkdirSync(dest, { recursive: true });
76
+ }
77
+ const entries = fs.readdirSync(src);
78
+ for (const entry of entries) {
79
+ copyRecursive(path.join(src, entry), path.join(dest, entry));
80
+ }
81
+ }
82
+ else {
83
+ fs.copyFileSync(src, dest);
84
+ }
85
+ }
86
+ async function initCommand() {
87
+ console.log('\n╔═══════════════════════════════════════╗');
88
+ console.log('║ ROGUELIKE CLI INITIALIZATION WIZARD ║');
89
+ console.log('╚═══════════════════════════════════════╝\n');
90
+ // Get existing config if any
91
+ const existingConfig = await (0, config_1.initConfig)();
92
+ const oldStoragePath = existingConfig?.storagePath;
93
+ // 1. Root directory
94
+ const defaultRoot = path.join(os.homedir(), '.rlc', 'notes');
95
+ const rootDirAnswer = await question(`Root directory for notes [${defaultRoot}]: `);
96
+ const rootDir = rootDirAnswer.trim() || defaultRoot;
97
+ // Check if we need to migrate data
98
+ if (oldStoragePath && oldStoragePath !== rootDir && fs.existsSync(oldStoragePath)) {
99
+ const entries = fs.readdirSync(oldStoragePath);
100
+ if (entries.length > 0) {
101
+ const migrateAnswer = await question(`\nMigrate existing data from ${oldStoragePath}? [Y/n]: `);
102
+ if (migrateAnswer.toLowerCase() !== 'n') {
103
+ if (!fs.existsSync(rootDir)) {
104
+ fs.mkdirSync(rootDir, { recursive: true });
105
+ }
106
+ console.log(`Migrating data...`);
107
+ for (const entry of entries) {
108
+ const srcPath = path.join(oldStoragePath, entry);
109
+ const destPath = path.join(rootDir, entry);
110
+ copyRecursive(srcPath, destPath);
111
+ }
112
+ console.log(`Migrated ${entries.length} items to ${rootDir}`);
113
+ }
114
+ }
115
+ }
116
+ if (!fs.existsSync(rootDir)) {
117
+ fs.mkdirSync(rootDir, { recursive: true });
118
+ console.log(`Created directory: ${rootDir}`);
119
+ }
120
+ // 2. AI Provider selection
121
+ console.log('\nSelect AI Provider:');
122
+ console.log(' 1. Claude Sonnet 4.5');
123
+ console.log(' 2. Claude Opus 4.5');
124
+ console.log(' 3. GPT-4o (latest)');
125
+ console.log(' 4. Gemini 3 Pro');
126
+ console.log(' 5. Grok (latest)');
127
+ const aiChoice = await question('\nEnter choice [1-5] (default: 1): ');
128
+ const aiProviders = [
129
+ { name: 'claude', model: 'claude-sonnet-4-20250514', apiUrl: 'https://api.anthropic.com' },
130
+ { name: 'claude-opus', model: 'claude-opus-4-20250514', apiUrl: 'https://api.anthropic.com' },
131
+ { name: 'openai', model: 'gpt-4o', apiUrl: 'https://api.openai.com' },
132
+ { name: 'gemini', model: 'gemini-3-pro', apiUrl: 'https://generativelanguage.googleapis.com' },
133
+ { name: 'grok', model: 'grok-beta', apiUrl: 'https://api.x.ai' },
134
+ ];
135
+ const selectedIndex = parseInt(aiChoice.trim()) - 1 || 0;
136
+ const selectedProvider = aiProviders[selectedIndex] || aiProviders[0];
137
+ console.log(`Selected: ${selectedProvider.name} (${selectedProvider.model})`);
138
+ // 3. API Key - reuse existing if not provided
139
+ const existingApiKey = existingConfig?.apiKey || '';
140
+ const hasExistingKey = existingApiKey.length > 0;
141
+ const keyPrompt = hasExistingKey
142
+ ? `\nAPI key for ${selectedProvider.name} [press Enter to keep existing]: `
143
+ : `\nEnter API key for ${selectedProvider.name}: `;
144
+ const apiKeyInput = await question(keyPrompt);
145
+ const apiKey = apiKeyInput.trim() || existingApiKey;
146
+ if (!apiKey) {
147
+ console.log('Warning: API key not set. You can set it later with config:apiKey=<key>');
148
+ }
149
+ else if (apiKeyInput.trim()) {
150
+ console.log('API key saved');
151
+ }
152
+ else {
153
+ console.log('Using existing API key');
154
+ }
155
+ // Save config
156
+ const config = {
157
+ aiProvider: selectedProvider.name,
158
+ apiKey: apiKey,
159
+ apiUrl: selectedProvider.apiUrl,
160
+ storagePath: rootDir,
161
+ currentPath: rootDir,
162
+ model: selectedProvider.model,
163
+ };
164
+ (0, config_1.saveConfig)(config);
165
+ // Ensure storage directory exists
166
+ if (!fs.existsSync(rootDir)) {
167
+ fs.mkdirSync(rootDir, { recursive: true });
168
+ }
169
+ console.log('\n╔═══════════════════════════════════════╗');
170
+ console.log('║ INITIALIZATION COMPLETE ║');
171
+ console.log('╚═══════════════════════════════════════╝\n');
172
+ console.log(`Root directory: ${rootDir}`);
173
+ console.log(`AI Provider: ${selectedProvider.name}`);
174
+ console.log(`Model: ${selectedProvider.model}\n`);
175
+ }
@@ -0,0 +1,85 @@
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.initConfig = initConfig;
37
+ exports.saveConfig = saveConfig;
38
+ exports.getConfig = getConfig;
39
+ exports.updateConfig = updateConfig;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const os = __importStar(require("os"));
43
+ const CONFIG_FILE = path.join(os.homedir(), '.rlc', 'config.json');
44
+ const DEFAULT_STORAGE = path.join(os.homedir(), '.rlc', 'notes');
45
+ async function initConfig() {
46
+ const configDir = path.dirname(CONFIG_FILE);
47
+ if (!fs.existsSync(configDir)) {
48
+ fs.mkdirSync(configDir, { recursive: true });
49
+ }
50
+ if (!fs.existsSync(CONFIG_FILE)) {
51
+ return null;
52
+ }
53
+ const configData = fs.readFileSync(CONFIG_FILE, 'utf-8');
54
+ const config = JSON.parse(configData);
55
+ if (!fs.existsSync(config.storagePath)) {
56
+ fs.mkdirSync(config.storagePath, { recursive: true });
57
+ }
58
+ if (!config.currentPath) {
59
+ config.currentPath = config.storagePath;
60
+ }
61
+ if (!config.model) {
62
+ config.model = 'claude-sonnet-4-20250514';
63
+ }
64
+ return config;
65
+ }
66
+ function saveConfig(config) {
67
+ const configDir = path.dirname(CONFIG_FILE);
68
+ if (!fs.existsSync(configDir)) {
69
+ fs.mkdirSync(configDir, { recursive: true });
70
+ }
71
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
72
+ }
73
+ function getConfig() {
74
+ if (!fs.existsSync(CONFIG_FILE)) {
75
+ throw new Error('Config not initialized. Run rlc first.');
76
+ }
77
+ const configData = fs.readFileSync(CONFIG_FILE, 'utf-8');
78
+ return JSON.parse(configData);
79
+ }
80
+ function updateConfig(updates) {
81
+ const config = getConfig();
82
+ const updated = { ...config, ...updates };
83
+ saveConfig(updated);
84
+ return updated;
85
+ }
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const interactive_1 = require("./interactive");
5
+ const config_1 = require("./config/config");
6
+ const init_1 = require("./commands/init");
7
+ async function main() {
8
+ // Check for "rlc init" command line argument
9
+ const args = process.argv.slice(2);
10
+ if (args[0] === 'init') {
11
+ await (0, init_1.initCommand)();
12
+ return;
13
+ }
14
+ let config = await (0, config_1.initConfig)();
15
+ if (!config) {
16
+ console.log('\n╔═══════════════════════════════════════╗');
17
+ console.log('║ ROGUELIKE CLI NOT INITIALIZED ║');
18
+ console.log('╚═══════════════════════════════════════╝\n');
19
+ console.log('Running init wizard...\n');
20
+ await (0, init_1.initCommand)();
21
+ config = await (0, config_1.initConfig)();
22
+ if (!config) {
23
+ console.log('Initialization failed. Please try again.');
24
+ process.exit(1);
25
+ }
26
+ }
27
+ await (0, interactive_1.startInteractive)(config);
28
+ }
29
+ main().catch(console.error);