@kjerneverk/riotplan 1.0.7-dev.1 → 1.0.8
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 +97 -16
- package/TESTING-CHECKLIST.md +140 -0
- package/dist/bin.js +1 -1
- package/dist/{cli-zlZlG2Bw.js → cli-BNnX3BBS.js} +308 -2
- package/dist/cli-BNnX3BBS.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +2 -2
- package/dist/mcp/prompts/execute_plan.md +69 -1
- package/dist/mcp/prompts/execute_step.md +25 -6
- package/dist/mcp-server.js +753 -647
- package/package.json +4 -4
- package/schemas/riotplan-config.schema.json +48 -0
- package/scripts/build-mcp.js +1 -0
- package/dist/cli-zlZlG2Bw.js.map +0 -1
- package/package.json~ +0 -85
package/README.md
CHANGED
|
@@ -1,18 +1,38 @@
|
|
|
1
|
-
#
|
|
1
|
+
# RiotPlan
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**A plan is bigger than a list of tasks.**
|
|
4
|
+
|
|
5
|
+
RiotPlan treats plans as **constructs**—full lifecycles from idea exploration through execution. Think before you execute. Support complex, multi-session workflows. Make your plans truly yours.
|
|
6
|
+
|
|
7
|
+
Part of [Kjerneverk](https://kjerneverk.github.io) - structured formats for working with generative AI.
|
|
4
8
|
|
|
5
9
|
**Now available as an MCP server!** Integrate with Cursor and other AI assistants - see [MCP Integration](#mcp-integration) below.
|
|
6
10
|
|
|
7
|
-
##
|
|
11
|
+
## Why RiotPlan?
|
|
12
|
+
|
|
13
|
+
### Before: Inadequate Planning
|
|
14
|
+
|
|
15
|
+
- **Tool-generated inadequacy**: Depending on AI tools to generate simplistic task lists
|
|
16
|
+
- **Markdown chaos**: Plans are markdown files that pile up without structure or lifecycle
|
|
17
|
+
- **Issue trackers without thinking**: Systems like Beads (Steve Yegge's git-backed tracker) address markdown problems but don't support deep, thoughtful planning
|
|
18
|
+
- **No analysis phase**: Jumping straight to execution without exploring ideas or comparing approaches
|
|
8
19
|
|
|
9
|
-
|
|
20
|
+
### After: Plans as Lifecycle
|
|
10
21
|
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
22
|
+
- **Standard lifecycle**: Idea exploration → Shaping approaches → Building detailed plan → Execution → Completion
|
|
23
|
+
- **Thinking before execution**: You can't just create a plan and execute it. RiotPlan supports analysis, elaboration, research.
|
|
24
|
+
- **Standard infrastructure**: MCP resources, tools, and prompts that know how to work with plans. Not just a format, but a system.
|
|
25
|
+
- **Tool independence**: Works from CLI with API keys, via MCP with any model, or through future GUI applications.
|
|
26
|
+
|
|
27
|
+
## What is a RiotPlan?
|
|
28
|
+
|
|
29
|
+
A **plan** is a construct that manages multi-step AI-assisted tasks:
|
|
30
|
+
|
|
31
|
+
- **Spans multiple sessions** - Work on a task over days or weeks
|
|
32
|
+
- **Has persistent state** - Track progress in STATUS.md
|
|
33
|
+
- **Organized into steps** - Numbered files (01-STEP.md, 02-STEP.md)
|
|
14
34
|
- **Can be interrupted and resumed** - Pick up where you left off
|
|
15
|
-
- **
|
|
35
|
+
- **Supports deep thinking** - Idea exploration, approach comparison, analysis before action
|
|
16
36
|
|
|
17
37
|
## Plan Structure
|
|
18
38
|
|
|
@@ -249,20 +269,52 @@ riotplan generate ./my-plan --provider anthropic --model claude-sonnet-4-5
|
|
|
249
269
|
|
|
250
270
|
### Configuration
|
|
251
271
|
|
|
252
|
-
|
|
272
|
+
RiotPlan uses a flexible **four-tier configuration system** to determine where plans are stored:
|
|
273
|
+
|
|
274
|
+
1. **Environment Variable** (`RIOTPLAN_PLAN_DIRECTORY`) - Highest priority
|
|
275
|
+
2. **Config File** (`riotplan.config.*`, `.riotplan/config.*`, etc.) - Project-level
|
|
276
|
+
3. **Auto-Detection** - Automatically finds `plans/` directory by walking up the tree
|
|
277
|
+
4. **Fallback** - Uses `./plans` in current directory (zero-config experience)
|
|
278
|
+
|
|
279
|
+
**Quick Start:**
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
# Most users: Just start using RiotPlan - it finds plans/ automatically!
|
|
283
|
+
riotplan create my-feature
|
|
284
|
+
|
|
285
|
+
# Create a config file (optional)
|
|
286
|
+
riotplan --init-config
|
|
287
|
+
|
|
288
|
+
# Check current configuration
|
|
289
|
+
riotplan check-config
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Example: `riotplan.config.yaml`**
|
|
293
|
+
|
|
294
|
+
```yaml
|
|
295
|
+
planDirectory: ./plans
|
|
296
|
+
defaultProvider: anthropic
|
|
297
|
+
defaultModel: claude-3-5-sonnet-20241022
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**MCP Server Configuration:**
|
|
253
301
|
|
|
254
302
|
```json
|
|
255
303
|
{
|
|
256
|
-
"
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
304
|
+
"mcpServers": {
|
|
305
|
+
"riotplan": {
|
|
306
|
+
"command": "npx",
|
|
307
|
+
"args": ["-y", "@riotprompt/riotplan"],
|
|
308
|
+
"env": {
|
|
309
|
+
"RIOTPLAN_PLAN_DIRECTORY": "/path/to/plans"
|
|
310
|
+
}
|
|
311
|
+
}
|
|
262
312
|
}
|
|
263
313
|
}
|
|
264
314
|
```
|
|
265
315
|
|
|
316
|
+
See [Configuration Guide](./guide/configuration.md) for complete documentation.
|
|
317
|
+
|
|
266
318
|
## Programmatic Usage
|
|
267
319
|
|
|
268
320
|
```typescript
|
|
@@ -371,12 +423,17 @@ Add to your Cursor MCP settings (`~/.cursor/mcp.json`):
|
|
|
371
423
|
"mcpServers": {
|
|
372
424
|
"riotplan": {
|
|
373
425
|
"command": "npx",
|
|
374
|
-
"args": ["-y", "@riotprompt/riotplan",
|
|
426
|
+
"args": ["-y", "@riotprompt/riotplan"],
|
|
427
|
+
"env": {
|
|
428
|
+
"RIOTPLAN_PLAN_DIRECTORY": "/path/to/plans"
|
|
429
|
+
}
|
|
375
430
|
}
|
|
376
431
|
}
|
|
377
432
|
}
|
|
378
433
|
```
|
|
379
434
|
|
|
435
|
+
**Zero-Config Experience:** If you don't set `RIOTPLAN_PLAN_DIRECTORY`, RiotPlan will automatically find your `plans/` directory by walking up from your workspace root. No configuration needed!
|
|
436
|
+
|
|
380
437
|
### MCP Tools
|
|
381
438
|
|
|
382
439
|
- **`riotplan_create`** - Create new plans with AI-generated steps
|
|
@@ -424,6 +481,30 @@ riotplan_step_start({ path: "./user-auth", step: 1 })
|
|
|
424
481
|
riotplan_step_complete({ path: "./user-auth", step: 1 })
|
|
425
482
|
```
|
|
426
483
|
|
|
484
|
+
### For AI Assistants: Executing Plans with Tracking
|
|
485
|
+
|
|
486
|
+
**When executing a RiotPlan, you MUST use RiotPlan's tracking infrastructure:**
|
|
487
|
+
|
|
488
|
+
1. **Check if step files exist** in `plan/` directory
|
|
489
|
+
- If `EXECUTION_PLAN.md` exists but step files don't, create them first
|
|
490
|
+
- Step files (e.g., `01-step.md`, `02-step.md`) are required for tracking
|
|
491
|
+
|
|
492
|
+
2. **For each step you execute:**
|
|
493
|
+
- Call `riotplan_step_start({ path, step: N })` **BEFORE** doing any work
|
|
494
|
+
- Do the actual work (implement, test, document)
|
|
495
|
+
- Call `riotplan_step_complete({ path, step: N })` **AFTER** completing the work
|
|
496
|
+
- Let RiotPlan update STATUS.md automatically
|
|
497
|
+
|
|
498
|
+
3. **Use the `execute_plan` prompt** for guided execution:
|
|
499
|
+
```
|
|
500
|
+
/riotplan/execute_plan
|
|
501
|
+
```
|
|
502
|
+
This provides the complete workflow for executing with tracking.
|
|
503
|
+
|
|
504
|
+
**Key Principle**: If you're working on a RiotPlan, RiotPlan should manage the execution, not just the planning. Don't just do the work - use the tracking tools!
|
|
505
|
+
|
|
506
|
+
**Common Mistake**: Executing steps without using `riotplan_step_start` and `riotplan_step_complete`. This bypasses RiotPlan's execution management and breaks progress tracking.
|
|
507
|
+
|
|
427
508
|
See [guide/mcp.md](./guide/mcp.md) for detailed MCP documentation.
|
|
428
509
|
|
|
429
510
|
## Philosophy
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Testing Checklist for Plan Directory Configuration
|
|
2
|
+
|
|
3
|
+
This document provides checklists for manually testing the plan directory configuration feature.
|
|
4
|
+
|
|
5
|
+
## MCP Server Testing (Step 13)
|
|
6
|
+
|
|
7
|
+
### Scenario 1: MCP with Environment Variable
|
|
8
|
+
- [ ] Set `RIOTPLAN_PLAN_DIRECTORY` in `.cursor/mcp.json`:
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"mcpServers": {
|
|
12
|
+
"riotplan": {
|
|
13
|
+
"command": "npx",
|
|
14
|
+
"args": ["-y", "@riotprompt/riotplan"],
|
|
15
|
+
"env": {
|
|
16
|
+
"RIOTPLAN_PLAN_DIRECTORY": "/path/to/custom/plans"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
- [ ] Restart Cursor/IDE to load MCP server
|
|
23
|
+
- [ ] Run `riotplan idea create test-idea "Test idea"` via MCP
|
|
24
|
+
- [ ] Verify plan created in `/path/to/custom/plans/test-idea`
|
|
25
|
+
- [ ] Run `riotplan check-config` via MCP or CLI
|
|
26
|
+
- [ ] Verify output shows "Environment variable (tier 1)" as source
|
|
27
|
+
|
|
28
|
+
### Scenario 2: MCP with Config File
|
|
29
|
+
- [ ] Create `riotplan.config.yaml` in project root:
|
|
30
|
+
```yaml
|
|
31
|
+
planDirectory: ./my-plans
|
|
32
|
+
```
|
|
33
|
+
- [ ] Remove `RIOTPLAN_PLAN_DIRECTORY` from `.cursor/mcp.json` (if set)
|
|
34
|
+
- [ ] Restart Cursor/IDE
|
|
35
|
+
- [ ] Run `riotplan idea create test-idea "Test"` via MCP
|
|
36
|
+
- [ ] Verify plan created in `./my-plans/test-idea`
|
|
37
|
+
- [ ] Run `riotplan check-config`
|
|
38
|
+
- [ ] Verify output shows "Config file (tier 2)" as source
|
|
39
|
+
|
|
40
|
+
### Scenario 3: MCP with Walk-Up
|
|
41
|
+
- [ ] Remove `RIOTPLAN_PLAN_DIRECTORY` from `.cursor/mcp.json`
|
|
42
|
+
- [ ] Remove or rename `riotplan.config.yaml`
|
|
43
|
+
- [ ] Ensure `plans/` directory exists in project root
|
|
44
|
+
- [ ] Restart Cursor/IDE
|
|
45
|
+
- [ ] Run `riotplan idea create test-idea "Test"` via MCP
|
|
46
|
+
- [ ] Verify plan created in `plans/test-idea`
|
|
47
|
+
- [ ] Run `riotplan check-config`
|
|
48
|
+
- [ ] Verify output shows "Walk-up detection (tier 3)" as source
|
|
49
|
+
|
|
50
|
+
### Scenario 4: MCP with Fallback
|
|
51
|
+
- [ ] Remove `RIOTPLAN_PLAN_DIRECTORY` from `.cursor/mcp.json`
|
|
52
|
+
- [ ] Remove `riotplan.config.yaml`
|
|
53
|
+
- [ ] Remove or rename `plans/` directory
|
|
54
|
+
- [ ] Restart Cursor/IDE
|
|
55
|
+
- [ ] Run `riotplan idea create test-idea "Test"` via MCP
|
|
56
|
+
- [ ] Verify plan created in `./plans/test-idea` (fallback)
|
|
57
|
+
- [ ] Run `riotplan check-config`
|
|
58
|
+
- [ ] Verify output shows "Fallback (tier 4)" as source
|
|
59
|
+
|
|
60
|
+
### Scenario 5: Precedence Testing
|
|
61
|
+
- [ ] Set `RIOTPLAN_PLAN_DIRECTORY` in `.cursor/mcp.json`
|
|
62
|
+
- [ ] Create `riotplan.config.yaml` with different `planDirectory`
|
|
63
|
+
- [ ] Ensure `plans/` directory exists
|
|
64
|
+
- [ ] Restart Cursor/IDE
|
|
65
|
+
- [ ] Run `riotplan check-config`
|
|
66
|
+
- [ ] Verify env var takes precedence (tier 1)
|
|
67
|
+
- [ ] Remove env var, restart, verify config takes precedence (tier 2)
|
|
68
|
+
- [ ] Remove config, restart, verify walk-up takes precedence (tier 3)
|
|
69
|
+
|
|
70
|
+
## CLI Testing (Step 14)
|
|
71
|
+
|
|
72
|
+
### Scenario 1: CLI with No Configuration
|
|
73
|
+
- [ ] Navigate to empty directory (no config, no plans/)
|
|
74
|
+
- [ ] Run `riotplan idea create test-idea "Test idea"`
|
|
75
|
+
- [ ] Verify `./plans` directory created
|
|
76
|
+
- [ ] Verify plan created in `./plans/test-idea`
|
|
77
|
+
- [ ] Run `riotplan check-config`
|
|
78
|
+
- [ ] Verify shows "Fallback (tier 4)"
|
|
79
|
+
|
|
80
|
+
### Scenario 2: CLI with Existing plans/ Directory
|
|
81
|
+
- [ ] Create `plans/` directory in project root
|
|
82
|
+
- [ ] Navigate to subdirectory: `cd src/utils`
|
|
83
|
+
- [ ] Run `riotplan idea create test-idea "Test"`
|
|
84
|
+
- [ ] Verify walk-up found `plans/` in project root
|
|
85
|
+
- [ ] Verify plan created in project root `plans/test-idea`
|
|
86
|
+
- [ ] Run `riotplan check-config`
|
|
87
|
+
- [ ] Verify shows "Walk-up detection (tier 3)"
|
|
88
|
+
|
|
89
|
+
### Scenario 3: CLI with Config File
|
|
90
|
+
- [ ] Create `riotplan.config.yaml`:
|
|
91
|
+
```yaml
|
|
92
|
+
planDirectory: ./custom-plans
|
|
93
|
+
```
|
|
94
|
+
- [ ] Run from any directory in project
|
|
95
|
+
- [ ] Run `riotplan idea create test-idea "Test"`
|
|
96
|
+
- [ ] Verify plan created in `./custom-plans/test-idea`
|
|
97
|
+
- [ ] Run `riotplan check-config`
|
|
98
|
+
- [ ] Verify shows "Config file (tier 2)"
|
|
99
|
+
|
|
100
|
+
### Scenario 4: CLI with Environment Variable
|
|
101
|
+
- [ ] Set `export RIOTPLAN_PLAN_DIRECTORY=/tmp/test-plans`
|
|
102
|
+
- [ ] Run from any directory
|
|
103
|
+
- [ ] Run `riotplan idea create test-idea "Test"`
|
|
104
|
+
- [ ] Verify plan created in `/tmp/test-plans/test-idea`
|
|
105
|
+
- [ ] Run `riotplan check-config`
|
|
106
|
+
- [ ] Verify shows "Environment variable (tier 1)"
|
|
107
|
+
|
|
108
|
+
### Scenario 5: CLI with Explicit Path
|
|
109
|
+
- [ ] Run `riotplan idea create test-idea "Test" --path /custom/explicit/path`
|
|
110
|
+
- [ ] Verify explicit path takes precedence over all tiers
|
|
111
|
+
- [ ] Verify plan created in `/custom/explicit/path/test-idea`
|
|
112
|
+
- [ ] Verify backward compatibility maintained
|
|
113
|
+
|
|
114
|
+
### Scenario 6: CLI Configuration Commands
|
|
115
|
+
- [ ] Run `riotplan --init-config`
|
|
116
|
+
- [ ] Verify `riotplan.config.yaml` created with defaults
|
|
117
|
+
- [ ] Verify file contains `planDirectory: ./plans`
|
|
118
|
+
- [ ] Run `riotplan check-config`
|
|
119
|
+
- [ ] Verify shows correct configuration source
|
|
120
|
+
- [ ] Verify shows resolved plan directory
|
|
121
|
+
|
|
122
|
+
## Performance Testing
|
|
123
|
+
|
|
124
|
+
- [ ] Run `riotplan check-config` multiple times - should be fast (cached)
|
|
125
|
+
- [ ] Test MCP server startup time - should not be significantly slower
|
|
126
|
+
- [ ] Test walk-up from deep nested directory (e.g., `src/components/ui/buttons/`)
|
|
127
|
+
- [ ] Verify caching works (second call should be instant)
|
|
128
|
+
|
|
129
|
+
## Error Scenarios
|
|
130
|
+
|
|
131
|
+
- [ ] Invalid config file (malformed YAML/JSON) - should show helpful error
|
|
132
|
+
- [ ] Config file with invalid `planDirectory` (non-existent absolute path)
|
|
133
|
+
- [ ] Permission issues (read-only directory)
|
|
134
|
+
- [ ] Missing CardiganTime dependency (should fail gracefully)
|
|
135
|
+
|
|
136
|
+
## Notes
|
|
137
|
+
|
|
138
|
+
- All tests should be performed in a clean environment
|
|
139
|
+
- Clear caches between tests if needed: `clearResolverCache()`, `clearConfigCache()`, `clearWalkUpCache()`
|
|
140
|
+
- Document any issues found during testing
|
package/dist/bin.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { readFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, statSync, writeFileSync, readFileSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { resolve, join, basename, dirname } from "node:path";
|
|
6
6
|
import { readFile, stat, readdir, writeFile, mkdir, access, rename, rm } from "node:fs/promises";
|
|
7
7
|
import "node:os";
|
|
8
8
|
import inquirer from "inquirer";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { create } from "@utilarium/cardigantime";
|
|
9
11
|
const PLAN_CONVENTIONS = {
|
|
10
12
|
/** Meta-prompt file patterns */
|
|
11
13
|
metaPromptPatterns: [
|
|
@@ -1179,6 +1181,18 @@ None currently.
|
|
|
1179
1181
|
|
|
1180
1182
|
---
|
|
1181
1183
|
|
|
1184
|
+
## Execution Tracking
|
|
1185
|
+
|
|
1186
|
+
**To execute this plan with RiotPlan tracking:**
|
|
1187
|
+
|
|
1188
|
+
1. Use \`riotplan_step_start({ path, step: N })\` **before** starting each step
|
|
1189
|
+
2. Complete the work for the step
|
|
1190
|
+
3. Use \`riotplan_step_complete({ path, step: N })\` **after** completing each step
|
|
1191
|
+
|
|
1192
|
+
**For AI Assistants:** When executing this plan, always use RiotPlan's tracking tools. Don't just do the work - use \`riotplan_step_start\` and \`riotplan_step_complete\` to track progress. This ensures STATUS.md stays up-to-date and the plan can be resumed later.
|
|
1193
|
+
|
|
1194
|
+
---
|
|
1195
|
+
|
|
1182
1196
|
*Last updated: ${date}*
|
|
1183
1197
|
`;
|
|
1184
1198
|
}
|
|
@@ -6144,6 +6158,297 @@ function registerVerifyCommand(program) {
|
|
|
6144
6158
|
}
|
|
6145
6159
|
});
|
|
6146
6160
|
}
|
|
6161
|
+
const RiotPlanConfigSchema = z.object({
|
|
6162
|
+
/**
|
|
6163
|
+
* Path to the directory where plans are stored
|
|
6164
|
+
* Can be relative (resolved from config file location) or absolute
|
|
6165
|
+
* Defaults to './plans' if not specified
|
|
6166
|
+
*/
|
|
6167
|
+
planDirectory: z.string().default("./plans"),
|
|
6168
|
+
/**
|
|
6169
|
+
* Default AI provider to use for plan generation
|
|
6170
|
+
* Optional - if not specified, uses system defaults
|
|
6171
|
+
*/
|
|
6172
|
+
defaultProvider: z.enum(["anthropic", "openai", "gemini"]).optional(),
|
|
6173
|
+
/**
|
|
6174
|
+
* Default model to use for plan generation
|
|
6175
|
+
* Optional - if not specified, uses provider defaults
|
|
6176
|
+
*/
|
|
6177
|
+
defaultModel: z.string().optional(),
|
|
6178
|
+
/**
|
|
6179
|
+
* Path to custom plan templates directory
|
|
6180
|
+
* Can be relative (resolved from config file location) or absolute
|
|
6181
|
+
* Optional - if not specified, uses built-in templates
|
|
6182
|
+
*/
|
|
6183
|
+
templateDirectory: z.string().optional()
|
|
6184
|
+
});
|
|
6185
|
+
const cardigantime = create({
|
|
6186
|
+
defaults: {
|
|
6187
|
+
configDirectory: process.cwd(),
|
|
6188
|
+
isRequired: false
|
|
6189
|
+
// Config file is optional
|
|
6190
|
+
},
|
|
6191
|
+
configShape: RiotPlanConfigSchema.shape,
|
|
6192
|
+
features: ["config"]
|
|
6193
|
+
// Hierarchical discovery is enabled by default in CardiganTime
|
|
6194
|
+
});
|
|
6195
|
+
let cachedConfig = null;
|
|
6196
|
+
let configLoadAttempted = false;
|
|
6197
|
+
async function loadConfig() {
|
|
6198
|
+
if (cachedConfig !== null) {
|
|
6199
|
+
return cachedConfig;
|
|
6200
|
+
}
|
|
6201
|
+
if (configLoadAttempted) {
|
|
6202
|
+
return null;
|
|
6203
|
+
}
|
|
6204
|
+
try {
|
|
6205
|
+
configLoadAttempted = true;
|
|
6206
|
+
const args = {};
|
|
6207
|
+
const config = await cardigantime.read(args);
|
|
6208
|
+
await cardigantime.validate(config);
|
|
6209
|
+
cachedConfig = config;
|
|
6210
|
+
return cachedConfig;
|
|
6211
|
+
} catch (error) {
|
|
6212
|
+
if (error instanceof Error && error.message.includes("not found")) {
|
|
6213
|
+
return null;
|
|
6214
|
+
}
|
|
6215
|
+
throw error;
|
|
6216
|
+
}
|
|
6217
|
+
}
|
|
6218
|
+
const walkUpCache = /* @__PURE__ */ new Map();
|
|
6219
|
+
function findPlansDirectory(startPath) {
|
|
6220
|
+
const normalizedStart = resolve(startPath);
|
|
6221
|
+
if (walkUpCache.has(normalizedStart)) {
|
|
6222
|
+
return walkUpCache.get(normalizedStart) ?? null;
|
|
6223
|
+
}
|
|
6224
|
+
let currentPath = normalizedStart;
|
|
6225
|
+
while (currentPath !== "/") {
|
|
6226
|
+
const plansPath = join(currentPath, "plans");
|
|
6227
|
+
if (existsSync(plansPath)) {
|
|
6228
|
+
try {
|
|
6229
|
+
const stats = statSync(plansPath);
|
|
6230
|
+
if (stats.isDirectory()) {
|
|
6231
|
+
walkUpCache.set(normalizedStart, currentPath);
|
|
6232
|
+
return currentPath;
|
|
6233
|
+
}
|
|
6234
|
+
} catch {
|
|
6235
|
+
}
|
|
6236
|
+
}
|
|
6237
|
+
const parentPath = dirname(currentPath);
|
|
6238
|
+
if (parentPath === currentPath) {
|
|
6239
|
+
break;
|
|
6240
|
+
}
|
|
6241
|
+
currentPath = parentPath;
|
|
6242
|
+
}
|
|
6243
|
+
walkUpCache.set(normalizedStart, null);
|
|
6244
|
+
return null;
|
|
6245
|
+
}
|
|
6246
|
+
let cachedPlanDirectory = null;
|
|
6247
|
+
let resolutionAttempted = false;
|
|
6248
|
+
async function resolvePlanDirectory() {
|
|
6249
|
+
if (cachedPlanDirectory !== null) {
|
|
6250
|
+
return cachedPlanDirectory;
|
|
6251
|
+
}
|
|
6252
|
+
if (resolutionAttempted && cachedPlanDirectory === null) {
|
|
6253
|
+
return join(process.cwd(), "plans");
|
|
6254
|
+
}
|
|
6255
|
+
resolutionAttempted = true;
|
|
6256
|
+
try {
|
|
6257
|
+
const config = await loadConfig();
|
|
6258
|
+
if (config?.planDirectory) {
|
|
6259
|
+
const resolvedPath = resolve(config.planDirectory);
|
|
6260
|
+
cachedPlanDirectory = resolvedPath;
|
|
6261
|
+
return resolvedPath;
|
|
6262
|
+
}
|
|
6263
|
+
} catch (error) {
|
|
6264
|
+
}
|
|
6265
|
+
const foundPlansParent = findPlansDirectory(process.cwd());
|
|
6266
|
+
if (foundPlansParent) {
|
|
6267
|
+
const plansPath = join(foundPlansParent, "plans");
|
|
6268
|
+
cachedPlanDirectory = resolve(plansPath);
|
|
6269
|
+
return cachedPlanDirectory;
|
|
6270
|
+
}
|
|
6271
|
+
const fallbackPath = join(process.cwd(), "plans");
|
|
6272
|
+
cachedPlanDirectory = resolve(fallbackPath);
|
|
6273
|
+
return cachedPlanDirectory;
|
|
6274
|
+
}
|
|
6275
|
+
function registerConfigCommands(program) {
|
|
6276
|
+
program.option("--init-config", "Create initial riotplan.config.yaml file").hook("preAction", async (thisCommand) => {
|
|
6277
|
+
if (thisCommand.opts().initConfig) {
|
|
6278
|
+
await handleInitConfig();
|
|
6279
|
+
process.exit(0);
|
|
6280
|
+
}
|
|
6281
|
+
});
|
|
6282
|
+
program.command("check-config").description("Show current configuration resolution and validation").action(async () => {
|
|
6283
|
+
await handleCheckConfig();
|
|
6284
|
+
});
|
|
6285
|
+
}
|
|
6286
|
+
async function handleInitConfig() {
|
|
6287
|
+
const configPath = resolve(process.cwd(), "riotplan.config.yaml");
|
|
6288
|
+
if (existsSync(configPath)) {
|
|
6289
|
+
console.log(chalk.yellow(`⚠ Configuration file already exists: ${configPath}`));
|
|
6290
|
+
console.log(chalk.dim("Use --check-config to view current configuration."));
|
|
6291
|
+
return;
|
|
6292
|
+
}
|
|
6293
|
+
const configContent = `# RiotPlan Configuration
|
|
6294
|
+
# See https://github.com/kjerneverk/riotplan for documentation
|
|
6295
|
+
|
|
6296
|
+
# Directory where plans are stored (relative or absolute)
|
|
6297
|
+
# Relative paths are resolved from this config file's location
|
|
6298
|
+
planDirectory: ./plans
|
|
6299
|
+
|
|
6300
|
+
# Optional: Default AI provider for plan generation
|
|
6301
|
+
# Options: anthropic, openai, gemini
|
|
6302
|
+
# defaultProvider: anthropic
|
|
6303
|
+
|
|
6304
|
+
# Optional: Default model to use for plan generation
|
|
6305
|
+
# Examples: claude-3-5-sonnet-20241022, gpt-4, gemini-pro
|
|
6306
|
+
# defaultModel: claude-3-5-sonnet-20241022
|
|
6307
|
+
|
|
6308
|
+
# Optional: Custom template directory
|
|
6309
|
+
# templateDirectory: ./.riotplan/templates
|
|
6310
|
+
`;
|
|
6311
|
+
try {
|
|
6312
|
+
writeFileSync(configPath, configContent, "utf-8");
|
|
6313
|
+
console.log(chalk.green(`✓ Created configuration file: ${configPath}`));
|
|
6314
|
+
console.log(chalk.dim("\nEdit this file to customize your RiotPlan configuration."));
|
|
6315
|
+
console.log(chalk.dim('Run "riotplan check-config" to verify your configuration.'));
|
|
6316
|
+
} catch (error) {
|
|
6317
|
+
console.error(chalk.red(`✗ Failed to create config file: ${error instanceof Error ? error.message : String(error)}`));
|
|
6318
|
+
process.exit(1);
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
async function handleCheckConfig() {
|
|
6322
|
+
console.log(chalk.bold("\nRiotPlan Configuration Check"));
|
|
6323
|
+
console.log("============================\n");
|
|
6324
|
+
let configSource = "Unknown";
|
|
6325
|
+
let configLocation = null;
|
|
6326
|
+
let planDirectory;
|
|
6327
|
+
let config = null;
|
|
6328
|
+
if (process.env.RIOTPLAN_PLAN_DIRECTORY) {
|
|
6329
|
+
configSource = "Environment variable (tier 1)";
|
|
6330
|
+
planDirectory = resolve(process.env.RIOTPLAN_PLAN_DIRECTORY);
|
|
6331
|
+
console.log(chalk.green("✓ Configuration loaded successfully\n"));
|
|
6332
|
+
console.log(`Source: ${chalk.bold(configSource)}`);
|
|
6333
|
+
console.log(`Environment Variable: ${chalk.cyan("RIOTPLAN_PLAN_DIRECTORY")}`);
|
|
6334
|
+
console.log(`Plan Directory: ${chalk.cyan(planDirectory)} ${existsSync(planDirectory) ? chalk.green("(exists)") : chalk.yellow("(does not exist)")}`);
|
|
6335
|
+
} else {
|
|
6336
|
+
try {
|
|
6337
|
+
config = await loadConfig();
|
|
6338
|
+
if (config) {
|
|
6339
|
+
configSource = "Config file (tier 2)";
|
|
6340
|
+
configLocation = findConfigFileLocation();
|
|
6341
|
+
planDirectory = resolve(config.planDirectory || "./plans");
|
|
6342
|
+
console.log(chalk.green("✓ Configuration loaded successfully\n"));
|
|
6343
|
+
console.log(`Source: ${chalk.bold(configSource)}`);
|
|
6344
|
+
if (configLocation) {
|
|
6345
|
+
console.log(`Location: ${chalk.cyan(configLocation)}`);
|
|
6346
|
+
}
|
|
6347
|
+
console.log(`Plan Directory: ${chalk.cyan(planDirectory)} ${existsSync(planDirectory) ? chalk.green("(exists)") : chalk.yellow("(does not exist)")}`);
|
|
6348
|
+
} else {
|
|
6349
|
+
const foundPlansParent = findPlansDirectory(process.cwd());
|
|
6350
|
+
if (foundPlansParent) {
|
|
6351
|
+
configSource = "Walk-up detection (tier 3)";
|
|
6352
|
+
planDirectory = join(foundPlansParent, "plans");
|
|
6353
|
+
console.log(chalk.green("✓ Configuration resolved successfully\n"));
|
|
6354
|
+
console.log(`Source: ${chalk.bold(configSource)}`);
|
|
6355
|
+
console.log(`Found plans/ directory at: ${chalk.cyan(dirname(planDirectory))}`);
|
|
6356
|
+
console.log(`Plan Directory: ${chalk.cyan(planDirectory)} ${existsSync(planDirectory) ? chalk.green("(exists)") : chalk.yellow("(does not exist)")}`);
|
|
6357
|
+
} else {
|
|
6358
|
+
configSource = "Fallback (tier 4)";
|
|
6359
|
+
planDirectory = join(process.cwd(), "plans");
|
|
6360
|
+
console.log(chalk.yellow("⚠ Using default configuration\n"));
|
|
6361
|
+
console.log(`Source: ${chalk.bold(configSource)}`);
|
|
6362
|
+
console.log(`Plan Directory: ${chalk.cyan(planDirectory)} ${existsSync(planDirectory) ? chalk.green("(exists)") : chalk.yellow("(will be created on first use)")}`);
|
|
6363
|
+
}
|
|
6364
|
+
}
|
|
6365
|
+
} catch (error) {
|
|
6366
|
+
console.log(chalk.red("✗ Configuration error\n"));
|
|
6367
|
+
console.log(`Error: ${chalk.red(error instanceof Error ? error.message : String(error))}`);
|
|
6368
|
+
console.log(chalk.dim("\nFix the configuration error and try again."));
|
|
6369
|
+
process.exit(1);
|
|
6370
|
+
}
|
|
6371
|
+
}
|
|
6372
|
+
try {
|
|
6373
|
+
const resolved = await resolvePlanDirectory();
|
|
6374
|
+
console.log(`
|
|
6375
|
+
Resolved Plan Directory: ${chalk.bold.cyan(resolved)}`);
|
|
6376
|
+
} catch (error) {
|
|
6377
|
+
console.log(chalk.yellow(`
|
|
6378
|
+
⚠ Could not resolve plan directory: ${error instanceof Error ? error.message : String(error)}`));
|
|
6379
|
+
}
|
|
6380
|
+
if (config) {
|
|
6381
|
+
console.log("\nSettings:");
|
|
6382
|
+
if (config.planDirectory) {
|
|
6383
|
+
console.log(` planDirectory: ${chalk.cyan(config.planDirectory)}`);
|
|
6384
|
+
}
|
|
6385
|
+
if (config.defaultProvider) {
|
|
6386
|
+
console.log(` defaultProvider: ${chalk.cyan(config.defaultProvider)}`);
|
|
6387
|
+
}
|
|
6388
|
+
if (config.defaultModel) {
|
|
6389
|
+
console.log(` defaultModel: ${chalk.cyan(config.defaultModel)}`);
|
|
6390
|
+
}
|
|
6391
|
+
if (config.templateDirectory) {
|
|
6392
|
+
console.log(` templateDirectory: ${chalk.cyan(config.templateDirectory)}`);
|
|
6393
|
+
}
|
|
6394
|
+
if (!config.planDirectory && !config.defaultProvider && !config.defaultModel && !config.templateDirectory) {
|
|
6395
|
+
console.log(chalk.dim(" (using defaults)"));
|
|
6396
|
+
}
|
|
6397
|
+
}
|
|
6398
|
+
console.log();
|
|
6399
|
+
}
|
|
6400
|
+
function findConfigFileLocation() {
|
|
6401
|
+
const configNames = [
|
|
6402
|
+
"riotplan.config.yaml",
|
|
6403
|
+
"riotplan.config.yml",
|
|
6404
|
+
"riotplan.config.json",
|
|
6405
|
+
"riotplan.config.js",
|
|
6406
|
+
"riotplan.config.ts",
|
|
6407
|
+
"riotplan.conf.yaml",
|
|
6408
|
+
"riotplan.conf.yml",
|
|
6409
|
+
"riotplan.conf.json",
|
|
6410
|
+
".riotplanrc.yaml",
|
|
6411
|
+
".riotplanrc.yml",
|
|
6412
|
+
".riotplanrc.json",
|
|
6413
|
+
".riotplanrc"
|
|
6414
|
+
];
|
|
6415
|
+
let currentPath = process.cwd();
|
|
6416
|
+
while (currentPath !== "/") {
|
|
6417
|
+
for (const name of configNames) {
|
|
6418
|
+
const configPath = join(currentPath, name);
|
|
6419
|
+
if (existsSync(configPath)) {
|
|
6420
|
+
try {
|
|
6421
|
+
const stats = statSync(configPath);
|
|
6422
|
+
if (stats.isFile()) {
|
|
6423
|
+
return configPath;
|
|
6424
|
+
}
|
|
6425
|
+
} catch {
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
const dotRiotplanDir = join(currentPath, ".riotplan");
|
|
6430
|
+
if (existsSync(dotRiotplanDir)) {
|
|
6431
|
+
try {
|
|
6432
|
+
const stats = statSync(dotRiotplanDir);
|
|
6433
|
+
if (stats.isDirectory()) {
|
|
6434
|
+
for (const ext of ["yaml", "yml", "json", "js", "ts"]) {
|
|
6435
|
+
const configPath = join(dotRiotplanDir, `config.${ext}`);
|
|
6436
|
+
if (existsSync(configPath)) {
|
|
6437
|
+
return configPath;
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
}
|
|
6441
|
+
} catch {
|
|
6442
|
+
}
|
|
6443
|
+
}
|
|
6444
|
+
const parentPath = dirname(currentPath);
|
|
6445
|
+
if (parentPath === currentPath) {
|
|
6446
|
+
break;
|
|
6447
|
+
}
|
|
6448
|
+
currentPath = parentPath;
|
|
6449
|
+
}
|
|
6450
|
+
return null;
|
|
6451
|
+
}
|
|
6147
6452
|
const __filename$1 = fileURLToPath(import.meta.url);
|
|
6148
6453
|
const __dirname$1 = dirname(__filename$1);
|
|
6149
6454
|
let packageJsonPath = join(__dirname$1, "../package.json");
|
|
@@ -6174,6 +6479,7 @@ function createProgram() {
|
|
|
6174
6479
|
registerAmendCommand(program);
|
|
6175
6480
|
registerAmendmentsCommands(program);
|
|
6176
6481
|
registerVerifyCommand(program);
|
|
6482
|
+
registerConfigCommands(program);
|
|
6177
6483
|
program.option("-v, --verbose", "Verbose output").option("--json", "Output as JSON").option("--no-color", "Disable colored output");
|
|
6178
6484
|
program.on("command:*", () => {
|
|
6179
6485
|
console.error(chalk.red(`Unknown command: ${program.args.join(" ")}`));
|
|
@@ -6255,4 +6561,4 @@ export {
|
|
|
6255
6561
|
getFeedback as y,
|
|
6256
6562
|
getReadySteps as z
|
|
6257
6563
|
};
|
|
6258
|
-
//# sourceMappingURL=cli-
|
|
6564
|
+
//# sourceMappingURL=cli-BNnX3BBS.js.map
|