openplanr 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +23 -5
  2. package/dist/agents/agent-factory.d.ts +7 -0
  3. package/dist/agents/agent-factory.d.ts.map +1 -0
  4. package/dist/agents/agent-factory.js +22 -0
  5. package/dist/agents/agent-factory.js.map +1 -0
  6. package/dist/agents/claude-agent.d.ts +13 -0
  7. package/dist/agents/claude-agent.d.ts.map +1 -0
  8. package/dist/agents/claude-agent.js +48 -0
  9. package/dist/agents/claude-agent.js.map +1 -0
  10. package/dist/agents/codex-agent.d.ts +13 -0
  11. package/dist/agents/codex-agent.d.ts.map +1 -0
  12. package/dist/agents/codex-agent.js +47 -0
  13. package/dist/agents/codex-agent.js.map +1 -0
  14. package/dist/agents/cursor-agent.d.ts +13 -0
  15. package/dist/agents/cursor-agent.d.ts.map +1 -0
  16. package/dist/agents/cursor-agent.js +40 -0
  17. package/dist/agents/cursor-agent.js.map +1 -0
  18. package/dist/agents/implementation-bridge.d.ts +21 -0
  19. package/dist/agents/implementation-bridge.d.ts.map +1 -0
  20. package/dist/agents/implementation-bridge.js +173 -0
  21. package/dist/agents/implementation-bridge.js.map +1 -0
  22. package/dist/agents/index.d.ts +6 -0
  23. package/dist/agents/index.d.ts.map +1 -0
  24. package/dist/agents/index.js +5 -0
  25. package/dist/agents/index.js.map +1 -0
  26. package/dist/agents/prompt-composer.d.ts +30 -0
  27. package/dist/agents/prompt-composer.d.ts.map +1 -0
  28. package/dist/agents/prompt-composer.js +81 -0
  29. package/dist/agents/prompt-composer.js.map +1 -0
  30. package/dist/agents/task-parser.d.ts +38 -0
  31. package/dist/agents/task-parser.d.ts.map +1 -0
  32. package/dist/agents/task-parser.js +83 -0
  33. package/dist/agents/task-parser.js.map +1 -0
  34. package/dist/agents/types.d.ts +23 -0
  35. package/dist/agents/types.d.ts.map +1 -0
  36. package/dist/agents/types.js +8 -0
  37. package/dist/agents/types.js.map +1 -0
  38. package/dist/agents/utils.d.ts +9 -0
  39. package/dist/agents/utils.d.ts.map +1 -0
  40. package/dist/agents/utils.js +21 -0
  41. package/dist/agents/utils.js.map +1 -0
  42. package/dist/ai/codebase/context-builder.d.ts +31 -0
  43. package/dist/ai/codebase/context-builder.d.ts.map +1 -0
  44. package/dist/ai/codebase/context-builder.js +93 -0
  45. package/dist/ai/codebase/context-builder.js.map +1 -0
  46. package/dist/ai/codebase/file-reader.d.ts +22 -0
  47. package/dist/ai/codebase/file-reader.d.ts.map +1 -0
  48. package/dist/ai/codebase/file-reader.js +111 -0
  49. package/dist/ai/codebase/file-reader.js.map +1 -0
  50. package/dist/ai/codebase/index.d.ts +5 -0
  51. package/dist/ai/codebase/index.d.ts.map +1 -0
  52. package/dist/ai/codebase/index.js +5 -0
  53. package/dist/ai/codebase/index.js.map +1 -0
  54. package/dist/ai/codebase/stack-detector.d.ts +18 -0
  55. package/dist/ai/codebase/stack-detector.d.ts.map +1 -0
  56. package/dist/ai/codebase/stack-detector.js +147 -0
  57. package/dist/ai/codebase/stack-detector.js.map +1 -0
  58. package/dist/ai/codebase/tree-generator.d.ts +8 -0
  59. package/dist/ai/codebase/tree-generator.d.ts.map +1 -0
  60. package/dist/ai/codebase/tree-generator.js +85 -0
  61. package/dist/ai/codebase/tree-generator.js.map +1 -0
  62. package/dist/ai/errors.d.ts +22 -0
  63. package/dist/ai/errors.d.ts.map +1 -0
  64. package/dist/ai/errors.js +70 -0
  65. package/dist/ai/errors.js.map +1 -0
  66. package/dist/ai/index.d.ts +11 -0
  67. package/dist/ai/index.d.ts.map +1 -0
  68. package/dist/ai/index.js +10 -0
  69. package/dist/ai/index.js.map +1 -0
  70. package/dist/ai/prompts/prompt-builder.d.ts +42 -0
  71. package/dist/ai/prompts/prompt-builder.d.ts.map +1 -0
  72. package/dist/ai/prompts/prompt-builder.js +96 -0
  73. package/dist/ai/prompts/prompt-builder.js.map +1 -0
  74. package/dist/ai/prompts/system-prompts.d.ts +13 -0
  75. package/dist/ai/prompts/system-prompts.d.ts.map +1 -0
  76. package/dist/ai/prompts/system-prompts.js +124 -0
  77. package/dist/ai/prompts/system-prompts.js.map +1 -0
  78. package/dist/ai/provider-factory.d.ts +10 -0
  79. package/dist/ai/provider-factory.d.ts.map +1 -0
  80. package/dist/ai/provider-factory.js +33 -0
  81. package/dist/ai/provider-factory.js.map +1 -0
  82. package/dist/ai/providers/anthropic-provider.d.ts +22 -0
  83. package/dist/ai/providers/anthropic-provider.d.ts.map +1 -0
  84. package/dist/ai/providers/anthropic-provider.js +82 -0
  85. package/dist/ai/providers/anthropic-provider.js.map +1 -0
  86. package/dist/ai/providers/ollama-provider.d.ts +13 -0
  87. package/dist/ai/providers/ollama-provider.d.ts.map +1 -0
  88. package/dist/ai/providers/ollama-provider.js +16 -0
  89. package/dist/ai/providers/ollama-provider.js.map +1 -0
  90. package/dist/ai/providers/openai-provider.d.ts +17 -0
  91. package/dist/ai/providers/openai-provider.d.ts.map +1 -0
  92. package/dist/ai/providers/openai-provider.js +58 -0
  93. package/dist/ai/providers/openai-provider.js.map +1 -0
  94. package/dist/ai/schemas/ai-response-schemas.d.ts +425 -0
  95. package/dist/ai/schemas/ai-response-schemas.d.ts.map +1 -0
  96. package/dist/ai/schemas/ai-response-schemas.js +87 -0
  97. package/dist/ai/schemas/ai-response-schemas.js.map +1 -0
  98. package/dist/ai/types.d.ts +40 -0
  99. package/dist/ai/types.d.ts.map +1 -0
  100. package/dist/ai/types.js +16 -0
  101. package/dist/ai/types.js.map +1 -0
  102. package/dist/cli/commands/checklist.d.ts.map +1 -1
  103. package/dist/cli/commands/checklist.js +60 -1
  104. package/dist/cli/commands/checklist.js.map +1 -1
  105. package/dist/cli/commands/config.d.ts +8 -0
  106. package/dist/cli/commands/config.d.ts.map +1 -0
  107. package/dist/cli/commands/config.js +112 -0
  108. package/dist/cli/commands/config.js.map +1 -0
  109. package/dist/cli/commands/epic.d.ts +7 -0
  110. package/dist/cli/commands/epic.d.ts.map +1 -1
  111. package/dist/cli/commands/epic.js +161 -31
  112. package/dist/cli/commands/epic.js.map +1 -1
  113. package/dist/cli/commands/feature.d.ts +6 -0
  114. package/dist/cli/commands/feature.d.ts.map +1 -1
  115. package/dist/cli/commands/feature.js +143 -30
  116. package/dist/cli/commands/feature.js.map +1 -1
  117. package/dist/cli/commands/init.d.ts +6 -0
  118. package/dist/cli/commands/init.d.ts.map +1 -1
  119. package/dist/cli/commands/init.js +48 -6
  120. package/dist/cli/commands/init.js.map +1 -1
  121. package/dist/cli/commands/plan.d.ts +15 -0
  122. package/dist/cli/commands/plan.d.ts.map +1 -0
  123. package/dist/cli/commands/plan.js +259 -0
  124. package/dist/cli/commands/plan.js.map +1 -0
  125. package/dist/cli/commands/refine.d.ts +9 -0
  126. package/dist/cli/commands/refine.d.ts.map +1 -0
  127. package/dist/cli/commands/refine.js +101 -0
  128. package/dist/cli/commands/refine.js.map +1 -0
  129. package/dist/cli/commands/status.d.ts.map +1 -1
  130. package/dist/cli/commands/status.js +150 -18
  131. package/dist/cli/commands/status.js.map +1 -1
  132. package/dist/cli/commands/story.d.ts +6 -0
  133. package/dist/cli/commands/story.d.ts.map +1 -1
  134. package/dist/cli/commands/story.js +259 -45
  135. package/dist/cli/commands/story.js.map +1 -1
  136. package/dist/cli/commands/sync.d.ts +12 -0
  137. package/dist/cli/commands/sync.d.ts.map +1 -0
  138. package/dist/cli/commands/sync.js +227 -0
  139. package/dist/cli/commands/sync.js.map +1 -0
  140. package/dist/cli/commands/task.d.ts +9 -0
  141. package/dist/cli/commands/task.d.ts.map +1 -1
  142. package/dist/cli/commands/task.js +350 -35
  143. package/dist/cli/commands/task.js.map +1 -1
  144. package/dist/cli/index.js +16 -2
  145. package/dist/cli/index.js.map +1 -1
  146. package/dist/models/schema.d.ts +41 -0
  147. package/dist/models/schema.d.ts.map +1 -1
  148. package/dist/models/schema.js +9 -0
  149. package/dist/models/schema.js.map +1 -1
  150. package/dist/models/types.d.ts +9 -0
  151. package/dist/models/types.d.ts.map +1 -1
  152. package/dist/services/ai-service.d.ts +40 -0
  153. package/dist/services/ai-service.d.ts.map +1 -0
  154. package/dist/services/ai-service.js +150 -0
  155. package/dist/services/ai-service.js.map +1 -0
  156. package/dist/services/artifact-gathering.d.ts +49 -0
  157. package/dist/services/artifact-gathering.d.ts.map +1 -0
  158. package/dist/services/artifact-gathering.js +128 -0
  159. package/dist/services/artifact-gathering.js.map +1 -0
  160. package/dist/services/artifact-service.d.ts +47 -0
  161. package/dist/services/artifact-service.d.ts.map +1 -1
  162. package/dist/services/artifact-service.js +169 -0
  163. package/dist/services/artifact-service.js.map +1 -1
  164. package/dist/services/checklist-service.d.ts +23 -0
  165. package/dist/services/checklist-service.d.ts.map +1 -1
  166. package/dist/services/checklist-service.js +44 -0
  167. package/dist/services/checklist-service.js.map +1 -1
  168. package/dist/services/credentials-service.d.ts +22 -0
  169. package/dist/services/credentials-service.d.ts.map +1 -0
  170. package/dist/services/credentials-service.js +58 -0
  171. package/dist/services/credentials-service.js.map +1 -0
  172. package/dist/services/id-service.d.ts.map +1 -1
  173. package/dist/services/id-service.js +8 -5
  174. package/dist/services/id-service.js.map +1 -1
  175. package/dist/services/prompt-service.d.ts +6 -0
  176. package/dist/services/prompt-service.d.ts.map +1 -1
  177. package/dist/services/prompt-service.js +7 -1
  178. package/dist/services/prompt-service.js.map +1 -1
  179. package/dist/services/template-service.d.ts.map +1 -1
  180. package/dist/services/template-service.js +6 -0
  181. package/dist/services/template-service.js.map +1 -1
  182. package/dist/templates/checklists/agile-checklist.md.hbs +8 -8
  183. package/dist/templates/epics/epic.md.hbs +8 -2
  184. package/dist/templates/features/feature.md.hbs +3 -3
  185. package/dist/templates/rules/cursor/2001-agile-create-epic.mdc.hbs +1 -1
  186. package/dist/templates/rules/cursor/2002-agile-create-features.mdc.hbs +1 -1
  187. package/dist/templates/rules/cursor/2003-agile-create-user-story.mdc.hbs +1 -1
  188. package/dist/templates/stories/user-story.md.hbs +2 -2
  189. package/dist/templates/tasks/task-list.md.hbs +26 -3
  190. package/dist/utils/logger.d.ts +3 -0
  191. package/dist/utils/logger.d.ts.map +1 -1
  192. package/dist/utils/logger.js +12 -0
  193. package/dist/utils/logger.js.map +1 -1
  194. package/package.json +12 -5
  195. package/dist/templates/templates/adrs/adr-general.md.hbs +0 -46
  196. package/dist/templates/templates/checklists/agile-checklist.md.hbs +0 -49
  197. package/dist/templates/templates/epics/epic.md.hbs +0 -46
  198. package/dist/templates/templates/features/feature.md.hbs +0 -42
  199. package/dist/templates/templates/rules/claude/CLAUDE.md.hbs +0 -63
  200. package/dist/templates/templates/rules/codex/AGENTS.md.hbs +0 -28
  201. package/dist/templates/templates/rules/cursor/2000-agile-checklist.mdc.hbs +0 -33
  202. package/dist/templates/templates/rules/cursor/2001-agile-create-epic.mdc.hbs +0 -35
  203. package/dist/templates/templates/rules/cursor/2002-agile-create-features.mdc.hbs +0 -35
  204. package/dist/templates/templates/rules/cursor/2003-agile-create-user-story.mdc.hbs +0 -31
  205. package/dist/templates/templates/rules/cursor/2100-create-task-list.mdc.hbs +0 -36
  206. package/dist/templates/templates/rules/cursor/2101-implement-task-list.mdc.hbs +0 -28
  207. package/dist/templates/templates/stories/gherkin.feature.hbs +0 -13
  208. package/dist/templates/templates/stories/user-story.md.hbs +0 -28
  209. package/dist/templates/templates/tasks/task-list.md.hbs +0 -24
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `planr config` command group.
3
+ *
4
+ * Manage AI provider settings, API keys, and coding agent preferences.
5
+ */
6
+ import { Command } from 'commander';
7
+ export declare function registerConfigCommand(program: Command): void;
8
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,QAoHrD"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * `planr config` command group.
3
+ *
4
+ * Manage AI provider settings, API keys, and coding agent preferences.
5
+ */
6
+ import { loadConfig, saveConfig } from '../../services/config-service.js';
7
+ import { saveCredential, resolveApiKey } from '../../services/credentials-service.js';
8
+ import { promptSelect, promptSecret } from '../../services/prompt-service.js';
9
+ import { logger } from '../../utils/logger.js';
10
+ export function registerConfigCommand(program) {
11
+ const config = program.command('config').description('Manage Planr configuration');
12
+ config
13
+ .command('show')
14
+ .description('Display current configuration')
15
+ .action(async () => {
16
+ const projectDir = program.opts().projectDir;
17
+ const cfg = await loadConfig(projectDir);
18
+ logger.heading('Planr Configuration');
19
+ console.log(` Project: ${cfg.projectName}`);
20
+ console.log(` Targets: ${cfg.targets.join(', ')}`);
21
+ console.log(` Artifacts: ${cfg.outputPaths.agile}/`);
22
+ if (cfg.ai) {
23
+ console.log('');
24
+ console.log(` AI Provider: ${cfg.ai.provider}`);
25
+ console.log(` AI Model: ${cfg.ai.model || '(default)'}`);
26
+ const key = await resolveApiKey(cfg.ai.provider);
27
+ if (key) {
28
+ const masked = key.slice(0, 8) + '...' + key.slice(-4);
29
+ console.log(` API Key: ${masked}`);
30
+ }
31
+ else {
32
+ console.log(` API Key: (not set)`);
33
+ }
34
+ }
35
+ else {
36
+ console.log('');
37
+ console.log(' AI: Not configured');
38
+ logger.dim(' Run `planr config set-provider <name>` to enable AI.');
39
+ }
40
+ if (cfg.defaultAgent) {
41
+ console.log(` Agent: ${cfg.defaultAgent}`);
42
+ }
43
+ });
44
+ config
45
+ .command('set-provider')
46
+ .description('Set the AI provider')
47
+ .argument('[provider]', 'anthropic, openai, or ollama')
48
+ .action(async (provider) => {
49
+ const projectDir = program.opts().projectDir;
50
+ const cfg = await loadConfig(projectDir);
51
+ const selected = provider ||
52
+ (await promptSelect('AI provider:', [
53
+ { name: 'Anthropic (Claude)', value: 'anthropic' },
54
+ { name: 'OpenAI (GPT-4o)', value: 'openai' },
55
+ { name: 'Ollama (Local)', value: 'ollama' },
56
+ ]));
57
+ cfg.ai = { ...cfg.ai, provider: selected };
58
+ await saveConfig(projectDir, cfg);
59
+ logger.success(`AI provider set to: ${selected}`);
60
+ });
61
+ config
62
+ .command('set-key')
63
+ .description('Store an API key securely')
64
+ .argument('[provider]', 'anthropic or openai')
65
+ .action(async (provider) => {
66
+ const selected = provider ||
67
+ (await promptSelect('Provider:', [
68
+ { name: 'Anthropic', value: 'anthropic' },
69
+ { name: 'OpenAI', value: 'openai' },
70
+ ]));
71
+ const key = await promptSecret(`API key for ${selected}:`);
72
+ if (!key.trim()) {
73
+ logger.error('API key cannot be empty.');
74
+ return;
75
+ }
76
+ await saveCredential(selected, key.trim());
77
+ logger.success(`API key for ${selected} saved to ~/.planr/credentials.json`);
78
+ });
79
+ config
80
+ .command('set-model')
81
+ .description('Set the AI model')
82
+ .argument('<model>', 'model name (e.g., claude-sonnet-4-20250514, gpt-4o, llama3.1)')
83
+ .action(async (model) => {
84
+ const projectDir = program.opts().projectDir;
85
+ const cfg = await loadConfig(projectDir);
86
+ if (!cfg.ai) {
87
+ logger.error('AI not configured. Run `planr config set-provider` first.');
88
+ return;
89
+ }
90
+ cfg.ai.model = model;
91
+ await saveConfig(projectDir, cfg);
92
+ logger.success(`AI model set to: ${model}`);
93
+ });
94
+ config
95
+ .command('set-agent')
96
+ .description('Set the default coding agent for task implementation')
97
+ .argument('[agent]', 'claude, cursor, or codex')
98
+ .action(async (agent) => {
99
+ const projectDir = program.opts().projectDir;
100
+ const cfg = await loadConfig(projectDir);
101
+ const selected = agent ||
102
+ (await promptSelect('Default coding agent:', [
103
+ { name: 'Claude Code CLI', value: 'claude' },
104
+ { name: 'Cursor', value: 'cursor' },
105
+ { name: 'Codex', value: 'codex' },
106
+ ]));
107
+ cfg.defaultAgent = selected;
108
+ await saveConfig(projectDir, cfg);
109
+ logger.success(`Default coding agent set to: ${selected}`);
110
+ });
111
+ }
112
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,10 @@
1
+ /**
2
+ * `planr epic` command group.
3
+ *
4
+ * AI-powered by default: user provides a brief description,
5
+ * AI expands into a full epic. Use --manual for the legacy
6
+ * interactive prompt flow.
7
+ */
1
8
  import { Command } from 'commander';
2
9
  export declare function registerEpicCommand(program: Command): void;
3
10
  //# sourceMappingURL=epic.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"epic.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/epic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QAqEnD"}
1
+ {"version":3,"file":"epic.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/epic.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QA0CnD"}
@@ -1,7 +1,18 @@
1
+ /**
2
+ * `planr epic` command group.
3
+ *
4
+ * AI-powered by default: user provides a brief description,
5
+ * AI expands into a full epic. Use --manual for the legacy
6
+ * interactive prompt flow.
7
+ */
1
8
  import { loadConfig } from '../../services/config-service.js';
2
9
  import { createArtifact, listArtifacts } from '../../services/artifact-service.js';
3
- import { promptText, promptMultiText } from '../../services/prompt-service.js';
10
+ import { isAIConfigured, getAIProvider, generateStreamingJSON } from '../../services/ai-service.js';
11
+ import { promptText, promptMultiText, promptSelect, promptEditor } from '../../services/prompt-service.js';
12
+ import { buildEpicPrompt } from '../../ai/prompts/prompt-builder.js';
13
+ import { aiEpicResponseSchema } from '../../ai/schemas/ai-response-schemas.js';
4
14
  import { logger } from '../../utils/logger.js';
15
+ import chalk from 'chalk';
5
16
  export function registerEpicCommand(program) {
6
17
  const epic = program.command('epic').description('Manage epics');
7
18
  epic
@@ -9,37 +20,20 @@ export function registerEpicCommand(program) {
9
20
  .description('Create a new epic')
10
21
  .option('--title <title>', 'epic title')
11
22
  .option('--owner <owner>', 'epic owner')
23
+ .option('--manual', 'use manual interactive prompts instead of AI')
12
24
  .action(async (opts) => {
13
25
  const projectDir = program.opts().projectDir;
14
26
  const config = await loadConfig(projectDir);
15
- logger.heading('Create Epic');
16
- const title = opts.title || (await promptText('Epic title:'));
17
- const owner = opts.owner || (await promptText('Owner:', config.author));
18
- const businessValue = await promptText('Business value:');
19
- const targetUsers = await promptText('Target users:');
20
- const problemStatement = await promptText('Problem statement:');
21
- const solutionOverview = await promptText('Solution overview:');
22
- const successCriteria = await promptText('Success criteria:');
23
- const keyFeatures = await promptMultiText('Key features', 'comma-separated');
24
- const dependencies = await promptText('Dependencies:', 'None');
25
- const risks = await promptText('Risks:', 'None');
26
- const { id, filePath } = await createArtifact(projectDir, config, 'epic', 'epics/epic.md.hbs', {
27
- title,
28
- owner,
29
- businessValue,
30
- targetUsers,
31
- problemStatement,
32
- solutionOverview,
33
- successCriteria,
34
- keyFeatures,
35
- dependencies,
36
- risks,
37
- featureIds: [],
38
- });
39
- logger.success(`Created epic ${id}: ${title}`);
40
- logger.dim(` ${filePath}`);
41
- logger.dim('');
42
- logger.dim(`Next: planr feature create --epic ${id}`);
27
+ const useAI = !opts.manual && isAIConfigured(config);
28
+ if (useAI) {
29
+ await createEpicWithAI(projectDir, config, opts);
30
+ }
31
+ else {
32
+ if (!opts.manual && !isAIConfigured(config)) {
33
+ logger.warn('AI not configured. Using manual mode. Run `planr config set-provider` to enable AI.');
34
+ }
35
+ await createEpicManually(projectDir, config, opts);
36
+ }
43
37
  });
44
38
  epic
45
39
  .command('list')
@@ -53,9 +47,145 @@ export function registerEpicCommand(program) {
53
47
  return;
54
48
  }
55
49
  logger.heading('Epics');
56
- for (const epic of epics) {
57
- console.log(` ${epic.id} ${epic.title}`);
50
+ for (const e of epics) {
51
+ console.log(` ${e.id} ${e.title}`);
52
+ }
53
+ });
54
+ }
55
+ function displayEpic(epicData) {
56
+ console.log(chalk.dim('━'.repeat(50)));
57
+ console.log(chalk.bold(` Title: ${epicData.title}`));
58
+ console.log(` Owner: ${epicData.owner}`);
59
+ console.log(` Business Value: ${epicData.businessValue}`);
60
+ console.log(` Target Users: ${epicData.targetUsers}`);
61
+ console.log(` Problem: ${epicData.problemStatement}`);
62
+ console.log(` Solution: ${epicData.solutionOverview}`);
63
+ console.log(` Success Criteria:`);
64
+ const criteria = Array.isArray(epicData.successCriteria) ? epicData.successCriteria : [epicData.successCriteria];
65
+ for (const c of criteria) {
66
+ console.log(` • ${c}`);
67
+ }
68
+ console.log(` Key Features:`);
69
+ for (const f of epicData.keyFeatures) {
70
+ console.log(` • ${f}`);
71
+ }
72
+ console.log(` Dependencies: ${epicData.dependencies || 'None'}`);
73
+ console.log(` Risks: ${epicData.risks || 'None'}`);
74
+ console.log(chalk.dim('━'.repeat(50)));
75
+ }
76
+ async function createEpicWithAI(projectDir, config, opts) {
77
+ logger.heading('Create Epic (AI-powered)');
78
+ const brief = opts.title || await promptText('Describe your epic in a sentence or two:');
79
+ // Get existing epics for deduplication
80
+ const existingEpics = await listArtifacts(projectDir, config, 'epic');
81
+ const existingTitles = existingEpics.map((e) => `${e.id}: ${e.title}`);
82
+ logger.dim('AI is generating your epic...');
83
+ try {
84
+ const provider = await getAIProvider(config);
85
+ const messages = buildEpicPrompt(brief, existingTitles);
86
+ let epicData = await generateStreamingJSON(provider, messages, aiEpicResponseSchema);
87
+ displayEpic(epicData);
88
+ // Action loop: save, edit, regenerate, cancel
89
+ let saved = false;
90
+ while (!saved) {
91
+ const action = await promptSelect('Action:', [
92
+ { name: 'Save this epic', value: 'save' },
93
+ { name: 'Edit before saving', value: 'edit' },
94
+ { name: 'Regenerate', value: 'regenerate' },
95
+ { name: 'Cancel', value: 'cancel' },
96
+ ]);
97
+ if (action === 'cancel') {
98
+ logger.info('Epic creation cancelled.');
99
+ return;
100
+ }
101
+ if (action === 'regenerate') {
102
+ logger.dim('Regenerating...');
103
+ epicData = await generateStreamingJSON(provider, messages, aiEpicResponseSchema);
104
+ displayEpic(epicData);
105
+ continue;
106
+ }
107
+ if (action === 'edit') {
108
+ const editContent = JSON.stringify(epicData, null, 2);
109
+ const edited = await promptEditor('Edit the epic JSON (save & close to apply):', editContent);
110
+ try {
111
+ const parsed = JSON.parse(edited);
112
+ const validated = aiEpicResponseSchema.parse(parsed);
113
+ epicData = validated;
114
+ displayEpic(epicData);
115
+ continue;
116
+ }
117
+ catch (err) {
118
+ logger.error('Invalid JSON after edit. Please try again.');
119
+ continue;
120
+ }
121
+ }
122
+ // Save
123
+ saved = true;
124
+ }
125
+ const criteriaArray = Array.isArray(epicData.successCriteria)
126
+ ? epicData.successCriteria
127
+ : [epicData.successCriteria];
128
+ const templateData = {
129
+ ...epicData,
130
+ successCriteria: criteriaArray.join('; '),
131
+ successCriteriaList: criteriaArray,
132
+ featureIds: [],
133
+ };
134
+ const { id, filePath } = await createArtifact(projectDir, config, 'epic', 'epics/epic.md.hbs', templateData);
135
+ logger.success(`Created epic ${id}: ${epicData.title}`);
136
+ logger.dim(` ${filePath}`);
137
+ logger.dim('');
138
+ logger.heading('Next steps:');
139
+ logger.dim(` 1. planr feature create --epic ${id} — Break epic into features`);
140
+ logger.dim(` 2. planr story create --feature FEAT-* — Create user stories per feature`);
141
+ logger.dim(` 3. planr task create --story US-* — Generate implementation tasks`);
142
+ logger.dim(` 4. planr task implement TASK-* — Implement with your coding agent`);
143
+ logger.dim('');
144
+ logger.dim(` Or run the full flow at once:`);
145
+ logger.dim(` planr plan --epic ${id} — Auto-generate features → stories → tasks`);
146
+ }
147
+ catch (err) {
148
+ const { AIError } = await import('../../ai/errors.js');
149
+ if (err instanceof AIError) {
150
+ logger.error(err.userMessage);
58
151
  }
152
+ else {
153
+ throw err;
154
+ }
155
+ }
156
+ }
157
+ async function createEpicManually(projectDir, config, opts) {
158
+ logger.heading('Create Epic');
159
+ const title = opts.title || (await promptText('Epic title:'));
160
+ const owner = opts.owner || (await promptText('Owner:', config.author));
161
+ const businessValue = await promptText('Business value:');
162
+ const targetUsers = await promptText('Target users:');
163
+ const problemStatement = await promptText('Problem statement:');
164
+ const solutionOverview = await promptText('Solution overview:');
165
+ const successCriteria = await promptText('Success criteria:');
166
+ const keyFeatures = await promptMultiText('Key features', 'comma-separated');
167
+ const dependencies = await promptText('Dependencies:', 'None');
168
+ const risks = await promptText('Risks:', 'None');
169
+ const { id, filePath } = await createArtifact(projectDir, config, 'epic', 'epics/epic.md.hbs', {
170
+ title,
171
+ owner,
172
+ businessValue,
173
+ targetUsers,
174
+ problemStatement,
175
+ solutionOverview,
176
+ successCriteria,
177
+ keyFeatures,
178
+ dependencies,
179
+ risks,
180
+ featureIds: [],
59
181
  });
182
+ logger.success(`Created epic ${id}: ${title}`);
183
+ logger.dim(` ${filePath}`);
184
+ logger.dim('');
185
+ logger.heading('Next steps:');
186
+ logger.dim(` 1. planr feature create --epic ${id} — Break epic into features`);
187
+ logger.dim(` 2. planr story create --feature FEAT-* — Create user stories per feature`);
188
+ logger.dim(` 3. planr task create --story US-* — Generate implementation tasks`);
189
+ logger.dim(` 4. planr task implement TASK-* — Implement with your coding agent`);
60
190
  }
61
191
  //# sourceMappingURL=epic.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"epic.js","sourceRoot":"","sources":["../../../src/cli/commands/epic.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"epic.js","sourceRoot":"","sources":["../../../src/cli/commands/epic.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,9 @@
1
+ /**
2
+ * `planr feature` command group.
3
+ *
4
+ * AI-powered by default: reads the parent epic and generates
5
+ * multiple features automatically. Use --manual for legacy mode.
6
+ */
1
7
  import { Command } from 'commander';
2
8
  export declare function registerFeatureCommand(program: Command): void;
3
9
  //# sourceMappingURL=feature.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/feature.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,QA6EtD"}
1
+ {"version":3,"file":"feature.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/feature.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,QAmDtD"}
@@ -1,54 +1,50 @@
1
+ /**
2
+ * `planr feature` command group.
3
+ *
4
+ * AI-powered by default: reads the parent epic and generates
5
+ * multiple features automatically. Use --manual for legacy mode.
6
+ */
1
7
  import { loadConfig } from '../../services/config-service.js';
2
- import { createArtifact, listArtifacts, readArtifact } from '../../services/artifact-service.js';
3
- import { promptText, promptMultiText } from '../../services/prompt-service.js';
8
+ import { createArtifact, listArtifacts, readArtifact, readArtifactRaw, resolveArtifactFilename, addChildReference, } from '../../services/artifact-service.js';
9
+ import { isAIConfigured, getAIProvider, generateStreamingJSON } from '../../services/ai-service.js';
10
+ import { promptText, promptMultiText, promptConfirm } from '../../services/prompt-service.js';
11
+ import { buildFeaturesPrompt } from '../../ai/prompts/prompt-builder.js';
12
+ import { aiFeaturesResponseSchema } from '../../ai/schemas/ai-response-schemas.js';
4
13
  import { logger } from '../../utils/logger.js';
14
+ import chalk from 'chalk';
5
15
  export function registerFeatureCommand(program) {
6
16
  const feature = program.command('feature').description('Manage features');
7
17
  feature
8
18
  .command('create')
9
- .description('Create a new feature from an epic')
19
+ .description('Create features from an epic')
10
20
  .requiredOption('--epic <epicId>', 'parent epic ID (e.g., EPIC-001)')
11
21
  .option('--title <title>', 'feature title')
22
+ .option('--count <n>', 'number of features to generate', parseInt)
23
+ .option('--manual', 'use manual interactive prompts instead of AI')
12
24
  .action(async (opts) => {
13
25
  const projectDir = program.opts().projectDir;
14
26
  const config = await loadConfig(projectDir);
15
- // Verify epic exists
16
27
  const epicData = await readArtifact(projectDir, config, 'epic', opts.epic);
17
28
  if (!epicData) {
18
29
  logger.error(`Epic ${opts.epic} not found.`);
19
30
  process.exit(1);
20
31
  }
21
- logger.heading(`Create Feature (from ${opts.epic})`);
22
- const title = opts.title || (await promptText('Feature title:'));
23
- const owner = await promptText('Owner:', config.author);
24
- const overview = await promptText('Overview:');
25
- const functionalRequirements = await promptMultiText('Functional requirements', 'comma-separated');
26
- const dependencies = await promptText('Dependencies:', 'None');
27
- const technicalConsiderations = await promptText('Technical considerations:', 'None');
28
- const risks = await promptText('Risks:', 'None');
29
- const successMetrics = await promptText('Success metrics:');
30
- const { id, filePath } = await createArtifact(projectDir, config, 'feature', 'features/feature.md.hbs', {
31
- title,
32
- epicId: opts.epic,
33
- owner,
34
- overview,
35
- functionalRequirements,
36
- dependencies,
37
- technicalConsiderations,
38
- risks,
39
- successMetrics,
40
- storyIds: [],
41
- });
42
- logger.success(`Created feature ${id}: ${title}`);
43
- logger.dim(` ${filePath}`);
44
- logger.dim('');
45
- logger.dim(`Next: planr story create --feature ${id}`);
32
+ const useAI = !opts.manual && isAIConfigured(config);
33
+ if (useAI) {
34
+ await createFeaturesWithAI(projectDir, config, opts.epic, opts.count);
35
+ }
36
+ else {
37
+ if (!opts.manual && !isAIConfigured(config)) {
38
+ logger.warn('AI not configured. Using manual mode.');
39
+ }
40
+ await createFeatureManually(projectDir, config, opts);
41
+ }
46
42
  });
47
43
  feature
48
44
  .command('list')
49
45
  .description('List features')
50
46
  .option('--epic <epicId>', 'filter by epic ID')
51
- .action(async (opts) => {
47
+ .action(async () => {
52
48
  const projectDir = program.opts().projectDir;
53
49
  const config = await loadConfig(projectDir);
54
50
  const features = await listArtifacts(projectDir, config, 'feature');
@@ -62,4 +58,121 @@ export function registerFeatureCommand(program) {
62
58
  }
63
59
  });
64
60
  }
61
+ async function createFeaturesWithAI(projectDir, config, epicId, featureCount) {
62
+ logger.heading(`Create Features (AI-powered from ${epicId})`);
63
+ const epicRaw = await readArtifactRaw(projectDir, config, 'epic', epicId);
64
+ if (!epicRaw) {
65
+ logger.error(`Could not read epic ${epicId}.`);
66
+ return;
67
+ }
68
+ // Find existing features for THIS epic (for dedup + warning)
69
+ const allFeatures = await listArtifacts(projectDir, config, 'feature');
70
+ const epicFeatures = [];
71
+ for (const f of allFeatures) {
72
+ const data = await readArtifact(projectDir, config, 'feature', f.id);
73
+ if (data && data.data.epicId === epicId) {
74
+ epicFeatures.push({ id: f.id, title: data.data.title || f.title });
75
+ }
76
+ }
77
+ if (epicFeatures.length > 0) {
78
+ logger.warn(`${epicId} already has ${epicFeatures.length} feature(s):`);
79
+ for (const f of epicFeatures) {
80
+ console.log(chalk.dim(` ${f.id}: ${f.title}`));
81
+ }
82
+ const continueCreate = await promptConfirm('Generate additional features? (AI will avoid duplicates)', false);
83
+ if (!continueCreate) {
84
+ logger.info('Feature creation cancelled.');
85
+ return;
86
+ }
87
+ }
88
+ const existingTitles = epicFeatures.map((f) => `${f.id}: ${f.title}`);
89
+ logger.dim('AI is generating features from the epic...');
90
+ try {
91
+ const provider = await getAIProvider(config);
92
+ const messages = buildFeaturesPrompt(epicRaw, existingTitles, featureCount);
93
+ const result = await generateStreamingJSON(provider, messages, aiFeaturesResponseSchema);
94
+ // Display generated features
95
+ console.log(chalk.dim('━'.repeat(50)));
96
+ result.features.forEach((feat, i) => {
97
+ console.log(chalk.bold(` ${i + 1}. ${feat.title}`));
98
+ console.log(chalk.dim(` ${feat.overview}`));
99
+ console.log(` Requirements: ${feat.functionalRequirements.length} items`);
100
+ });
101
+ console.log(chalk.dim('━'.repeat(50)));
102
+ const confirmAll = await promptConfirm(`Create all ${result.features.length} features?`, true);
103
+ if (!confirmAll) {
104
+ logger.info('Feature creation cancelled.');
105
+ return;
106
+ }
107
+ // Create each feature
108
+ const createdIds = [];
109
+ const epicFilename = await resolveArtifactFilename(projectDir, config, 'epic', epicId);
110
+ for (const feat of result.features) {
111
+ const { id, filePath } = await createArtifact(projectDir, config, 'feature', 'features/feature.md.hbs', {
112
+ title: feat.title,
113
+ epicId,
114
+ epicFilename,
115
+ owner: config.author || 'Engineering',
116
+ overview: feat.overview,
117
+ functionalRequirements: feat.functionalRequirements,
118
+ dependencies: feat.dependencies,
119
+ technicalConsiderations: feat.technicalConsiderations,
120
+ risks: feat.risks,
121
+ successMetrics: feat.successMetrics,
122
+ storyIds: [],
123
+ });
124
+ createdIds.push(id);
125
+ await addChildReference(projectDir, config, 'epic', epicId, 'feature', id, feat.title);
126
+ logger.success(`Created ${id}: ${feat.title}`);
127
+ logger.dim(` ${filePath}`);
128
+ }
129
+ logger.dim('');
130
+ logger.heading('Next steps:');
131
+ logger.dim(` 1. planr story create --feature ${createdIds[0]} — Create user stories`);
132
+ logger.dim(` 2. planr task create --story US-* — Generate implementation tasks`);
133
+ logger.dim(` 3. planr task implement TASK-* — Implement with your coding agent`);
134
+ logger.dim('');
135
+ logger.dim(` Or generate stories for all features at once:`);
136
+ logger.dim(` planr plan --epic ${epicId} — Auto-generate stories → tasks`);
137
+ }
138
+ catch (err) {
139
+ const { AIError } = await import('../../ai/errors.js');
140
+ if (err instanceof AIError) {
141
+ logger.error(err.userMessage);
142
+ }
143
+ else {
144
+ throw err;
145
+ }
146
+ }
147
+ }
148
+ async function createFeatureManually(projectDir, config, opts) {
149
+ logger.heading(`Create Feature (from ${opts.epic})`);
150
+ const title = opts.title || (await promptText('Feature title:'));
151
+ const owner = await promptText('Owner:', config.author);
152
+ const overview = await promptText('Overview:');
153
+ const functionalRequirements = await promptMultiText('Functional requirements', 'comma-separated');
154
+ const dependencies = await promptText('Dependencies:', 'None');
155
+ const technicalConsiderations = await promptText('Technical considerations:', 'None');
156
+ const risks = await promptText('Risks:', 'None');
157
+ const successMetrics = await promptText('Success metrics:');
158
+ const epicFilename = await resolveArtifactFilename(projectDir, config, 'epic', opts.epic);
159
+ const { id, filePath } = await createArtifact(projectDir, config, 'feature', 'features/feature.md.hbs', {
160
+ title,
161
+ epicId: opts.epic,
162
+ epicFilename,
163
+ owner,
164
+ overview,
165
+ functionalRequirements,
166
+ dependencies,
167
+ technicalConsiderations,
168
+ risks,
169
+ successMetrics,
170
+ storyIds: [],
171
+ });
172
+ await addChildReference(projectDir, config, 'epic', opts.epic, 'feature', id, title);
173
+ logger.success(`Created feature ${id}: ${title}`);
174
+ logger.dim(` ${filePath}`);
175
+ logger.dim('');
176
+ logger.dim(`Next: planr story create --feature ${id}`);
177
+ }
65
178
  //# sourceMappingURL=feature.js.map