@dedesfr/prompter 0.9.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +105 -77
  3. package/dist/cli/index.js +25 -1
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +32 -9
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/login.d.ts +4 -0
  9. package/dist/commands/login.d.ts.map +1 -0
  10. package/dist/commands/login.js +56 -0
  11. package/dist/commands/login.js.map +1 -0
  12. package/dist/commands/logout.d.ts +4 -0
  13. package/dist/commands/logout.d.ts.map +1 -0
  14. package/dist/commands/logout.js +14 -0
  15. package/dist/commands/logout.js.map +1 -0
  16. package/dist/commands/update.d.ts.map +1 -1
  17. package/dist/commands/update.js +18 -5
  18. package/dist/commands/update.js.map +1 -1
  19. package/dist/commands/whoami.d.ts +4 -0
  20. package/dist/commands/whoami.d.ts.map +1 -0
  21. package/dist/commands/whoami.js +42 -0
  22. package/dist/commands/whoami.js.map +1 -0
  23. package/dist/core/auth-store.d.ts +10 -0
  24. package/dist/core/auth-store.d.ts.map +1 -0
  25. package/dist/core/auth-store.js +39 -0
  26. package/dist/core/auth-store.js.map +1 -0
  27. package/dist/core/registry.d.ts +18 -0
  28. package/dist/core/registry.d.ts.map +1 -0
  29. package/dist/core/registry.js +94 -0
  30. package/dist/core/registry.js.map +1 -0
  31. package/package.json +7 -1
  32. package/AGENTS.md +0 -123
  33. package/CLAUDE.md +0 -17
  34. package/build.js +0 -20
  35. package/convex-setup.md +0 -403
  36. package/prompt/ai-humanizer.md +0 -45
  37. package/prompt/api-contract-generator.md +0 -234
  38. package/prompt/apply.md +0 -17
  39. package/prompt/archive.md +0 -21
  40. package/prompt/design-system.md +0 -210
  41. package/prompt/document-explainer.md +0 -149
  42. package/prompt/epic-generator.md +0 -198
  43. package/prompt/epic-single.md +0 -47
  44. package/prompt/erd-generator.md +0 -130
  45. package/prompt/fsd-generator.md +0 -157
  46. package/prompt/prd-agent-generator.md +0 -147
  47. package/prompt/prd-generator.md +0 -195
  48. package/prompt/product-brief.md +0 -289
  49. package/prompt/proposal.md +0 -22
  50. package/prompt/qa-test-scenario.md +0 -133
  51. package/prompt/skill-creator.md +0 -350
  52. package/prompt/story-generator.md +0 -278
  53. package/prompt/story-single.md +0 -70
  54. package/prompt/tdd-generator.md +0 -294
  55. package/prompt/tdd-lite-generator.md +0 -224
  56. package/prompt/wireframe-generator.md +0 -219
  57. package/skills/ai-context-generator/SKILL.md +0 -54
  58. package/skills/ai-context-generator/references/AGENTS.template.md +0 -83
  59. package/skills/ai-context-generator/references/CLAUDE.template.md +0 -39
  60. package/skills/ai-context-generator/references/behavioral-guidelines.md +0 -71
  61. package/skills/ai-context-generator/references/discovery-checklist.md +0 -40
  62. package/skills/ai-context-generator/references/examples/AGENTS.good.md +0 -103
  63. package/skills/ai-context-generator/references/extraction-checklist.md +0 -23
  64. package/skills/ai-context-generator/references/overlays/laravel.md +0 -44
  65. package/skills/ai-humanizer/SKILL.md +0 -50
  66. package/skills/api-contract-generator/SKILL.md +0 -243
  67. package/skills/apply/SKILL.md +0 -23
  68. package/skills/archive/SKILL.md +0 -27
  69. package/skills/cerebro/SKILL.md +0 -187
  70. package/skills/cerebro/references/agents.md +0 -213
  71. package/skills/code-review/SKILL.md +0 -373
  72. package/skills/code-review/assets/report-template-agent.md +0 -212
  73. package/skills/code-review/assets/report-template-compact.md +0 -81
  74. package/skills/code-review/assets/report-template-full.md +0 -264
  75. package/skills/code-review/assets/report-template-human.md +0 -168
  76. package/skills/code-review/references/universal-patterns.md +0 -495
  77. package/skills/design-md/README.md +0 -34
  78. package/skills/design-md/SKILL.md +0 -172
  79. package/skills/design-md/examples/DESIGN.md +0 -154
  80. package/skills/design-system/SKILL.md +0 -216
  81. package/skills/design-system-generator/SKILL.md +0 -324
  82. package/skills/design-system-generator/assets/design-system-template.md +0 -348
  83. package/skills/design-system-generator/references/extraction-patterns.md +0 -321
  84. package/skills/doc-builder/SKILL.md +0 -115
  85. package/skills/doc-builder/references/ui-patterns.md +0 -394
  86. package/skills/document-explainer/SKILL.md +0 -155
  87. package/skills/document-translator/SKILL.md +0 -58
  88. package/skills/enhance/SKILL.md +0 -47
  89. package/skills/enhance-prompt/README.md +0 -34
  90. package/skills/enhance-prompt/SKILL.md +0 -204
  91. package/skills/enhance-prompt/references/KEYWORDS.md +0 -114
  92. package/skills/epic-generator/SKILL.md +0 -204
  93. package/skills/epic-single/SKILL.md +0 -63
  94. package/skills/erd-generator/SKILL.md +0 -138
  95. package/skills/feature-planner/SKILL.md +0 -305
  96. package/skills/feature-planner/assets/implementation-plan-template.md +0 -85
  97. package/skills/frontend-design/LICENSE.txt +0 -177
  98. package/skills/frontend-design/SKILL.md +0 -42
  99. package/skills/fsd-generator/SKILL.md +0 -163
  100. package/skills/gamma-builder/SKILL.md +0 -134
  101. package/skills/laravel-code-review/SKILL.md +0 -383
  102. package/skills/laravel-code-review/assets/report-template-agent.md +0 -195
  103. package/skills/laravel-code-review/assets/report-template-compact.md +0 -79
  104. package/skills/laravel-code-review/assets/report-template-full.md +0 -253
  105. package/skills/laravel-code-review/assets/report-template-human.md +0 -159
  106. package/skills/laravel-code-review/references/laravel-patterns.md +0 -571
  107. package/skills/laravel-code-review/references/php84-features.md +0 -442
  108. package/skills/mcp-builder/LICENSE.txt +0 -202
  109. package/skills/mcp-builder/SKILL.md +0 -236
  110. package/skills/mcp-builder/reference/evaluation.md +0 -602
  111. package/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  112. package/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  113. package/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  114. package/skills/mcp-builder/scripts/connections.py +0 -151
  115. package/skills/mcp-builder/scripts/evaluation.py +0 -373
  116. package/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  117. package/skills/mcp-builder/scripts/requirements.txt +0 -2
  118. package/skills/meeting-notes/SKILL.md +0 -159
  119. package/skills/meeting-notes/evals/evals.json +0 -23
  120. package/skills/prd-agent-generator/SKILL.md +0 -132
  121. package/skills/prd-generator/SKILL.md +0 -211
  122. package/skills/product-brief/SKILL.md +0 -141
  123. package/skills/project-orchestrator/SKILL.md +0 -487
  124. package/skills/project-orchestrator/assets/caddy-vps-setup.md +0 -180
  125. package/skills/project-orchestrator/assets/plan-summary-template.md +0 -159
  126. package/skills/prompter-specs/SKILL.md +0 -115
  127. package/skills/prompter-workflow/SKILL.md +0 -166
  128. package/skills/prompter-workflow/evals/evals.json +0 -89
  129. package/skills/proposal/SKILL.md +0 -28
  130. package/skills/qa-test-scenario/SKILL.md +0 -149
  131. package/skills/skill-creator/SKILL.md +0 -173
  132. package/skills/sph-generator/SKILL.md +0 -488
  133. package/skills/story-generator/SKILL.md +0 -285
  134. package/skills/story-single/SKILL.md +0 -86
  135. package/skills/tdd-generator/SKILL.md +0 -300
  136. package/skills/tdd-lite-generator/SKILL.md +0 -230
  137. package/skills/ui-ux-pro/SKILL.md +0 -199
  138. package/skills/ui-ux-pro/assets/design-spec-template.md +0 -173
  139. package/skills/ui-ux-pro/references/component-patterns.md +0 -255
  140. package/skills/ui-ux-pro/references/design-principles.md +0 -167
  141. package/skills/wireframe-generator/SKILL.md +0 -227
  142. package/src/cli/index.ts +0 -223
  143. package/src/commands/archive.ts +0 -302
  144. package/src/commands/change.ts +0 -292
  145. package/src/commands/config.ts +0 -233
  146. package/src/commands/guide.ts +0 -50
  147. package/src/commands/init.ts +0 -597
  148. package/src/commands/list.ts +0 -194
  149. package/src/commands/show.ts +0 -138
  150. package/src/commands/spec.ts +0 -251
  151. package/src/commands/update.ts +0 -129
  152. package/src/commands/upgrade.ts +0 -30
  153. package/src/commands/validate.ts +0 -326
  154. package/src/core/artifact-graph/graph.ts +0 -167
  155. package/src/core/artifact-graph/index.ts +0 -44
  156. package/src/core/artifact-graph/instruction-loader.ts +0 -302
  157. package/src/core/artifact-graph/resolver.ts +0 -226
  158. package/src/core/artifact-graph/schema.ts +0 -124
  159. package/src/core/artifact-graph/state.ts +0 -64
  160. package/src/core/artifact-graph/types.ts +0 -65
  161. package/src/core/completions/command-registry.ts +0 -382
  162. package/src/core/completions/completion-provider.ts +0 -128
  163. package/src/core/completions/generators/bash-generator.ts +0 -191
  164. package/src/core/completions/generators/fish-generator.ts +0 -188
  165. package/src/core/completions/generators/powershell-generator.ts +0 -223
  166. package/src/core/completions/generators/zsh-generator.ts +0 -281
  167. package/src/core/completions/templates/bash-templates.ts +0 -24
  168. package/src/core/completions/templates/fish-templates.ts +0 -40
  169. package/src/core/completions/templates/powershell-templates.ts +0 -25
  170. package/src/core/completions/templates/zsh-templates.ts +0 -36
  171. package/src/core/completions/types.ts +0 -90
  172. package/src/core/config-schema.ts +0 -230
  173. package/src/core/config.ts +0 -181
  174. package/src/core/configurators/slash/antigravity.ts +0 -10
  175. package/src/core/configurators/slash/base.ts +0 -109
  176. package/src/core/configurators/slash/claude.ts +0 -10
  177. package/src/core/configurators/slash/codex.ts +0 -10
  178. package/src/core/configurators/slash/droid.ts +0 -10
  179. package/src/core/configurators/slash/forge.ts +0 -10
  180. package/src/core/configurators/slash/github-copilot.ts +0 -10
  181. package/src/core/configurators/slash/index.ts +0 -10
  182. package/src/core/configurators/slash/kilocode.ts +0 -10
  183. package/src/core/configurators/slash/opencode.ts +0 -10
  184. package/src/core/configurators/slash/registry.ts +0 -51
  185. package/src/core/converters/json-converter.ts +0 -62
  186. package/src/core/global-config.ts +0 -136
  187. package/src/core/parsers/change-parser.ts +0 -234
  188. package/src/core/parsers/markdown-parser.ts +0 -237
  189. package/src/core/parsers/requirement-blocks.ts +0 -234
  190. package/src/core/prompt-templates.ts +0 -3504
  191. package/src/core/schemas/base.schema.ts +0 -20
  192. package/src/core/schemas/change.schema.ts +0 -42
  193. package/src/core/schemas/index.ts +0 -20
  194. package/src/core/schemas/spec.schema.ts +0 -17
  195. package/src/core/skill-discovery.ts +0 -68
  196. package/src/core/specs-apply.ts +0 -483
  197. package/src/core/styles/palette.ts +0 -8
  198. package/src/core/templates/agents-template.ts +0 -459
  199. package/src/core/templates/claude-template.ts +0 -2
  200. package/src/core/templates/index.ts +0 -3
  201. package/src/core/templates/project-template.ts +0 -32
  202. package/src/core/validation/constants.ts +0 -48
  203. package/src/core/validation/types.ts +0 -19
  204. package/src/core/validation/validator.ts +0 -449
  205. package/src/core/view.ts +0 -219
  206. package/src/index.ts +0 -1
  207. package/src/utils/change-metadata.ts +0 -171
  208. package/src/utils/change-utils.ts +0 -131
  209. package/src/utils/file-system.ts +0 -252
  210. package/src/utils/index.ts +0 -12
  211. package/src/utils/interactive.ts +0 -29
  212. package/src/utils/item-discovery.ts +0 -66
  213. package/src/utils/match.ts +0 -26
  214. package/src/utils/shell-detection.ts +0 -62
  215. package/src/utils/task-progress.ts +0 -43
  216. package/tsconfig.json +0 -28
@@ -1,292 +0,0 @@
1
- import { promises as fs } from 'fs';
2
- import path from 'path';
3
- import { JsonConverter } from '../core/converters/json-converter.js';
4
- import { Validator } from '../core/validation/validator.js';
5
- import { ChangeParser } from '../core/parsers/change-parser.js';
6
- import { Change } from '../core/schemas/index.js';
7
- import { isInteractive } from '../utils/interactive.js';
8
- import { getActiveChangeIds } from '../utils/item-discovery.js';
9
-
10
- // Constants for better maintainability
11
- const ARCHIVE_DIR = 'archive';
12
- const TASK_PATTERN = /^[-*]\s+\[[\sx]\]/i;
13
- const COMPLETED_TASK_PATTERN = /^[-*]\s+\[x\]/i;
14
-
15
- export class ChangeCommand {
16
- private converter: JsonConverter;
17
-
18
- constructor() {
19
- this.converter = new JsonConverter();
20
- }
21
-
22
- /**
23
- * Show a change proposal.
24
- * - Text mode: raw markdown passthrough (no filters)
25
- * - JSON mode: minimal object with deltas; --deltas-only returns same object with filtered deltas
26
- * Note: --requirements-only is deprecated alias for --deltas-only
27
- */
28
- async show(changeName?: string, options?: { json?: boolean; requirementsOnly?: boolean; deltasOnly?: boolean; noInteractive?: boolean }): Promise<void> {
29
- const changesPath = path.join(process.cwd(), 'prompter', 'changes');
30
-
31
- if (!changeName) {
32
- const canPrompt = isInteractive(options);
33
- const changes = await this.getActiveChanges(changesPath);
34
- if (canPrompt && changes.length > 0) {
35
- const { select } = await import('@inquirer/prompts');
36
- const selected = await select({
37
- message: 'Select a change to show',
38
- choices: changes.map(id => ({ name: id, value: id })),
39
- });
40
- changeName = selected;
41
- } else {
42
- if (changes.length === 0) {
43
- console.error('No change specified. No active changes found.');
44
- } else {
45
- console.error(`No change specified. Available IDs: ${changes.join(', ')}`);
46
- }
47
- console.error('Hint: use "prompter change list" to view available changes.');
48
- process.exitCode = 1;
49
- return;
50
- }
51
- }
52
-
53
- const proposalPath = path.join(changesPath, changeName, 'proposal.md');
54
-
55
- try {
56
- await fs.access(proposalPath);
57
- } catch {
58
- throw new Error(`Change "${changeName}" not found at ${proposalPath}`);
59
- }
60
-
61
- if (options?.json) {
62
- const jsonOutput = await this.converter.convertChangeToJson(proposalPath);
63
-
64
- if (options.requirementsOnly) {
65
- console.error('Flag --requirements-only is deprecated; use --deltas-only instead.');
66
- }
67
-
68
- const parsed: Change = JSON.parse(jsonOutput);
69
- const contentForTitle = await fs.readFile(proposalPath, 'utf-8');
70
- const title = this.extractTitle(contentForTitle, changeName);
71
- const id = parsed.name;
72
- const deltas = parsed.deltas || [];
73
-
74
- if (options.requirementsOnly || options.deltasOnly) {
75
- const output = { id, title, deltaCount: deltas.length, deltas };
76
- console.log(JSON.stringify(output, null, 2));
77
- } else {
78
- const output = {
79
- id,
80
- title,
81
- deltaCount: deltas.length,
82
- deltas,
83
- };
84
- console.log(JSON.stringify(output, null, 2));
85
- }
86
- } else {
87
- const content = await fs.readFile(proposalPath, 'utf-8');
88
- console.log(content);
89
- }
90
- }
91
-
92
- /**
93
- * List active changes.
94
- * - Text default: IDs only; --long prints minimal details (title, counts)
95
- * - JSON: array of { id, title, deltaCount, taskStatus }, sorted by id
96
- */
97
- async list(options?: { json?: boolean; long?: boolean }): Promise<void> {
98
- const changesPath = path.join(process.cwd(), 'prompter', 'changes');
99
-
100
- const changes = await this.getActiveChanges(changesPath);
101
-
102
- if (options?.json) {
103
- const changeDetails = await Promise.all(
104
- changes.map(async (changeName) => {
105
- const proposalPath = path.join(changesPath, changeName, 'proposal.md');
106
- const tasksPath = path.join(changesPath, changeName, 'tasks.md');
107
-
108
- try {
109
- const content = await fs.readFile(proposalPath, 'utf-8');
110
- const changeDir = path.join(changesPath, changeName);
111
- const parser = new ChangeParser(content, changeDir);
112
- const change = await parser.parseChangeWithDeltas(changeName);
113
-
114
- let taskStatus = { total: 0, completed: 0 };
115
- try {
116
- const tasksContent = await fs.readFile(tasksPath, 'utf-8');
117
- taskStatus = this.countTasks(tasksContent);
118
- } catch (error) {
119
- // Tasks file may not exist, which is okay
120
- if (process.env.DEBUG) {
121
- console.error(`Failed to read tasks file at ${tasksPath}:`, error);
122
- }
123
- }
124
-
125
- return {
126
- id: changeName,
127
- title: this.extractTitle(content, changeName),
128
- deltaCount: change.deltas.length,
129
- taskStatus,
130
- };
131
- } catch (error) {
132
- return {
133
- id: changeName,
134
- title: 'Unknown',
135
- deltaCount: 0,
136
- taskStatus: { total: 0, completed: 0 },
137
- };
138
- }
139
- })
140
- );
141
-
142
- const sorted = changeDetails.sort((a, b) => a.id.localeCompare(b.id));
143
- console.log(JSON.stringify(sorted, null, 2));
144
- } else {
145
- if (changes.length === 0) {
146
- console.log('No items found');
147
- return;
148
- }
149
- const sorted = [...changes].sort();
150
- if (!options?.long) {
151
- // IDs only
152
- sorted.forEach(id => console.log(id));
153
- return;
154
- }
155
-
156
- // Long format: id: title and minimal counts
157
- for (const changeName of sorted) {
158
- const proposalPath = path.join(changesPath, changeName, 'proposal.md');
159
- const tasksPath = path.join(changesPath, changeName, 'tasks.md');
160
- try {
161
- const content = await fs.readFile(proposalPath, 'utf-8');
162
- const title = this.extractTitle(content, changeName);
163
- let taskStatusText = '';
164
- try {
165
- const tasksContent = await fs.readFile(tasksPath, 'utf-8');
166
- const { total, completed } = this.countTasks(tasksContent);
167
- taskStatusText = ` [tasks ${completed}/${total}]`;
168
- } catch (error) {
169
- if (process.env.DEBUG) {
170
- console.error(`Failed to read tasks file at ${tasksPath}:`, error);
171
- }
172
- }
173
- const changeDir = path.join(changesPath, changeName);
174
- const parser = new ChangeParser(await fs.readFile(proposalPath, 'utf-8'), changeDir);
175
- const change = await parser.parseChangeWithDeltas(changeName);
176
- const deltaCountText = ` [deltas ${change.deltas.length}]`;
177
- console.log(`${changeName}: ${title}${deltaCountText}${taskStatusText}`);
178
- } catch {
179
- console.log(`${changeName}: (unable to read)`);
180
- }
181
- }
182
- }
183
- }
184
-
185
- async validate(changeName?: string, options?: { strict?: boolean; json?: boolean; noInteractive?: boolean }): Promise<void> {
186
- const changesPath = path.join(process.cwd(), 'prompter', 'changes');
187
-
188
- if (!changeName) {
189
- const canPrompt = isInteractive(options);
190
- const changes = await getActiveChangeIds();
191
- if (canPrompt && changes.length > 0) {
192
- const { select } = await import('@inquirer/prompts');
193
- const selected = await select({
194
- message: 'Select a change to validate',
195
- choices: changes.map(id => ({ name: id, value: id })),
196
- });
197
- changeName = selected;
198
- } else {
199
- if (changes.length === 0) {
200
- console.error('No change specified. No active changes found.');
201
- } else {
202
- console.error(`No change specified. Available IDs: ${changes.join(', ')}`);
203
- }
204
- console.error('Hint: use "prompter change list" to view available changes.');
205
- process.exitCode = 1;
206
- return;
207
- }
208
- }
209
-
210
- const changeDir = path.join(changesPath, changeName);
211
-
212
- try {
213
- await fs.access(changeDir);
214
- } catch {
215
- throw new Error(`Change "${changeName}" not found at ${changeDir}`);
216
- }
217
-
218
- const validator = new Validator(options?.strict || false);
219
- const report = await validator.validateChangeDeltaSpecs(changeDir);
220
-
221
- if (options?.json) {
222
- console.log(JSON.stringify(report, null, 2));
223
- } else {
224
- if (report.valid) {
225
- console.log(`Change "${changeName}" is valid`);
226
- } else {
227
- console.error(`Change "${changeName}" has issues`);
228
- report.issues.forEach(issue => {
229
- const label = issue.level === 'ERROR' ? 'ERROR' : 'WARNING';
230
- const prefix = issue.level === 'ERROR' ? '✗' : '⚠';
231
- console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`);
232
- });
233
- // Next steps footer to guide fixing issues
234
- this.printNextSteps();
235
- if (!options?.json) {
236
- process.exitCode = 1;
237
- }
238
- }
239
- }
240
- }
241
-
242
- private async getActiveChanges(changesPath: string): Promise<string[]> {
243
- try {
244
- const entries = await fs.readdir(changesPath, { withFileTypes: true });
245
- const result: string[] = [];
246
- for (const entry of entries) {
247
- if (!entry.isDirectory() || entry.name.startsWith('.') || entry.name === ARCHIVE_DIR) continue;
248
- const proposalPath = path.join(changesPath, entry.name, 'proposal.md');
249
- try {
250
- await fs.access(proposalPath);
251
- result.push(entry.name);
252
- } catch {
253
- // skip directories without proposal.md
254
- }
255
- }
256
- return result.sort();
257
- } catch {
258
- return [];
259
- }
260
- }
261
-
262
- private extractTitle(content: string, changeName: string): string {
263
- const match = content.match(/^#\s+(?:Change:\s+)?(.+)$/im);
264
- return match ? match[1].trim() : changeName;
265
- }
266
-
267
- private countTasks(content: string): { total: number; completed: number } {
268
- const lines = content.split('\n');
269
- let total = 0;
270
- let completed = 0;
271
-
272
- for (const line of lines) {
273
- if (line.match(TASK_PATTERN)) {
274
- total++;
275
- if (line.match(COMPLETED_TASK_PATTERN)) {
276
- completed++;
277
- }
278
- }
279
- }
280
-
281
- return { total, completed };
282
- }
283
-
284
- private printNextSteps(): void {
285
- const bullets: string[] = [];
286
- bullets.push('- Ensure change has deltas in specs/: use headers ## ADDED/MODIFIED/REMOVED/RENAMED Requirements');
287
- bullets.push('- Each requirement MUST include at least one #### Scenario: block');
288
- bullets.push('- Debug parsed deltas: prompter change show <id> --json --deltas-only');
289
- console.error('Next steps:');
290
- bullets.forEach(b => console.error(` ${b}`));
291
- }
292
- }
@@ -1,233 +0,0 @@
1
- import { Command } from 'commander';
2
- import { spawn } from 'node:child_process';
3
- import * as fs from 'node:fs';
4
- import {
5
- getGlobalConfigPath,
6
- getGlobalConfig,
7
- saveGlobalConfig,
8
- GlobalConfig,
9
- } from '../core/global-config.js';
10
- import {
11
- getNestedValue,
12
- setNestedValue,
13
- deleteNestedValue,
14
- coerceValue,
15
- formatValueYaml,
16
- validateConfigKeyPath,
17
- validateConfig,
18
- DEFAULT_CONFIG,
19
- } from '../core/config-schema.js';
20
-
21
- /**
22
- * Register the config command and all its subcommands.
23
- *
24
- * @param program - The Commander program instance
25
- */
26
- export function registerConfigCommand(program: Command): void {
27
- const configCmd = program
28
- .command('config')
29
- .description('View and modify global Prompter configuration')
30
- .option('--scope <scope>', 'Config scope (only "global" supported currently)')
31
- .hook('preAction', (thisCommand) => {
32
- const opts = thisCommand.opts();
33
- if (opts.scope && opts.scope !== 'global') {
34
- console.error('Error: Project-local config is not yet implemented');
35
- process.exit(1);
36
- }
37
- });
38
-
39
- // config path
40
- configCmd
41
- .command('path')
42
- .description('Show config file location')
43
- .action(() => {
44
- console.log(getGlobalConfigPath());
45
- });
46
-
47
- // config list
48
- configCmd
49
- .command('list')
50
- .description('Show all current settings')
51
- .option('--json', 'Output as JSON')
52
- .action((options: { json?: boolean }) => {
53
- const config = getGlobalConfig();
54
-
55
- if (options.json) {
56
- console.log(JSON.stringify(config, null, 2));
57
- } else {
58
- console.log(formatValueYaml(config));
59
- }
60
- });
61
-
62
- // config get
63
- configCmd
64
- .command('get <key>')
65
- .description('Get a specific value (raw, scriptable)')
66
- .action((key: string) => {
67
- const config = getGlobalConfig();
68
- const value = getNestedValue(config as Record<string, unknown>, key);
69
-
70
- if (value === undefined) {
71
- process.exitCode = 1;
72
- return;
73
- }
74
-
75
- if (typeof value === 'object' && value !== null) {
76
- console.log(JSON.stringify(value));
77
- } else {
78
- console.log(String(value));
79
- }
80
- });
81
-
82
- // config set
83
- configCmd
84
- .command('set <key> <value>')
85
- .description('Set a value (auto-coerce types)')
86
- .option('--string', 'Force value to be stored as string')
87
- .option('--allow-unknown', 'Allow setting unknown keys')
88
- .action((key: string, value: string, options: { string?: boolean; allowUnknown?: boolean }) => {
89
- const allowUnknown = Boolean(options.allowUnknown);
90
- const keyValidation = validateConfigKeyPath(key);
91
- if (!keyValidation.valid && !allowUnknown) {
92
- const reason = keyValidation.reason ? ` ${keyValidation.reason}.` : '';
93
- console.error(`Error: Invalid configuration key "${key}".${reason}`);
94
- console.error('Use "prompter config list" to see available keys.');
95
- console.error('Pass --allow-unknown to bypass this check.');
96
- process.exitCode = 1;
97
- return;
98
- }
99
-
100
- const config = getGlobalConfig() as Record<string, unknown>;
101
- const coercedValue = coerceValue(value, options.string || false);
102
-
103
- // Create a copy to validate before saving
104
- const newConfig = JSON.parse(JSON.stringify(config));
105
- setNestedValue(newConfig, key, coercedValue);
106
-
107
- // Validate the new config
108
- const validation = validateConfig(newConfig);
109
- if (!validation.success) {
110
- console.error(`Error: Invalid configuration - ${validation.error}`);
111
- process.exitCode = 1;
112
- return;
113
- }
114
-
115
- // Apply changes and save
116
- setNestedValue(config, key, coercedValue);
117
- saveGlobalConfig(config as GlobalConfig);
118
-
119
- const displayValue =
120
- typeof coercedValue === 'string' ? `"${coercedValue}"` : String(coercedValue);
121
- console.log(`Set ${key} = ${displayValue}`);
122
- });
123
-
124
- // config unset
125
- configCmd
126
- .command('unset <key>')
127
- .description('Remove a key (revert to default)')
128
- .action((key: string) => {
129
- const config = getGlobalConfig() as Record<string, unknown>;
130
- const existed = deleteNestedValue(config, key);
131
-
132
- if (existed) {
133
- saveGlobalConfig(config as GlobalConfig);
134
- console.log(`Unset ${key} (reverted to default)`);
135
- } else {
136
- console.log(`Key "${key}" was not set`);
137
- }
138
- });
139
-
140
- // config reset
141
- configCmd
142
- .command('reset')
143
- .description('Reset configuration to defaults')
144
- .option('--all', 'Reset all configuration (required)')
145
- .option('-y, --yes', 'Skip confirmation prompts')
146
- .action(async (options: { all?: boolean; yes?: boolean }) => {
147
- if (!options.all) {
148
- console.error('Error: --all flag is required for reset');
149
- console.error('Usage: prompter config reset --all [-y]');
150
- process.exitCode = 1;
151
- return;
152
- }
153
-
154
- if (!options.yes) {
155
- const { confirm } = await import('@inquirer/prompts');
156
- const confirmed = await confirm({
157
- message: 'Reset all configuration to defaults?',
158
- default: false,
159
- });
160
-
161
- if (!confirmed) {
162
- console.log('Reset cancelled.');
163
- return;
164
- }
165
- }
166
-
167
- saveGlobalConfig({ ...DEFAULT_CONFIG });
168
- console.log('Configuration reset to defaults');
169
- });
170
-
171
- // config edit
172
- configCmd
173
- .command('edit')
174
- .description('Open config in $EDITOR')
175
- .action(async () => {
176
- const editor = process.env.EDITOR || process.env.VISUAL;
177
-
178
- if (!editor) {
179
- console.error('Error: No editor configured');
180
- console.error('Set the EDITOR or VISUAL environment variable to your preferred editor');
181
- console.error('Example: export EDITOR=vim');
182
- process.exitCode = 1;
183
- return;
184
- }
185
-
186
- const configPath = getGlobalConfigPath();
187
-
188
- // Ensure config file exists with defaults
189
- if (!fs.existsSync(configPath)) {
190
- saveGlobalConfig({ ...DEFAULT_CONFIG });
191
- }
192
-
193
- // Spawn editor and wait for it to close
194
- // Avoid shell parsing to correctly handle paths with spaces in both
195
- // the editor path and config path
196
- const child = spawn(editor, [configPath], {
197
- stdio: 'inherit',
198
- shell: false,
199
- });
200
-
201
- await new Promise<void>((resolve, reject) => {
202
- child.on('close', (code) => {
203
- if (code === 0) {
204
- resolve();
205
- } else {
206
- reject(new Error(`Editor exited with code ${code}`));
207
- }
208
- });
209
- child.on('error', reject);
210
- });
211
-
212
- try {
213
- const rawConfig = fs.readFileSync(configPath, 'utf-8');
214
- const parsedConfig = JSON.parse(rawConfig);
215
- const validation = validateConfig(parsedConfig);
216
-
217
- if (!validation.success) {
218
- console.error(`Error: Invalid configuration - ${validation.error}`);
219
- process.exitCode = 1;
220
- }
221
- } catch (error) {
222
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
223
- console.error(`Error: Config file not found at ${configPath}`);
224
- } else if (error instanceof SyntaxError) {
225
- console.error(`Error: Invalid JSON in ${configPath}`);
226
- console.error(error.message);
227
- } else {
228
- console.error(`Error: Unable to validate configuration - ${error instanceof Error ? error.message : String(error)}`);
229
- }
230
- process.exitCode = 1;
231
- }
232
- });
233
- }
@@ -1,50 +0,0 @@
1
- import chalk from 'chalk';
2
-
3
- export class GuideCommand {
4
- async execute(): Promise<void> {
5
- console.log(chalk.blue('\nGuides:'));
6
- console.log(chalk.gray(' 1. Provide a detailed and filled-out version of the "prompter/project.md"'));
7
- console.log(chalk.gray(' document based on the project\'s specifics, including a clear description'));
8
- console.log(chalk.gray(' of the project, its purpose, the technologies used (tech stack), and any'));
9
- console.log(chalk.gray(' conventions or standards being followed.'));
10
-
11
- console.log(chalk.blue('\nDocument Workflow & Dependencies:'));
12
- console.log(chalk.gray(' This table shows the recommended order for document generation:\n'));
13
-
14
- // Print table header
15
- console.log(chalk.cyan(' Document │ Required Inputs │ Extra Inputs │ Recommended Model'));
16
- console.log(chalk.cyan(' ─────────────────┼───────────────────────────────────────┼───────────────────────────────────┼──────────────────────────'));
17
-
18
- // Table rows
19
- const rows = [
20
- ['Product Brief ', '— ', '— ', 'Opus'],
21
- ['PRD ', 'Product Brief ', '— ', 'Sonnet'],
22
- ['FSD ', 'PRD ', 'Product Brief ', 'Sonnet'],
23
- ['ERD ', 'FSD ', '— ', 'Sonnet'],
24
- ['API Contract ', 'FSD + ERD ', '— ', 'Sonnet / GPT 5.2 Codex'],
25
- ['UI Wireframes ', 'FSD + ERD + API Contract ', '— ', 'Gemini 3 Pro / Sonnet'],
26
- ['Design System ', 'UI Wireframes (new) / FSD (existing) ', 'Brand guide / existing UI ', 'Sonnet'],
27
- ['TDD ', 'FSD ', 'ERD + API + UI + Design System ', 'Sonnet'],
28
- ['Epics ', 'FSD + TDD-Lite ', 'UI Wireframes + Design System ', 'Sonnet'],
29
- ['Stories ', 'Epics ', 'FSD + UI Wireframes + API ', 'Sonnet'],
30
- ['Proposal ', 'Specs (FSD/TDD/etc) ', '— ', 'Opus'],
31
- ['Apply ', 'Approved Proposal ', '— ', 'Sonnet / Gemini 3 / GPT 5.2 / GLM / Haiku']
32
- ];
33
-
34
- rows.forEach(row => {
35
- console.log(chalk.gray(` ${row[0]} │ ${row[1]} │ ${row[2]} │ ${row[3]}`));
36
- });
37
-
38
- console.log(chalk.blue('\n Complexity-Based Workflows:'));
39
- console.log(chalk.gray(' Small → Brief → FSD → Design System → Stories'));
40
- console.log(chalk.gray(' Medium → Brief → PRD → FSD → UI → Design System → Stories'));
41
- console.log(chalk.gray(' High → Brief → PRD → FSD → ERD → API → UI → Design System → TDD → Epics → Stories'));
42
-
43
- console.log(chalk.blue('\n Prompter Solo Dev 🤓 Workflow:'));
44
- console.log(chalk.green.bold(' Step 1 📄: Brief → PRD → Epic (Foundation)'));
45
- console.log(chalk.green.bold(' Step 2 🚀: [ Proposal ⇄ Apply ⇄ Archive ] ↺ (Development Loop)'));
46
- console.log(chalk.gray(' • Initial setup: Create Brief, PRD and Epic to establish context.'));
47
- console.log(chalk.gray(' • Active Dev: Repeat Step 2 for every feature, bugfix, or improvement.'));
48
- console.log(chalk.gray(' • Archive step automatically updates your specs to keep them current.'));
49
- }
50
- }