roguelike-cli 1.3.1 → 1.3.2
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 +126 -118
- package/dist/ai/claude.js +14 -4
- package/dist/commands/init.js +39 -6
- package/dist/config/config.js +32 -0
- package/dist/interactive/commands.js +80 -57
- package/dist/interactive/startup.js +6 -6
- package/package.json +1 -1
- package/src/ai/claude.ts +18 -4
- package/src/commands/init.ts +43 -7
- package/src/config/config.ts +34 -0
- package/src/interactive/commands.ts +87 -62
- package/src/interactive/startup.ts +6 -6
package/README.md
CHANGED
|
@@ -17,21 +17,18 @@
|
|
|
17
17
|
╚═════════════════════════╝
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
**Turn your backlog into a dungeon crawl.** Roguelike CLI is a terminal-based task manager that transforms productivity into an RPG experience. Complete quests, level up, and conquer your goals.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
## Features
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
- **RPG Engine** — Tasks are quests. Completing them earns XP, unlocks achievements, and levels you up.
|
|
25
|
+
- **AI Game Master** — Integrated AI helps decompose complex tasks and generates structured plans.
|
|
26
|
+
- **Local-First** — Your data stays on your machine in simple folders and JSON files.
|
|
27
|
+
- **Themeable** — Choose your adventure style: Fantasy, Space Opera, Star Wars, Cyberpunk, and more.
|
|
25
28
|
|
|
26
|
-
##
|
|
29
|
+
## Why Roguelike CLI?
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
- **Track progress** with XP, levels, and streaks
|
|
30
|
-
- **Earn achievements** for completing tasks
|
|
31
|
-
- **Mark milestones** as boss fights (3x XP)
|
|
32
|
-
- **Set deadlines** with human-readable dates
|
|
33
|
-
- **Generate visualizations** - trees, block diagrams, dungeon maps
|
|
34
|
-
- Let **AI help** you structure complex projects
|
|
31
|
+
Most task managers are boring. We make deep work addictive by applying proven RPG mechanics to your daily workflow.
|
|
35
32
|
|
|
36
33
|
## Install
|
|
37
34
|
|
|
@@ -40,40 +37,107 @@ npm i -g roguelike-cli
|
|
|
40
37
|
rlc
|
|
41
38
|
```
|
|
42
39
|
|
|
40
|
+
## Folder Structure
|
|
41
|
+
|
|
42
|
+
Every task is a folder. You can drop files directly into task folders — designs, docs, code, anything. Your file manager becomes your task manager.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
my-startup/
|
|
46
|
+
├── research/
|
|
47
|
+
│ ├── market-analysis/
|
|
48
|
+
│ │ └── competitors.xlsx <- attached file
|
|
49
|
+
│ └── user-interviews/
|
|
50
|
+
│ └── notes.md <- attached file
|
|
51
|
+
├── development/
|
|
52
|
+
│ ├── backend-api/
|
|
53
|
+
│ │ └── spec.yaml <- attached file
|
|
54
|
+
│ └── frontend-ui/
|
|
55
|
+
└── launch/
|
|
56
|
+
└── marketing/
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Navigate with `cd`, view with `tree`, open in Finder with `open`. It's just folders.
|
|
60
|
+
|
|
43
61
|
## Quick Start
|
|
44
62
|
|
|
45
63
|
```
|
|
46
|
-
> todo launch startup
|
|
64
|
+
> todo launch my startup
|
|
47
65
|
|
|
48
66
|
├── Research [BOSS]
|
|
49
67
|
│ ├── Market analysis
|
|
50
|
-
│ └── User interviews
|
|
68
|
+
│ └── User interviews [DUE: +7d]
|
|
51
69
|
├── Development
|
|
52
|
-
│ ├── Backend API [DUE:
|
|
70
|
+
│ ├── Backend API [DUE: Jan 20]
|
|
53
71
|
│ └── Frontend UI
|
|
54
72
|
└── Launch [MILESTONE]
|
|
55
|
-
└── Marketing campaign
|
|
56
73
|
|
|
57
|
-
[Type "save" to create
|
|
74
|
+
[Type "save" to create folders]
|
|
58
75
|
> save
|
|
59
|
-
Created
|
|
76
|
+
Created: launch-my-startup/
|
|
60
77
|
|
|
61
|
-
> cd launch-startup/research
|
|
78
|
+
> cd launch-my-startup/research
|
|
62
79
|
> done
|
|
63
80
|
|
|
64
|
-
===
|
|
81
|
+
=== QUEST COMPLETED ===
|
|
65
82
|
|
|
66
|
-
Tasks completed: 3
|
|
67
|
-
Bosses defeated: 1
|
|
68
83
|
+45 XP
|
|
69
|
-
|
|
70
84
|
*** LEVEL UP! ***
|
|
71
|
-
You are now level 2!
|
|
72
85
|
|
|
73
86
|
=== NEW ACHIEVEMENTS ===
|
|
74
|
-
[x] First Blood: Complete your first
|
|
75
|
-
[x] Boss Slayer:
|
|
87
|
+
[x] First Blood: Complete your first quest
|
|
88
|
+
[x] Boss Slayer: Defeat a boss
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Configuration
|
|
92
|
+
|
|
93
|
+
Run `init` to set up, or use `config` flags:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
> config
|
|
97
|
+
|
|
98
|
+
Provider: claude
|
|
99
|
+
Model: claude-sonnet-4-20250514
|
|
100
|
+
API Key: sk-ant-a...xxxx
|
|
101
|
+
Storage: /Users/you/.rlc/workspace
|
|
102
|
+
Theme: Fantasy RPG
|
|
103
|
+
Rules: Use fantasy RPG language...
|
|
104
|
+
|
|
105
|
+
Set with flags:
|
|
106
|
+
config -k=<key> Set API key
|
|
107
|
+
config -m=<model> Set model
|
|
108
|
+
config -t=<theme> Set theme
|
|
109
|
+
config -r="<rules>" Set custom rules
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Default Settings
|
|
113
|
+
|
|
114
|
+
| Setting | Default |
|
|
115
|
+
|---------|---------|
|
|
116
|
+
| Provider | Claude Sonnet 4.5 |
|
|
117
|
+
| Storage | `~/.rlc/workspace` |
|
|
118
|
+
| Theme | Default (no theme) |
|
|
119
|
+
|
|
120
|
+
## Themes (Rules)
|
|
121
|
+
|
|
122
|
+
Themes change how the AI speaks. Set with `config -t=<theme>` or during `init`.
|
|
123
|
+
|
|
124
|
+
| Theme | Style |
|
|
125
|
+
|-------|-------|
|
|
126
|
+
| `default` | Standard task manager language |
|
|
127
|
+
| `fantasy` | Quests, dungeons, dragons, loot |
|
|
128
|
+
| `space` | Missions, starships, commanders |
|
|
129
|
+
| `starwars` | Jedi, Force, Rebel Alliance |
|
|
130
|
+
| `western` | Bounties, sheriffs, frontier |
|
|
131
|
+
| `cyberpunk` | Gigs, netrunners, corps |
|
|
132
|
+
| `pirate` | Plunder, treasure, seven seas |
|
|
133
|
+
|
|
134
|
+
### Custom Rules
|
|
135
|
+
|
|
76
136
|
```
|
|
137
|
+
> config -r="Speak like a medieval knight. Tasks are 'duties'. Use 'huzzah' for success."
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Or select "Custom" during `init` to enter your own rules.
|
|
77
141
|
|
|
78
142
|
## Commands
|
|
79
143
|
|
|
@@ -84,24 +148,21 @@ You are now level 2!
|
|
|
84
148
|
| `ls` | List tasks (shows status) |
|
|
85
149
|
| `tree` | Task tree with deadlines |
|
|
86
150
|
| `tree -A` | Include files |
|
|
87
|
-
| `tree --depth=N` | Limit depth |
|
|
88
151
|
| `cd <task>` | Enter task |
|
|
89
|
-
| `..`, `...` | Go up
|
|
90
|
-
| `pwd` | Current path |
|
|
152
|
+
| `..`, `...` | Go up levels |
|
|
91
153
|
| `open` | Open in Finder |
|
|
92
154
|
|
|
93
155
|
### Task Management
|
|
94
156
|
|
|
95
157
|
| Command | Description |
|
|
96
158
|
|---------|-------------|
|
|
97
|
-
| `done` | Complete task (
|
|
98
|
-
| `undo` | Undo last done
|
|
159
|
+
| `done` | Complete task (earns XP) |
|
|
160
|
+
| `undo` | Undo last done |
|
|
99
161
|
| `deadline <date>` | Set deadline |
|
|
100
|
-
| `boss` | Toggle boss
|
|
101
|
-
| `block [node]` | Block by task
|
|
102
|
-
| `unblock` | Remove
|
|
103
|
-
| `
|
|
104
|
-
| `check` | Show overdue/upcoming deadlines |
|
|
162
|
+
| `boss` | Toggle boss (3x XP) |
|
|
163
|
+
| `block [node]` | Block by task |
|
|
164
|
+
| `unblock` | Remove block |
|
|
165
|
+
| `check` | Show deadlines |
|
|
105
166
|
|
|
106
167
|
### Gamification
|
|
107
168
|
|
|
@@ -109,77 +170,63 @@ You are now level 2!
|
|
|
109
170
|
|---------|-------------|
|
|
110
171
|
| `stats` | XP, level, streaks |
|
|
111
172
|
| `achievements` | Achievement list |
|
|
112
|
-
| `map` | Dungeon map
|
|
113
|
-
| `map --ai` | AI-generated
|
|
173
|
+
| `map` | Dungeon map |
|
|
174
|
+
| `map --ai` | AI-generated map |
|
|
114
175
|
|
|
115
176
|
### File Operations
|
|
116
177
|
|
|
117
178
|
| Command | Description |
|
|
118
179
|
|---------|-------------|
|
|
119
180
|
| `mkdir <name>` | Create task |
|
|
120
|
-
| `cp
|
|
121
|
-
| `mv <src> <dst>` | Move/rename |
|
|
122
|
-
| `rm <name>` | Delete file |
|
|
123
|
-
| `rm -rf <name>` | Delete folder |
|
|
181
|
+
| `cp`, `mv`, `rm` | Standard ops |
|
|
124
182
|
|
|
125
|
-
###
|
|
183
|
+
### Configuration
|
|
126
184
|
|
|
127
185
|
| Command | Description |
|
|
128
186
|
|---------|-------------|
|
|
129
|
-
|
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
187
|
+
| `init` | Setup wizard |
|
|
188
|
+
| `config` | View settings |
|
|
189
|
+
| `config -k=<key>` | Set API key |
|
|
190
|
+
| `config -m=<model>` | Set model |
|
|
191
|
+
| `config -t=<theme>` | Set theme |
|
|
192
|
+
| `config -r="<rules>"` | Custom rules |
|
|
132
193
|
|
|
133
194
|
## Deadlines
|
|
134
195
|
|
|
135
196
|
```
|
|
136
197
|
> deadline today # Due today
|
|
137
198
|
> deadline tomorrow # Due tomorrow
|
|
138
|
-
> deadline +3d #
|
|
139
|
-
> deadline Jan 15 #
|
|
199
|
+
> deadline +3d # In 3 days
|
|
200
|
+
> deadline Jan 15 # Specific date
|
|
140
201
|
```
|
|
141
202
|
|
|
142
203
|
Tree shows deadlines:
|
|
143
|
-
|
|
144
204
|
```
|
|
145
|
-
├── Backend
|
|
205
|
+
├── Backend/ [BOSS] [3d left]
|
|
146
206
|
│ ├── Database/ [DONE]
|
|
147
|
-
│ └──
|
|
207
|
+
│ └── API/ [OVERDUE 2d]
|
|
148
208
|
└── Frontend/ [tomorrow]
|
|
149
209
|
```
|
|
150
210
|
|
|
151
211
|
## XP System
|
|
152
212
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
|
158
|
-
|-------|-------------|
|
|
159
|
-
| 1 | 0 |
|
|
160
|
-
| 2 | 100 |
|
|
161
|
-
| 3 | 150 |
|
|
162
|
-
| 5 | 337 |
|
|
163
|
-
| 10 | 3,844 |
|
|
213
|
+
| Factor | XP |
|
|
214
|
+
|--------|-----|
|
|
215
|
+
| Base task | 10 |
|
|
216
|
+
| Per depth level | +5 |
|
|
217
|
+
| Boss multiplier | 3x |
|
|
164
218
|
|
|
165
219
|
## Achievements
|
|
166
220
|
|
|
167
|
-
| Achievement |
|
|
168
|
-
|
|
221
|
+
| Achievement | How to unlock |
|
|
222
|
+
|-------------|---------------|
|
|
169
223
|
| First Blood | Complete first task |
|
|
170
224
|
| Getting Started | Complete 10 tasks |
|
|
171
|
-
| Productive | Complete 50 tasks |
|
|
172
225
|
| Centurion | Complete 100 tasks |
|
|
173
|
-
| Deep Diver |
|
|
174
|
-
| Boss Slayer | Complete a boss
|
|
175
|
-
|
|
|
176
|
-
| Speedrunner | Complete task same day |
|
|
177
|
-
| On a Roll | 3 day streak |
|
|
226
|
+
| Deep Diver | Task at depth 5+ |
|
|
227
|
+
| Boss Slayer | Complete a boss |
|
|
228
|
+
| Speedrunner | Same-day completion |
|
|
178
229
|
| Streak Master | 7 day streak |
|
|
179
|
-
| Unstoppable | 30 day streak |
|
|
180
|
-
| Adventurer | Reach level 5 |
|
|
181
|
-
| Veteran | Reach level 10 |
|
|
182
|
-
| Legend | Reach level 25 |
|
|
183
230
|
|
|
184
231
|
## Dungeon Map
|
|
185
232
|
|
|
@@ -187,42 +234,12 @@ Tree shows deadlines:
|
|
|
187
234
|
> map
|
|
188
235
|
|
|
189
236
|
###########################################
|
|
190
|
-
#
|
|
191
|
-
# [Research]
|
|
192
|
-
#
|
|
193
|
-
# x
|
|
194
|
-
#
|
|
195
|
-
|
|
196
|
-
| |
|
|
197
|
-
##########+############+##################
|
|
198
|
-
# #
|
|
199
|
-
# [Launch] #
|
|
200
|
-
# * Marketing #
|
|
201
|
-
# @ SHIP IT! [BOSS] #
|
|
202
|
-
# #
|
|
203
|
-
###########################################
|
|
204
|
-
|
|
205
|
-
Legend: * Task x Done @ Boss ! Blocked + Door
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Use `map --ai` for creative AI-generated layouts.
|
|
209
|
-
|
|
210
|
-
## Block Diagrams
|
|
211
|
-
|
|
212
|
-
```
|
|
213
|
-
> schema kubernetes cluster
|
|
214
|
-
|
|
215
|
-
┌─────────────────────────────────────────────────────────────┐
|
|
216
|
-
│ Kubernetes Cluster │
|
|
217
|
-
│ │
|
|
218
|
-
│ ┌──────────────────┐ ┌──────────────────┐ │
|
|
219
|
-
│ │ Control Plane │ │ Worker Nodes │ │
|
|
220
|
-
│ └────────┬─────────┘ └────────┬─────────┘ │
|
|
221
|
-
│ └──────────┬───────────────┘ │
|
|
222
|
-
│ ┌──────────────────┐│┌──────────────────┐ │
|
|
223
|
-
│ │ PostgreSQL │││ Redis │ │
|
|
224
|
-
│ └──────────────────┘│└──────────────────┘ │
|
|
225
|
-
└─────────────────────────────────────────────────────────────┘
|
|
237
|
+
# # #
|
|
238
|
+
# [Research] # [Development] #
|
|
239
|
+
# x Analysis +---* Backend #
|
|
240
|
+
# x Interviews # @ Deploy BOSS #
|
|
241
|
+
# # #
|
|
242
|
+
##########+###########+###################
|
|
226
243
|
```
|
|
227
244
|
|
|
228
245
|
## Clipboard
|
|
@@ -230,15 +247,6 @@ Use `map --ai` for creative AI-generated layouts.
|
|
|
230
247
|
```
|
|
231
248
|
> tree | pbcopy # macOS
|
|
232
249
|
> tree | clip # Windows
|
|
233
|
-
> ls | copy # Alternative
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
## Configuration
|
|
237
|
-
|
|
238
|
-
```
|
|
239
|
-
> init # Setup wizard
|
|
240
|
-
> config # Show settings
|
|
241
|
-
> config:apiKey=sk-... # Set API key
|
|
242
250
|
```
|
|
243
251
|
|
|
244
252
|
## Website
|
package/dist/ai/claude.js
CHANGED
|
@@ -46,7 +46,7 @@ Create a creative, interesting dungeon layout for the given tasks.
|
|
|
46
46
|
Output ONLY the ASCII map, no JSON wrapper.`;
|
|
47
47
|
async function generateSchemaWithAI(input, config, signal, history) {
|
|
48
48
|
if (!config.apiKey) {
|
|
49
|
-
throw new Error('API key not set. Use config
|
|
49
|
+
throw new Error('API key not set. Use config -k=<key> to set it.');
|
|
50
50
|
}
|
|
51
51
|
const client = new sdk_1.default({
|
|
52
52
|
apiKey: config.apiKey,
|
|
@@ -68,10 +68,15 @@ async function generateSchemaWithAI(input, config, signal, history) {
|
|
|
68
68
|
});
|
|
69
69
|
try {
|
|
70
70
|
const model = config.model || 'claude-sonnet-4-20250514';
|
|
71
|
+
// Build system prompt with custom rules
|
|
72
|
+
let systemPrompt = SYSTEM_PROMPT;
|
|
73
|
+
if (config.rules) {
|
|
74
|
+
systemPrompt += '\n\nADDITIONAL STYLE RULES (apply to all responses):\n' + config.rules;
|
|
75
|
+
}
|
|
71
76
|
const message = await client.messages.create({
|
|
72
77
|
model: model,
|
|
73
78
|
max_tokens: 2000,
|
|
74
|
-
system:
|
|
79
|
+
system: systemPrompt,
|
|
75
80
|
messages: messages,
|
|
76
81
|
});
|
|
77
82
|
const content = message.content[0];
|
|
@@ -98,17 +103,22 @@ async function generateSchemaWithAI(input, config, signal, history) {
|
|
|
98
103
|
}
|
|
99
104
|
async function generateDungeonMapWithAI(treeContent, config, signal) {
|
|
100
105
|
if (!config.apiKey) {
|
|
101
|
-
throw new Error('API key not set. Use config
|
|
106
|
+
throw new Error('API key not set. Use config -k=<key> to set it.');
|
|
102
107
|
}
|
|
103
108
|
const client = new sdk_1.default({
|
|
104
109
|
apiKey: config.apiKey,
|
|
105
110
|
});
|
|
106
111
|
try {
|
|
107
112
|
const model = config.model || 'claude-sonnet-4-20250514';
|
|
113
|
+
// Build system prompt with custom rules
|
|
114
|
+
let systemPrompt = DUNGEON_MAP_PROMPT;
|
|
115
|
+
if (config.rules) {
|
|
116
|
+
systemPrompt += '\n\nADDITIONAL STYLE RULES:\n' + config.rules;
|
|
117
|
+
}
|
|
108
118
|
const message = await client.messages.create({
|
|
109
119
|
model: model,
|
|
110
120
|
max_tokens: 2000,
|
|
111
|
-
system:
|
|
121
|
+
system: systemPrompt,
|
|
112
122
|
messages: [{
|
|
113
123
|
role: 'user',
|
|
114
124
|
content: 'Generate a dungeon map for this task tree:\n\n' + treeContent
|
package/dist/commands/init.js
CHANGED
|
@@ -63,7 +63,6 @@ function copyRecursive(src, dest) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
async function initCommand(existingRl) {
|
|
66
|
-
// Create our own readline if not provided, or use existing one
|
|
67
66
|
const rl = existingRl || readline.createInterface({
|
|
68
67
|
input: process.stdin,
|
|
69
68
|
output: process.stdout,
|
|
@@ -73,7 +72,6 @@ async function initCommand(existingRl) {
|
|
|
73
72
|
console.log('\n╔═══════════════════════════════════════╗');
|
|
74
73
|
console.log('║ ROGUELIKE CLI INITIALIZATION WIZARD ║');
|
|
75
74
|
console.log('╚═══════════════════════════════════════╝\n');
|
|
76
|
-
// Get existing config if any
|
|
77
75
|
const existingConfig = await (0, config_1.initConfig)();
|
|
78
76
|
const oldStoragePath = existingConfig?.storagePath;
|
|
79
77
|
// 1. Root directory
|
|
@@ -121,7 +119,7 @@ async function initCommand(existingRl) {
|
|
|
121
119
|
const selectedIndex = parseInt(aiChoice.trim()) - 1 || 0;
|
|
122
120
|
const selectedProvider = aiProviders[selectedIndex] || aiProviders[0];
|
|
123
121
|
console.log(`Selected: ${selectedProvider.name} (${selectedProvider.model})`);
|
|
124
|
-
// 3. API Key
|
|
122
|
+
// 3. API Key
|
|
125
123
|
const existingApiKey = existingConfig?.apiKey || '';
|
|
126
124
|
const hasExistingKey = existingApiKey.length > 0;
|
|
127
125
|
const keyPrompt = hasExistingKey
|
|
@@ -130,7 +128,7 @@ async function initCommand(existingRl) {
|
|
|
130
128
|
const apiKeyInput = await question(rl, keyPrompt);
|
|
131
129
|
const apiKey = apiKeyInput.trim() || existingApiKey;
|
|
132
130
|
if (!apiKey) {
|
|
133
|
-
console.log('Warning: API key not set. You can set it later with config
|
|
131
|
+
console.log('Warning: API key not set. You can set it later with: config -k <key>');
|
|
134
132
|
}
|
|
135
133
|
else if (apiKeyInput.trim()) {
|
|
136
134
|
console.log('API key saved');
|
|
@@ -138,6 +136,39 @@ async function initCommand(existingRl) {
|
|
|
138
136
|
else {
|
|
139
137
|
console.log('Using existing API key');
|
|
140
138
|
}
|
|
139
|
+
// 4. Theme/Rules selection
|
|
140
|
+
console.log('\nSelect AI Theme (affects language style):');
|
|
141
|
+
const presetKeys = Object.keys(config_1.RULES_PRESETS);
|
|
142
|
+
presetKeys.forEach((key, index) => {
|
|
143
|
+
console.log(` ${index + 1}. ${config_1.RULES_PRESETS[key].name}`);
|
|
144
|
+
});
|
|
145
|
+
console.log(` ${presetKeys.length + 1}. Custom (enter your own rules)`);
|
|
146
|
+
const existingPreset = existingConfig?.rulesPreset || 'default';
|
|
147
|
+
const defaultPresetIndex = presetKeys.indexOf(existingPreset) + 1 || 1;
|
|
148
|
+
const themeChoice = await question(rl, `\nEnter choice [1-${presetKeys.length + 1}] (default: ${defaultPresetIndex}): `);
|
|
149
|
+
const themeIndex = parseInt(themeChoice.trim()) - 1;
|
|
150
|
+
let selectedRules = '';
|
|
151
|
+
let selectedPreset = 'default';
|
|
152
|
+
if (themeIndex >= 0 && themeIndex < presetKeys.length) {
|
|
153
|
+
selectedPreset = presetKeys[themeIndex];
|
|
154
|
+
selectedRules = config_1.RULES_PRESETS[selectedPreset].rules;
|
|
155
|
+
console.log(`Selected: ${config_1.RULES_PRESETS[selectedPreset].name}`);
|
|
156
|
+
}
|
|
157
|
+
else if (themeIndex === presetKeys.length) {
|
|
158
|
+
// Custom rules
|
|
159
|
+
console.log('\nEnter your custom rules for AI (how it should speak, what terms to use):');
|
|
160
|
+
console.log('Example: "Use pirate language. Tasks are treasure hunts. Be playful."');
|
|
161
|
+
const customRules = await question(rl, '\nYour rules: ');
|
|
162
|
+
selectedRules = customRules.trim();
|
|
163
|
+
selectedPreset = 'custom';
|
|
164
|
+
console.log('Custom rules saved');
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
// Default
|
|
168
|
+
selectedPreset = existingConfig?.rulesPreset || 'default';
|
|
169
|
+
selectedRules = existingConfig?.rules || '';
|
|
170
|
+
console.log(`Keeping: ${config_1.RULES_PRESETS[selectedPreset]?.name || 'Default'}`);
|
|
171
|
+
}
|
|
141
172
|
// Save config
|
|
142
173
|
const config = {
|
|
143
174
|
aiProvider: selectedProvider.name,
|
|
@@ -146,9 +177,10 @@ async function initCommand(existingRl) {
|
|
|
146
177
|
storagePath: rootDir,
|
|
147
178
|
currentPath: rootDir,
|
|
148
179
|
model: selectedProvider.model,
|
|
180
|
+
rules: selectedRules,
|
|
181
|
+
rulesPreset: selectedPreset,
|
|
149
182
|
};
|
|
150
183
|
(0, config_1.saveConfig)(config);
|
|
151
|
-
// Ensure storage directory exists
|
|
152
184
|
if (!fs.existsSync(rootDir)) {
|
|
153
185
|
fs.mkdirSync(rootDir, { recursive: true });
|
|
154
186
|
}
|
|
@@ -157,7 +189,8 @@ async function initCommand(existingRl) {
|
|
|
157
189
|
console.log('╚═══════════════════════════════════════╝\n');
|
|
158
190
|
console.log(`Root directory: ${rootDir}`);
|
|
159
191
|
console.log(`AI Provider: ${selectedProvider.name}`);
|
|
160
|
-
console.log(`Model: ${selectedProvider.model}
|
|
192
|
+
console.log(`Model: ${selectedProvider.model}`);
|
|
193
|
+
console.log(`Theme: ${config_1.RULES_PRESETS[selectedPreset]?.name || 'Custom'}\n`);
|
|
161
194
|
}
|
|
162
195
|
finally {
|
|
163
196
|
if (shouldCloseRl) {
|
package/dist/config/config.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.RULES_PRESETS = void 0;
|
|
36
37
|
exports.initConfig = initConfig;
|
|
37
38
|
exports.saveConfig = saveConfig;
|
|
38
39
|
exports.getConfig = getConfig;
|
|
@@ -40,6 +41,37 @@ exports.updateConfig = updateConfig;
|
|
|
40
41
|
const fs = __importStar(require("fs"));
|
|
41
42
|
const path = __importStar(require("path"));
|
|
42
43
|
const os = __importStar(require("os"));
|
|
44
|
+
// Preset rules for different themes
|
|
45
|
+
exports.RULES_PRESETS = {
|
|
46
|
+
default: {
|
|
47
|
+
name: 'Default (No theme)',
|
|
48
|
+
rules: '',
|
|
49
|
+
},
|
|
50
|
+
fantasy: {
|
|
51
|
+
name: 'Fantasy RPG',
|
|
52
|
+
rules: 'Use fantasy RPG language. Tasks are "quests", completing them is "slaying". Major milestones are "boss battles". Use terms like "adventurer", "dungeon", "loot", "guild". Add flavor text with swords, dragons, magic.',
|
|
53
|
+
},
|
|
54
|
+
space: {
|
|
55
|
+
name: 'Space Opera',
|
|
56
|
+
rules: 'Use sci-fi space language. Tasks are "missions", completing them is "mission accomplished". Major milestones are "final frontier". Use terms like "commander", "starship", "coordinates", "hyperdrive". Add flavor with stars, planets, aliens.',
|
|
57
|
+
},
|
|
58
|
+
starwars: {
|
|
59
|
+
name: 'Star Wars',
|
|
60
|
+
rules: 'Use Star Wars language. Tasks are "missions from the Rebel Alliance". Completing is "defeating the Empire". Milestones are "destroying the Death Star". Use "Jedi", "Force", "Padawan", "Master". May the Force be with you.',
|
|
61
|
+
},
|
|
62
|
+
western: {
|
|
63
|
+
name: 'Wild West',
|
|
64
|
+
rules: 'Use Wild West language. Tasks are "bounties", completing them is "collecting the reward". Milestones are "showdowns". Use terms like "sheriff", "outlaw", "saloon", "frontier", "partner". Add dusty trails and tumbleweeds.',
|
|
65
|
+
},
|
|
66
|
+
cyberpunk: {
|
|
67
|
+
name: 'Cyberpunk',
|
|
68
|
+
rules: 'Use cyberpunk language. Tasks are "gigs", completing them is "flatlined". Milestones are "megacorp takedowns". Use terms like "netrunner", "chrome", "corpo", "edgerunner", "eddies". Add neon and rain.',
|
|
69
|
+
},
|
|
70
|
+
pirate: {
|
|
71
|
+
name: 'Pirate',
|
|
72
|
+
rules: 'Use pirate language. Tasks are "plunder", completing them is "claiming the treasure". Milestones are "capturing the flagship". Use "captain", "crew", "booty", "seven seas", "landlubber". Arr matey!',
|
|
73
|
+
},
|
|
74
|
+
};
|
|
43
75
|
const CONFIG_FILE = path.join(os.homedir(), '.rlc', 'config.json');
|
|
44
76
|
const DEFAULT_STORAGE = path.join(os.homedir(), '.rlc', 'workspace');
|
|
45
77
|
async function initConfig() {
|
|
@@ -931,36 +931,67 @@ async function processCommand(input, currentPath, config, signal, rl) {
|
|
|
931
931
|
return wrapResult({ output: currentPath });
|
|
932
932
|
}
|
|
933
933
|
if (command === 'config') {
|
|
934
|
+
const { updateConfig, RULES_PRESETS } = await Promise.resolve().then(() => __importStar(require('../config/config')));
|
|
935
|
+
// Check for flags
|
|
936
|
+
const keyFlag = parts.find(p => p.startsWith('-k=') || p.startsWith('--key='));
|
|
937
|
+
const modelFlag = parts.find(p => p.startsWith('-m=') || p.startsWith('--model='));
|
|
938
|
+
const rulesFlag = parts.find(p => p.startsWith('-r=') || p.startsWith('--rules='));
|
|
939
|
+
const themeFlag = parts.find(p => p.startsWith('-t=') || p.startsWith('--theme='));
|
|
940
|
+
if (keyFlag) {
|
|
941
|
+
const value = keyFlag.split('=').slice(1).join('=');
|
|
942
|
+
updateConfig({ apiKey: value });
|
|
943
|
+
return wrapResult({ output: 'API key updated.' });
|
|
944
|
+
}
|
|
945
|
+
if (modelFlag) {
|
|
946
|
+
const value = modelFlag.split('=').slice(1).join('=');
|
|
947
|
+
updateConfig({ model: value });
|
|
948
|
+
return wrapResult({ output: `Model updated: ${value}` });
|
|
949
|
+
}
|
|
950
|
+
if (rulesFlag) {
|
|
951
|
+
const value = rulesFlag.split('=').slice(1).join('=');
|
|
952
|
+
updateConfig({ rules: value, rulesPreset: 'custom' });
|
|
953
|
+
return wrapResult({ output: 'Custom rules updated.' });
|
|
954
|
+
}
|
|
955
|
+
if (themeFlag) {
|
|
956
|
+
const value = themeFlag.split('=').slice(1).join('=').toLowerCase();
|
|
957
|
+
if (RULES_PRESETS[value]) {
|
|
958
|
+
updateConfig({
|
|
959
|
+
rules: RULES_PRESETS[value].rules,
|
|
960
|
+
rulesPreset: value
|
|
961
|
+
});
|
|
962
|
+
return wrapResult({ output: `Theme updated: ${RULES_PRESETS[value].name}` });
|
|
963
|
+
}
|
|
964
|
+
else {
|
|
965
|
+
const themes = Object.keys(RULES_PRESETS).join(', ');
|
|
966
|
+
return wrapResult({ output: `Unknown theme. Available: ${themes}` });
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
// Show config
|
|
934
970
|
const maskedKey = config.apiKey
|
|
935
971
|
? config.apiKey.slice(0, 8) + '...' + config.apiKey.slice(-4)
|
|
936
972
|
: '(not set)';
|
|
973
|
+
const themeName = config.rulesPreset
|
|
974
|
+
? (RULES_PRESETS[config.rulesPreset]?.name || 'Custom')
|
|
975
|
+
: 'Default';
|
|
976
|
+
const rulesPreview = config.rules
|
|
977
|
+
? (config.rules.length > 50 ? config.rules.substring(0, 50) + '...' : config.rules)
|
|
978
|
+
: '(none)';
|
|
937
979
|
const output = `
|
|
938
980
|
Provider: ${config.aiProvider}
|
|
939
981
|
Model: ${config.model || '(default)'}
|
|
940
982
|
API Key: ${maskedKey}
|
|
941
983
|
Storage: ${config.storagePath}
|
|
984
|
+
Theme: ${themeName}
|
|
985
|
+
Rules: ${rulesPreview}
|
|
986
|
+
|
|
987
|
+
Set with flags:
|
|
988
|
+
config -k=<key> Set API key
|
|
989
|
+
config -m=<model> Set model
|
|
990
|
+
config -t=<theme> Set theme (fantasy, space, starwars, western, cyberpunk, pirate)
|
|
991
|
+
config -r="<rules>" Set custom rules
|
|
942
992
|
`.trim();
|
|
943
993
|
return wrapResult({ output });
|
|
944
994
|
}
|
|
945
|
-
if (command.startsWith('config:')) {
|
|
946
|
-
const configParts = input.split(':').slice(1).join(':').trim().split('=');
|
|
947
|
-
if (configParts.length !== 2) {
|
|
948
|
-
return { output: 'Usage: config:key=value' };
|
|
949
|
-
}
|
|
950
|
-
const key = configParts[0].trim();
|
|
951
|
-
const value = configParts[1].trim();
|
|
952
|
-
if (key === 'apiKey') {
|
|
953
|
-
const { updateConfig } = await Promise.resolve().then(() => __importStar(require('../config/config')));
|
|
954
|
-
updateConfig({ apiKey: value });
|
|
955
|
-
return { output: 'API key updated.' };
|
|
956
|
-
}
|
|
957
|
-
if (key === 'storagePath') {
|
|
958
|
-
const { updateConfig } = await Promise.resolve().then(() => __importStar(require('../config/config')));
|
|
959
|
-
updateConfig({ storagePath: value, currentPath: value });
|
|
960
|
-
return { output: `Storage path updated to: ${value}` };
|
|
961
|
-
}
|
|
962
|
-
return { output: `Unknown config key: ${key}` };
|
|
963
|
-
}
|
|
964
995
|
if (command === 'help') {
|
|
965
996
|
return wrapResult({
|
|
966
997
|
output: `
|
|
@@ -968,9 +999,7 @@ Storage: ${config.storagePath}
|
|
|
968
999
|
|
|
969
1000
|
Navigation:
|
|
970
1001
|
ls List tasks and files
|
|
971
|
-
tree
|
|
972
|
-
tree -A Include files
|
|
973
|
-
tree --depth=N Limit tree depth
|
|
1002
|
+
tree [-A] [--depth=N] Show task tree
|
|
974
1003
|
cd <task> Navigate into task
|
|
975
1004
|
cd .., ... Go back 1 or 2 levels
|
|
976
1005
|
pwd Show current path
|
|
@@ -978,49 +1007,43 @@ Navigation:
|
|
|
978
1007
|
|
|
979
1008
|
Task Management:
|
|
980
1009
|
mkdir <name> Create new task
|
|
981
|
-
done
|
|
982
|
-
undo Undo last done
|
|
983
|
-
deadline <date> Set deadline (today,
|
|
984
|
-
boss Toggle boss
|
|
985
|
-
block [node] Block by task
|
|
986
|
-
unblock Remove
|
|
987
|
-
status
|
|
988
|
-
check
|
|
989
|
-
|
|
990
|
-
File Operations:
|
|
991
|
-
cp <src> <dest> Copy file or folder
|
|
992
|
-
mv <src> <dest> Move/rename
|
|
993
|
-
rm <name> Delete file
|
|
994
|
-
rm -rf <name> Delete folder
|
|
1010
|
+
done Complete task (earns XP)
|
|
1011
|
+
undo Undo last done
|
|
1012
|
+
deadline <date> Set deadline (today, +3d, Jan 15)
|
|
1013
|
+
boss Toggle boss status (3x XP)
|
|
1014
|
+
block [node] Block by task or text
|
|
1015
|
+
unblock Remove block
|
|
1016
|
+
status Task details
|
|
1017
|
+
check Upcoming deadlines
|
|
995
1018
|
|
|
996
1019
|
Gamification:
|
|
997
|
-
stats
|
|
998
|
-
achievements
|
|
999
|
-
map
|
|
1000
|
-
map --ai AI-generated dungeon map
|
|
1020
|
+
stats XP, level, streaks
|
|
1021
|
+
achievements Achievement list
|
|
1022
|
+
map [--ai] Dungeon map view
|
|
1001
1023
|
|
|
1002
|
-
|
|
1003
|
-
<description> AI generates todo/schema preview
|
|
1004
|
-
save Save pending schema
|
|
1005
|
-
cancel Discard pending schema
|
|
1006
|
-
|
|
1007
|
-
Utility:
|
|
1024
|
+
Configuration:
|
|
1008
1025
|
init Setup wizard
|
|
1009
1026
|
config Show settings
|
|
1027
|
+
config -k=<key> Set API key
|
|
1028
|
+
config -m=<model> Set model
|
|
1029
|
+
config -t=<theme> Set theme (fantasy, space, starwars, etc)
|
|
1030
|
+
config -r="<rules>" Custom AI rules
|
|
1031
|
+
|
|
1032
|
+
Themes:
|
|
1033
|
+
default, fantasy, space, starwars, western, cyberpunk, pirate
|
|
1034
|
+
|
|
1035
|
+
File Operations:
|
|
1036
|
+
cp, mv, rm [-rf] Standard file operations
|
|
1010
1037
|
clean --yes Clear current folder
|
|
1011
|
-
v, version Show version
|
|
1012
|
-
help This help
|
|
1013
|
-
exit, quit Exit
|
|
1014
1038
|
|
|
1015
|
-
|
|
1016
|
-
<
|
|
1017
|
-
|
|
1039
|
+
AI Generation:
|
|
1040
|
+
<description> AI generates preview
|
|
1041
|
+
save Save to folders/file
|
|
1042
|
+
cancel Discard
|
|
1018
1043
|
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
deadline +3d Due in 3 days
|
|
1023
|
-
check See all upcoming deadlines
|
|
1044
|
+
Clipboard:
|
|
1045
|
+
<cmd> | pbcopy Copy to clipboard (macOS)
|
|
1046
|
+
<cmd> | clip Copy to clipboard (Windows)
|
|
1024
1047
|
|
|
1025
1048
|
www.rlc.rocks
|
|
1026
1049
|
`.trim()
|
|
@@ -19,16 +19,16 @@ const ASCII_ART = [
|
|
|
19
19
|
'║ Roguelike CLI ║',
|
|
20
20
|
'╚═════════════════════════╝',
|
|
21
21
|
'',
|
|
22
|
-
' Navigation: ls, cd, tree, pwd, open',
|
|
23
22
|
' Tasks: done, undo, deadline, boss, block',
|
|
24
|
-
'
|
|
23
|
+
' Stats: stats, achievements, map, check',
|
|
24
|
+
' Config: init, config -t=<theme>',
|
|
25
25
|
'',
|
|
26
|
-
'
|
|
27
|
-
' Workflow: <description> -> refine -> save',
|
|
26
|
+
' Themes: fantasy, space, starwars, cyberpunk',
|
|
28
27
|
'',
|
|
29
|
-
'
|
|
28
|
+
' TAB autocomplete, | pbcopy to copy',
|
|
29
|
+
' <description> -> refine -> save',
|
|
30
30
|
'',
|
|
31
|
-
' www.rlc.rocks',
|
|
31
|
+
' help - commands, www.rlc.rocks',
|
|
32
32
|
'',
|
|
33
33
|
' Ready...',
|
|
34
34
|
'',
|
package/package.json
CHANGED
package/src/ai/claude.ts
CHANGED
|
@@ -60,7 +60,7 @@ export async function generateSchemaWithAI(
|
|
|
60
60
|
history?: ConversationMessage[]
|
|
61
61
|
): Promise<GeneratedSchema | null> {
|
|
62
62
|
if (!config.apiKey) {
|
|
63
|
-
throw new Error('API key not set. Use config
|
|
63
|
+
throw new Error('API key not set. Use config -k=<key> to set it.');
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
const client = new Anthropic({
|
|
@@ -87,10 +87,17 @@ export async function generateSchemaWithAI(
|
|
|
87
87
|
|
|
88
88
|
try {
|
|
89
89
|
const model = config.model || 'claude-sonnet-4-20250514';
|
|
90
|
+
|
|
91
|
+
// Build system prompt with custom rules
|
|
92
|
+
let systemPrompt = SYSTEM_PROMPT;
|
|
93
|
+
if (config.rules) {
|
|
94
|
+
systemPrompt += '\n\nADDITIONAL STYLE RULES (apply to all responses):\n' + config.rules;
|
|
95
|
+
}
|
|
96
|
+
|
|
90
97
|
const message = await client.messages.create({
|
|
91
98
|
model: model,
|
|
92
99
|
max_tokens: 2000,
|
|
93
|
-
system:
|
|
100
|
+
system: systemPrompt,
|
|
94
101
|
messages: messages,
|
|
95
102
|
});
|
|
96
103
|
|
|
@@ -126,7 +133,7 @@ export async function generateDungeonMapWithAI(
|
|
|
126
133
|
signal?: AbortSignal
|
|
127
134
|
): Promise<string | null> {
|
|
128
135
|
if (!config.apiKey) {
|
|
129
|
-
throw new Error('API key not set. Use config
|
|
136
|
+
throw new Error('API key not set. Use config -k=<key> to set it.');
|
|
130
137
|
}
|
|
131
138
|
|
|
132
139
|
const client = new Anthropic({
|
|
@@ -135,10 +142,17 @@ export async function generateDungeonMapWithAI(
|
|
|
135
142
|
|
|
136
143
|
try {
|
|
137
144
|
const model = config.model || 'claude-sonnet-4-20250514';
|
|
145
|
+
|
|
146
|
+
// Build system prompt with custom rules
|
|
147
|
+
let systemPrompt = DUNGEON_MAP_PROMPT;
|
|
148
|
+
if (config.rules) {
|
|
149
|
+
systemPrompt += '\n\nADDITIONAL STYLE RULES:\n' + config.rules;
|
|
150
|
+
}
|
|
151
|
+
|
|
138
152
|
const message = await client.messages.create({
|
|
139
153
|
model: model,
|
|
140
154
|
max_tokens: 2000,
|
|
141
|
-
system:
|
|
155
|
+
system: systemPrompt,
|
|
142
156
|
messages: [{
|
|
143
157
|
role: 'user',
|
|
144
158
|
content: 'Generate a dungeon map for this task tree:\n\n' + treeContent
|
package/src/commands/init.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
import * as readline from 'readline';
|
|
5
|
-
import { Config, saveConfig, initConfig } from '../config/config';
|
|
5
|
+
import { Config, saveConfig, initConfig, RULES_PRESETS } from '../config/config';
|
|
6
6
|
|
|
7
7
|
function question(rl: readline.Interface, query: string): Promise<string> {
|
|
8
8
|
return new Promise((resolve) => {
|
|
@@ -31,7 +31,6 @@ function copyRecursive(src: string, dest: string): void {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export async function initCommand(existingRl?: readline.Interface): Promise<void> {
|
|
34
|
-
// Create our own readline if not provided, or use existing one
|
|
35
34
|
const rl = existingRl || readline.createInterface({
|
|
36
35
|
input: process.stdin,
|
|
37
36
|
output: process.stdout,
|
|
@@ -44,7 +43,6 @@ export async function initCommand(existingRl?: readline.Interface): Promise<void
|
|
|
44
43
|
console.log('║ ROGUELIKE CLI INITIALIZATION WIZARD ║');
|
|
45
44
|
console.log('╚═══════════════════════════════════════╝\n');
|
|
46
45
|
|
|
47
|
-
// Get existing config if any
|
|
48
46
|
const existingConfig = await initConfig();
|
|
49
47
|
const oldStoragePath = existingConfig?.storagePath;
|
|
50
48
|
|
|
@@ -101,7 +99,7 @@ export async function initCommand(existingRl?: readline.Interface): Promise<void
|
|
|
101
99
|
|
|
102
100
|
console.log(`Selected: ${selectedProvider.name} (${selectedProvider.model})`);
|
|
103
101
|
|
|
104
|
-
// 3. API Key
|
|
102
|
+
// 3. API Key
|
|
105
103
|
const existingApiKey = existingConfig?.apiKey || '';
|
|
106
104
|
const hasExistingKey = existingApiKey.length > 0;
|
|
107
105
|
const keyPrompt = hasExistingKey
|
|
@@ -112,13 +110,49 @@ export async function initCommand(existingRl?: readline.Interface): Promise<void
|
|
|
112
110
|
const apiKey = apiKeyInput.trim() || existingApiKey;
|
|
113
111
|
|
|
114
112
|
if (!apiKey) {
|
|
115
|
-
console.log('Warning: API key not set. You can set it later with config
|
|
113
|
+
console.log('Warning: API key not set. You can set it later with: config -k <key>');
|
|
116
114
|
} else if (apiKeyInput.trim()) {
|
|
117
115
|
console.log('API key saved');
|
|
118
116
|
} else {
|
|
119
117
|
console.log('Using existing API key');
|
|
120
118
|
}
|
|
121
119
|
|
|
120
|
+
// 4. Theme/Rules selection
|
|
121
|
+
console.log('\nSelect AI Theme (affects language style):');
|
|
122
|
+
const presetKeys = Object.keys(RULES_PRESETS);
|
|
123
|
+
presetKeys.forEach((key, index) => {
|
|
124
|
+
console.log(` ${index + 1}. ${RULES_PRESETS[key].name}`);
|
|
125
|
+
});
|
|
126
|
+
console.log(` ${presetKeys.length + 1}. Custom (enter your own rules)`);
|
|
127
|
+
|
|
128
|
+
const existingPreset = existingConfig?.rulesPreset || 'default';
|
|
129
|
+
const defaultPresetIndex = presetKeys.indexOf(existingPreset) + 1 || 1;
|
|
130
|
+
|
|
131
|
+
const themeChoice = await question(rl, `\nEnter choice [1-${presetKeys.length + 1}] (default: ${defaultPresetIndex}): `);
|
|
132
|
+
const themeIndex = parseInt(themeChoice.trim()) - 1;
|
|
133
|
+
|
|
134
|
+
let selectedRules = '';
|
|
135
|
+
let selectedPreset = 'default';
|
|
136
|
+
|
|
137
|
+
if (themeIndex >= 0 && themeIndex < presetKeys.length) {
|
|
138
|
+
selectedPreset = presetKeys[themeIndex];
|
|
139
|
+
selectedRules = RULES_PRESETS[selectedPreset].rules;
|
|
140
|
+
console.log(`Selected: ${RULES_PRESETS[selectedPreset].name}`);
|
|
141
|
+
} else if (themeIndex === presetKeys.length) {
|
|
142
|
+
// Custom rules
|
|
143
|
+
console.log('\nEnter your custom rules for AI (how it should speak, what terms to use):');
|
|
144
|
+
console.log('Example: "Use pirate language. Tasks are treasure hunts. Be playful."');
|
|
145
|
+
const customRules = await question(rl, '\nYour rules: ');
|
|
146
|
+
selectedRules = customRules.trim();
|
|
147
|
+
selectedPreset = 'custom';
|
|
148
|
+
console.log('Custom rules saved');
|
|
149
|
+
} else {
|
|
150
|
+
// Default
|
|
151
|
+
selectedPreset = existingConfig?.rulesPreset || 'default';
|
|
152
|
+
selectedRules = existingConfig?.rules || '';
|
|
153
|
+
console.log(`Keeping: ${RULES_PRESETS[selectedPreset]?.name || 'Default'}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
122
156
|
// Save config
|
|
123
157
|
const config: Config = {
|
|
124
158
|
aiProvider: selectedProvider.name as any,
|
|
@@ -127,11 +161,12 @@ export async function initCommand(existingRl?: readline.Interface): Promise<void
|
|
|
127
161
|
storagePath: rootDir,
|
|
128
162
|
currentPath: rootDir,
|
|
129
163
|
model: selectedProvider.model,
|
|
164
|
+
rules: selectedRules,
|
|
165
|
+
rulesPreset: selectedPreset,
|
|
130
166
|
};
|
|
131
167
|
|
|
132
168
|
saveConfig(config);
|
|
133
169
|
|
|
134
|
-
// Ensure storage directory exists
|
|
135
170
|
if (!fs.existsSync(rootDir)) {
|
|
136
171
|
fs.mkdirSync(rootDir, { recursive: true });
|
|
137
172
|
}
|
|
@@ -141,7 +176,8 @@ export async function initCommand(existingRl?: readline.Interface): Promise<void
|
|
|
141
176
|
console.log('╚═══════════════════════════════════════╝\n');
|
|
142
177
|
console.log(`Root directory: ${rootDir}`);
|
|
143
178
|
console.log(`AI Provider: ${selectedProvider.name}`);
|
|
144
|
-
console.log(`Model: ${selectedProvider.model}
|
|
179
|
+
console.log(`Model: ${selectedProvider.model}`);
|
|
180
|
+
console.log(`Theme: ${RULES_PRESETS[selectedPreset]?.name || 'Custom'}\n`);
|
|
145
181
|
} finally {
|
|
146
182
|
if (shouldCloseRl) {
|
|
147
183
|
rl.close();
|
package/src/config/config.ts
CHANGED
|
@@ -9,8 +9,42 @@ export interface Config {
|
|
|
9
9
|
storagePath: string;
|
|
10
10
|
currentPath: string;
|
|
11
11
|
model?: string;
|
|
12
|
+
rules?: string;
|
|
13
|
+
rulesPreset?: string;
|
|
12
14
|
}
|
|
13
15
|
|
|
16
|
+
// Preset rules for different themes
|
|
17
|
+
export const RULES_PRESETS: Record<string, { name: string; rules: string }> = {
|
|
18
|
+
default: {
|
|
19
|
+
name: 'Default (No theme)',
|
|
20
|
+
rules: '',
|
|
21
|
+
},
|
|
22
|
+
fantasy: {
|
|
23
|
+
name: 'Fantasy RPG',
|
|
24
|
+
rules: 'Use fantasy RPG language. Tasks are "quests", completing them is "slaying". Major milestones are "boss battles". Use terms like "adventurer", "dungeon", "loot", "guild". Add flavor text with swords, dragons, magic.',
|
|
25
|
+
},
|
|
26
|
+
space: {
|
|
27
|
+
name: 'Space Opera',
|
|
28
|
+
rules: 'Use sci-fi space language. Tasks are "missions", completing them is "mission accomplished". Major milestones are "final frontier". Use terms like "commander", "starship", "coordinates", "hyperdrive". Add flavor with stars, planets, aliens.',
|
|
29
|
+
},
|
|
30
|
+
starwars: {
|
|
31
|
+
name: 'Star Wars',
|
|
32
|
+
rules: 'Use Star Wars language. Tasks are "missions from the Rebel Alliance". Completing is "defeating the Empire". Milestones are "destroying the Death Star". Use "Jedi", "Force", "Padawan", "Master". May the Force be with you.',
|
|
33
|
+
},
|
|
34
|
+
western: {
|
|
35
|
+
name: 'Wild West',
|
|
36
|
+
rules: 'Use Wild West language. Tasks are "bounties", completing them is "collecting the reward". Milestones are "showdowns". Use terms like "sheriff", "outlaw", "saloon", "frontier", "partner". Add dusty trails and tumbleweeds.',
|
|
37
|
+
},
|
|
38
|
+
cyberpunk: {
|
|
39
|
+
name: 'Cyberpunk',
|
|
40
|
+
rules: 'Use cyberpunk language. Tasks are "gigs", completing them is "flatlined". Milestones are "megacorp takedowns". Use terms like "netrunner", "chrome", "corpo", "edgerunner", "eddies". Add neon and rain.',
|
|
41
|
+
},
|
|
42
|
+
pirate: {
|
|
43
|
+
name: 'Pirate',
|
|
44
|
+
rules: 'Use pirate language. Tasks are "plunder", completing them is "claiming the treasure". Milestones are "capturing the flagship". Use "captain", "crew", "booty", "seven seas", "landlubber". Arr matey!',
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
14
48
|
const CONFIG_FILE = path.join(os.homedir(), '.rlc', 'config.json');
|
|
15
49
|
const DEFAULT_STORAGE = path.join(os.homedir(), '.rlc', 'workspace');
|
|
16
50
|
|
|
@@ -1107,44 +1107,77 @@ export async function processCommand(
|
|
|
1107
1107
|
}
|
|
1108
1108
|
|
|
1109
1109
|
if (command === 'config') {
|
|
1110
|
+
const { updateConfig, RULES_PRESETS } = await import('../config/config');
|
|
1111
|
+
|
|
1112
|
+
// Check for flags
|
|
1113
|
+
const keyFlag = parts.find(p => p.startsWith('-k=') || p.startsWith('--key='));
|
|
1114
|
+
const modelFlag = parts.find(p => p.startsWith('-m=') || p.startsWith('--model='));
|
|
1115
|
+
const rulesFlag = parts.find(p => p.startsWith('-r=') || p.startsWith('--rules='));
|
|
1116
|
+
const themeFlag = parts.find(p => p.startsWith('-t=') || p.startsWith('--theme='));
|
|
1117
|
+
|
|
1118
|
+
if (keyFlag) {
|
|
1119
|
+
const value = keyFlag.split('=').slice(1).join('=');
|
|
1120
|
+
updateConfig({ apiKey: value });
|
|
1121
|
+
return wrapResult({ output: 'API key updated.' });
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
if (modelFlag) {
|
|
1125
|
+
const value = modelFlag.split('=').slice(1).join('=');
|
|
1126
|
+
updateConfig({ model: value });
|
|
1127
|
+
return wrapResult({ output: `Model updated: ${value}` });
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
if (rulesFlag) {
|
|
1131
|
+
const value = rulesFlag.split('=').slice(1).join('=');
|
|
1132
|
+
updateConfig({ rules: value, rulesPreset: 'custom' });
|
|
1133
|
+
return wrapResult({ output: 'Custom rules updated.' });
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
if (themeFlag) {
|
|
1137
|
+
const value = themeFlag.split('=').slice(1).join('=').toLowerCase();
|
|
1138
|
+
if (RULES_PRESETS[value]) {
|
|
1139
|
+
updateConfig({
|
|
1140
|
+
rules: RULES_PRESETS[value].rules,
|
|
1141
|
+
rulesPreset: value
|
|
1142
|
+
});
|
|
1143
|
+
return wrapResult({ output: `Theme updated: ${RULES_PRESETS[value].name}` });
|
|
1144
|
+
} else {
|
|
1145
|
+
const themes = Object.keys(RULES_PRESETS).join(', ');
|
|
1146
|
+
return wrapResult({ output: `Unknown theme. Available: ${themes}` });
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// Show config
|
|
1110
1151
|
const maskedKey = config.apiKey
|
|
1111
1152
|
? config.apiKey.slice(0, 8) + '...' + config.apiKey.slice(-4)
|
|
1112
1153
|
: '(not set)';
|
|
1113
1154
|
|
|
1155
|
+
const themeName = config.rulesPreset
|
|
1156
|
+
? (RULES_PRESETS[config.rulesPreset]?.name || 'Custom')
|
|
1157
|
+
: 'Default';
|
|
1158
|
+
|
|
1159
|
+
const rulesPreview = config.rules
|
|
1160
|
+
? (config.rules.length > 50 ? config.rules.substring(0, 50) + '...' : config.rules)
|
|
1161
|
+
: '(none)';
|
|
1162
|
+
|
|
1114
1163
|
const output = `
|
|
1115
1164
|
Provider: ${config.aiProvider}
|
|
1116
1165
|
Model: ${config.model || '(default)'}
|
|
1117
1166
|
API Key: ${maskedKey}
|
|
1118
1167
|
Storage: ${config.storagePath}
|
|
1168
|
+
Theme: ${themeName}
|
|
1169
|
+
Rules: ${rulesPreview}
|
|
1170
|
+
|
|
1171
|
+
Set with flags:
|
|
1172
|
+
config -k=<key> Set API key
|
|
1173
|
+
config -m=<model> Set model
|
|
1174
|
+
config -t=<theme> Set theme (fantasy, space, starwars, western, cyberpunk, pirate)
|
|
1175
|
+
config -r="<rules>" Set custom rules
|
|
1119
1176
|
`.trim();
|
|
1120
1177
|
|
|
1121
1178
|
return wrapResult({ output });
|
|
1122
1179
|
}
|
|
1123
1180
|
|
|
1124
|
-
if (command.startsWith('config:')) {
|
|
1125
|
-
const configParts = input.split(':').slice(1).join(':').trim().split('=');
|
|
1126
|
-
if (configParts.length !== 2) {
|
|
1127
|
-
return { output: 'Usage: config:key=value' };
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
const key = configParts[0].trim();
|
|
1131
|
-
const value = configParts[1].trim();
|
|
1132
|
-
|
|
1133
|
-
if (key === 'apiKey') {
|
|
1134
|
-
const { updateConfig } = await import('../config/config');
|
|
1135
|
-
updateConfig({ apiKey: value });
|
|
1136
|
-
return { output: 'API key updated.' };
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
|
-
if (key === 'storagePath') {
|
|
1140
|
-
const { updateConfig } = await import('../config/config');
|
|
1141
|
-
updateConfig({ storagePath: value, currentPath: value });
|
|
1142
|
-
return { output: `Storage path updated to: ${value}` };
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
return { output: `Unknown config key: ${key}` };
|
|
1146
|
-
}
|
|
1147
|
-
|
|
1148
1181
|
if (command === 'help') {
|
|
1149
1182
|
return wrapResult({
|
|
1150
1183
|
output: `
|
|
@@ -1152,9 +1185,7 @@ Storage: ${config.storagePath}
|
|
|
1152
1185
|
|
|
1153
1186
|
Navigation:
|
|
1154
1187
|
ls List tasks and files
|
|
1155
|
-
tree
|
|
1156
|
-
tree -A Include files
|
|
1157
|
-
tree --depth=N Limit tree depth
|
|
1188
|
+
tree [-A] [--depth=N] Show task tree
|
|
1158
1189
|
cd <task> Navigate into task
|
|
1159
1190
|
cd .., ... Go back 1 or 2 levels
|
|
1160
1191
|
pwd Show current path
|
|
@@ -1162,49 +1193,43 @@ Navigation:
|
|
|
1162
1193
|
|
|
1163
1194
|
Task Management:
|
|
1164
1195
|
mkdir <name> Create new task
|
|
1165
|
-
done
|
|
1166
|
-
undo Undo last done
|
|
1167
|
-
deadline <date> Set deadline (today,
|
|
1168
|
-
boss Toggle boss
|
|
1169
|
-
block [node] Block by task
|
|
1170
|
-
unblock Remove
|
|
1171
|
-
status
|
|
1172
|
-
check
|
|
1173
|
-
|
|
1174
|
-
File Operations:
|
|
1175
|
-
cp <src> <dest> Copy file or folder
|
|
1176
|
-
mv <src> <dest> Move/rename
|
|
1177
|
-
rm <name> Delete file
|
|
1178
|
-
rm -rf <name> Delete folder
|
|
1196
|
+
done Complete task (earns XP)
|
|
1197
|
+
undo Undo last done
|
|
1198
|
+
deadline <date> Set deadline (today, +3d, Jan 15)
|
|
1199
|
+
boss Toggle boss status (3x XP)
|
|
1200
|
+
block [node] Block by task or text
|
|
1201
|
+
unblock Remove block
|
|
1202
|
+
status Task details
|
|
1203
|
+
check Upcoming deadlines
|
|
1179
1204
|
|
|
1180
1205
|
Gamification:
|
|
1181
|
-
stats
|
|
1182
|
-
achievements
|
|
1183
|
-
map
|
|
1184
|
-
map --ai AI-generated dungeon map
|
|
1206
|
+
stats XP, level, streaks
|
|
1207
|
+
achievements Achievement list
|
|
1208
|
+
map [--ai] Dungeon map view
|
|
1185
1209
|
|
|
1186
|
-
|
|
1187
|
-
<description> AI generates todo/schema preview
|
|
1188
|
-
save Save pending schema
|
|
1189
|
-
cancel Discard pending schema
|
|
1190
|
-
|
|
1191
|
-
Utility:
|
|
1210
|
+
Configuration:
|
|
1192
1211
|
init Setup wizard
|
|
1193
1212
|
config Show settings
|
|
1213
|
+
config -k=<key> Set API key
|
|
1214
|
+
config -m=<model> Set model
|
|
1215
|
+
config -t=<theme> Set theme (fantasy, space, starwars, etc)
|
|
1216
|
+
config -r="<rules>" Custom AI rules
|
|
1217
|
+
|
|
1218
|
+
Themes:
|
|
1219
|
+
default, fantasy, space, starwars, western, cyberpunk, pirate
|
|
1220
|
+
|
|
1221
|
+
File Operations:
|
|
1222
|
+
cp, mv, rm [-rf] Standard file operations
|
|
1194
1223
|
clean --yes Clear current folder
|
|
1195
|
-
v, version Show version
|
|
1196
|
-
help This help
|
|
1197
|
-
exit, quit Exit
|
|
1198
1224
|
|
|
1199
|
-
|
|
1200
|
-
<
|
|
1201
|
-
|
|
1225
|
+
AI Generation:
|
|
1226
|
+
<description> AI generates preview
|
|
1227
|
+
save Save to folders/file
|
|
1228
|
+
cancel Discard
|
|
1202
1229
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
deadline +3d Due in 3 days
|
|
1207
|
-
check See all upcoming deadlines
|
|
1230
|
+
Clipboard:
|
|
1231
|
+
<cmd> | pbcopy Copy to clipboard (macOS)
|
|
1232
|
+
<cmd> | clip Copy to clipboard (Windows)
|
|
1208
1233
|
|
|
1209
1234
|
www.rlc.rocks
|
|
1210
1235
|
`.trim()
|
|
@@ -17,16 +17,16 @@ const ASCII_ART = [
|
|
|
17
17
|
'║ Roguelike CLI ║',
|
|
18
18
|
'╚═════════════════════════╝',
|
|
19
19
|
'',
|
|
20
|
-
' Navigation: ls, cd, tree, pwd, open',
|
|
21
20
|
' Tasks: done, undo, deadline, boss, block',
|
|
22
|
-
'
|
|
21
|
+
' Stats: stats, achievements, map, check',
|
|
22
|
+
' Config: init, config -t=<theme>',
|
|
23
23
|
'',
|
|
24
|
-
'
|
|
25
|
-
' Workflow: <description> -> refine -> save',
|
|
24
|
+
' Themes: fantasy, space, starwars, cyberpunk',
|
|
26
25
|
'',
|
|
27
|
-
'
|
|
26
|
+
' TAB autocomplete, | pbcopy to copy',
|
|
27
|
+
' <description> -> refine -> save',
|
|
28
28
|
'',
|
|
29
|
-
' www.rlc.rocks',
|
|
29
|
+
' help - commands, www.rlc.rocks',
|
|
30
30
|
'',
|
|
31
31
|
' Ready...',
|
|
32
32
|
'',
|