@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 CHANGED
@@ -1,18 +1,38 @@
1
- # riotplan
1
+ # RiotPlan
2
2
 
3
- Framework for long-lived, stateful AI workflows (plans).
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
- ## What is a Plan?
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
- A **plan** is a structured way to manage multi-step AI-assisted tasks that:
20
+ ### After: Plans as Lifecycle
10
21
 
11
- - **Span multiple sessions** - Work on a task over days or weeks
12
- - **Have persistent state** - Track progress in STATUS.md
13
- - **Are organized into steps** - Numbered files (01-STEP.md, 02-STEP.md)
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
- - **Support collaboration** - Human reviews, feedback loops
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
- Create `.riotplanrc.json` in your plan directory:
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
- "defaultProvider": "anthropic",
257
- "autoUpdateStatus": true,
258
- "stepTemplate": "detailed",
259
- "analysis": {
260
- "enabled": true,
261
- "directory": "analysis"
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", "riotplan-mcp"]
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,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { q as createProgram } from "./cli-zlZlG2Bw.js";
2
+ import { q as createProgram } from "./cli-BNnX3BBS.js";
3
3
  const program = createProgram();
4
4
  program.parse();
5
5
  //# sourceMappingURL=bin.js.map
@@ -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-zlZlG2Bw.js.map
6564
+ //# sourceMappingURL=cli-BNnX3BBS.js.map