@dedesfr/prompter 0.8.23 → 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 (247) hide show
  1. package/CHANGELOG.md +70 -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 +1 -7
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +60 -299
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/login.d.ts +4 -0
  10. package/dist/commands/login.d.ts.map +1 -0
  11. package/dist/commands/login.js +56 -0
  12. package/dist/commands/login.js.map +1 -0
  13. package/dist/commands/logout.d.ts +4 -0
  14. package/dist/commands/logout.d.ts.map +1 -0
  15. package/dist/commands/logout.js +14 -0
  16. package/dist/commands/logout.js.map +1 -0
  17. package/dist/commands/update.d.ts.map +1 -1
  18. package/dist/commands/update.js +31 -41
  19. package/dist/commands/update.js.map +1 -1
  20. package/dist/commands/whoami.d.ts +4 -0
  21. package/dist/commands/whoami.d.ts.map +1 -0
  22. package/dist/commands/whoami.js +42 -0
  23. package/dist/commands/whoami.js.map +1 -0
  24. package/dist/core/auth-store.d.ts +10 -0
  25. package/dist/core/auth-store.d.ts.map +1 -0
  26. package/dist/core/auth-store.js +39 -0
  27. package/dist/core/auth-store.js.map +1 -0
  28. package/dist/core/configurators/slash/antigravity.d.ts +2 -5
  29. package/dist/core/configurators/slash/antigravity.d.ts.map +1 -1
  30. package/dist/core/configurators/slash/antigravity.js +2 -57
  31. package/dist/core/configurators/slash/antigravity.js.map +1 -1
  32. package/dist/core/configurators/slash/base.d.ts +6 -18
  33. package/dist/core/configurators/slash/base.d.ts.map +1 -1
  34. package/dist/core/configurators/slash/base.js +8 -77
  35. package/dist/core/configurators/slash/base.js.map +1 -1
  36. package/dist/core/configurators/slash/claude.d.ts +2 -5
  37. package/dist/core/configurators/slash/claude.d.ts.map +1 -1
  38. package/dist/core/configurators/slash/claude.js +2 -57
  39. package/dist/core/configurators/slash/claude.js.map +1 -1
  40. package/dist/core/configurators/slash/codex.d.ts +2 -5
  41. package/dist/core/configurators/slash/codex.d.ts.map +1 -1
  42. package/dist/core/configurators/slash/codex.js +2 -57
  43. package/dist/core/configurators/slash/codex.js.map +1 -1
  44. package/dist/core/configurators/slash/droid.d.ts +2 -5
  45. package/dist/core/configurators/slash/droid.d.ts.map +1 -1
  46. package/dist/core/configurators/slash/droid.js +2 -32
  47. package/dist/core/configurators/slash/droid.js.map +1 -1
  48. package/dist/core/configurators/slash/forge.d.ts +2 -5
  49. package/dist/core/configurators/slash/forge.d.ts.map +1 -1
  50. package/dist/core/configurators/slash/forge.js +2 -32
  51. package/dist/core/configurators/slash/forge.js.map +1 -1
  52. package/dist/core/configurators/slash/github-copilot.d.ts +2 -7
  53. package/dist/core/configurators/slash/github-copilot.d.ts.map +1 -1
  54. package/dist/core/configurators/slash/github-copilot.js +2 -96
  55. package/dist/core/configurators/slash/github-copilot.js.map +1 -1
  56. package/dist/core/configurators/slash/index.d.ts +1 -1
  57. package/dist/core/configurators/slash/index.d.ts.map +1 -1
  58. package/dist/core/configurators/slash/index.js +1 -1
  59. package/dist/core/configurators/slash/index.js.map +1 -1
  60. package/dist/core/configurators/slash/kilocode.d.ts +2 -5
  61. package/dist/core/configurators/slash/kilocode.d.ts.map +1 -1
  62. package/dist/core/configurators/slash/kilocode.js +2 -57
  63. package/dist/core/configurators/slash/kilocode.js.map +1 -1
  64. package/dist/core/configurators/slash/opencode.d.ts +2 -5
  65. package/dist/core/configurators/slash/opencode.d.ts.map +1 -1
  66. package/dist/core/configurators/slash/opencode.js +2 -57
  67. package/dist/core/configurators/slash/opencode.js.map +1 -1
  68. package/dist/core/configurators/slash/registry.d.ts +4 -4
  69. package/dist/core/configurators/slash/registry.d.ts.map +1 -1
  70. package/dist/core/configurators/slash/registry.js.map +1 -1
  71. package/dist/core/registry.d.ts +18 -0
  72. package/dist/core/registry.d.ts.map +1 -0
  73. package/dist/core/registry.js +94 -0
  74. package/dist/core/registry.js.map +1 -0
  75. package/dist/core/templates/index.d.ts +0 -1
  76. package/dist/core/templates/index.d.ts.map +1 -1
  77. package/dist/core/templates/index.js +0 -1
  78. package/dist/core/templates/index.js.map +1 -1
  79. package/package.json +7 -1
  80. package/AGENTS.md +0 -123
  81. package/CLAUDE.md +0 -17
  82. package/build.js +0 -20
  83. package/convex-setup.md +0 -403
  84. package/dist/core/templates/slash-command-templates.d.ts +0 -7
  85. package/dist/core/templates/slash-command-templates.d.ts.map +0 -1
  86. package/dist/core/templates/slash-command-templates.js +0 -1041
  87. package/dist/core/templates/slash-command-templates.js.map +0 -1
  88. package/prompt/ai-humanizer.md +0 -45
  89. package/prompt/api-contract-generator.md +0 -234
  90. package/prompt/apply.md +0 -17
  91. package/prompt/archive.md +0 -21
  92. package/prompt/design-system.md +0 -210
  93. package/prompt/document-explainer.md +0 -149
  94. package/prompt/epic-generator.md +0 -198
  95. package/prompt/epic-single.md +0 -47
  96. package/prompt/erd-generator.md +0 -130
  97. package/prompt/fsd-generator.md +0 -157
  98. package/prompt/prd-agent-generator.md +0 -147
  99. package/prompt/prd-generator.md +0 -195
  100. package/prompt/product-brief.md +0 -289
  101. package/prompt/proposal.md +0 -22
  102. package/prompt/qa-test-scenario.md +0 -133
  103. package/prompt/skill-creator.md +0 -350
  104. package/prompt/story-generator.md +0 -278
  105. package/prompt/story-single.md +0 -70
  106. package/prompt/tdd-generator.md +0 -294
  107. package/prompt/tdd-lite-generator.md +0 -224
  108. package/prompt/wireframe-generator.md +0 -219
  109. package/skills/ai-context-generator/SKILL.md +0 -54
  110. package/skills/ai-context-generator/references/AGENTS.template.md +0 -83
  111. package/skills/ai-context-generator/references/CLAUDE.template.md +0 -39
  112. package/skills/ai-context-generator/references/behavioral-guidelines.md +0 -71
  113. package/skills/ai-context-generator/references/discovery-checklist.md +0 -40
  114. package/skills/ai-context-generator/references/examples/AGENTS.good.md +0 -103
  115. package/skills/ai-context-generator/references/extraction-checklist.md +0 -23
  116. package/skills/ai-context-generator/references/overlays/laravel.md +0 -44
  117. package/skills/cerebro/SKILL.md +0 -187
  118. package/skills/cerebro/references/agents.md +0 -213
  119. package/skills/code-review/SKILL.md +0 -373
  120. package/skills/code-review/assets/report-template-agent.md +0 -212
  121. package/skills/code-review/assets/report-template-compact.md +0 -81
  122. package/skills/code-review/assets/report-template-full.md +0 -264
  123. package/skills/code-review/assets/report-template-human.md +0 -168
  124. package/skills/code-review/references/universal-patterns.md +0 -495
  125. package/skills/design-md/README.md +0 -34
  126. package/skills/design-md/SKILL.md +0 -172
  127. package/skills/design-md/examples/DESIGN.md +0 -154
  128. package/skills/design-system-generator/SKILL.md +0 -324
  129. package/skills/design-system-generator/assets/design-system-template.md +0 -348
  130. package/skills/design-system-generator/references/extraction-patterns.md +0 -321
  131. package/skills/doc-builder/SKILL.md +0 -115
  132. package/skills/doc-builder/references/ui-patterns.md +0 -394
  133. package/skills/document-translator/SKILL.md +0 -58
  134. package/skills/enhance-prompt/README.md +0 -34
  135. package/skills/enhance-prompt/SKILL.md +0 -204
  136. package/skills/enhance-prompt/references/KEYWORDS.md +0 -114
  137. package/skills/feature-planner/SKILL.md +0 -305
  138. package/skills/feature-planner/assets/implementation-plan-template.md +0 -85
  139. package/skills/frontend-design/LICENSE.txt +0 -177
  140. package/skills/frontend-design/SKILL.md +0 -42
  141. package/skills/gamma-builder/SKILL.md +0 -134
  142. package/skills/laravel-code-review/SKILL.md +0 -383
  143. package/skills/laravel-code-review/assets/report-template-agent.md +0 -195
  144. package/skills/laravel-code-review/assets/report-template-compact.md +0 -79
  145. package/skills/laravel-code-review/assets/report-template-full.md +0 -253
  146. package/skills/laravel-code-review/assets/report-template-human.md +0 -159
  147. package/skills/laravel-code-review/references/laravel-patterns.md +0 -571
  148. package/skills/laravel-code-review/references/php84-features.md +0 -442
  149. package/skills/mcp-builder/LICENSE.txt +0 -202
  150. package/skills/mcp-builder/SKILL.md +0 -236
  151. package/skills/mcp-builder/reference/evaluation.md +0 -602
  152. package/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  153. package/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  154. package/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  155. package/skills/mcp-builder/scripts/connections.py +0 -151
  156. package/skills/mcp-builder/scripts/evaluation.py +0 -373
  157. package/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  158. package/skills/mcp-builder/scripts/requirements.txt +0 -2
  159. package/skills/meeting-notes/SKILL.md +0 -159
  160. package/skills/meeting-notes/evals/evals.json +0 -23
  161. package/skills/project-orchestrator/SKILL.md +0 -487
  162. package/skills/project-orchestrator/assets/caddy-vps-setup.md +0 -180
  163. package/skills/project-orchestrator/assets/plan-summary-template.md +0 -159
  164. package/skills/prompter-specs/SKILL.md +0 -115
  165. package/skills/prompter-workflow/SKILL.md +0 -166
  166. package/skills/prompter-workflow/evals/evals.json +0 -89
  167. package/skills/sph-generator/SKILL.md +0 -488
  168. package/skills/ui-ux-pro/SKILL.md +0 -199
  169. package/skills/ui-ux-pro/assets/design-spec-template.md +0 -173
  170. package/skills/ui-ux-pro/references/component-patterns.md +0 -255
  171. package/skills/ui-ux-pro/references/design-principles.md +0 -167
  172. package/src/cli/index.ts +0 -223
  173. package/src/commands/archive.ts +0 -302
  174. package/src/commands/change.ts +0 -292
  175. package/src/commands/config.ts +0 -233
  176. package/src/commands/guide.ts +0 -50
  177. package/src/commands/init.ts +0 -899
  178. package/src/commands/list.ts +0 -194
  179. package/src/commands/show.ts +0 -138
  180. package/src/commands/spec.ts +0 -251
  181. package/src/commands/update.ts +0 -156
  182. package/src/commands/upgrade.ts +0 -30
  183. package/src/commands/validate.ts +0 -326
  184. package/src/core/artifact-graph/graph.ts +0 -167
  185. package/src/core/artifact-graph/index.ts +0 -44
  186. package/src/core/artifact-graph/instruction-loader.ts +0 -302
  187. package/src/core/artifact-graph/resolver.ts +0 -226
  188. package/src/core/artifact-graph/schema.ts +0 -124
  189. package/src/core/artifact-graph/state.ts +0 -64
  190. package/src/core/artifact-graph/types.ts +0 -65
  191. package/src/core/completions/command-registry.ts +0 -382
  192. package/src/core/completions/completion-provider.ts +0 -128
  193. package/src/core/completions/generators/bash-generator.ts +0 -191
  194. package/src/core/completions/generators/fish-generator.ts +0 -188
  195. package/src/core/completions/generators/powershell-generator.ts +0 -223
  196. package/src/core/completions/generators/zsh-generator.ts +0 -281
  197. package/src/core/completions/templates/bash-templates.ts +0 -24
  198. package/src/core/completions/templates/fish-templates.ts +0 -40
  199. package/src/core/completions/templates/powershell-templates.ts +0 -25
  200. package/src/core/completions/templates/zsh-templates.ts +0 -36
  201. package/src/core/completions/types.ts +0 -90
  202. package/src/core/config-schema.ts +0 -230
  203. package/src/core/config.ts +0 -181
  204. package/src/core/configurators/slash/antigravity.ts +0 -70
  205. package/src/core/configurators/slash/base.ts +0 -203
  206. package/src/core/configurators/slash/claude.ts +0 -70
  207. package/src/core/configurators/slash/codex.ts +0 -70
  208. package/src/core/configurators/slash/droid.ts +0 -44
  209. package/src/core/configurators/slash/forge.ts +0 -44
  210. package/src/core/configurators/slash/github-copilot.ts +0 -114
  211. package/src/core/configurators/slash/index.ts +0 -10
  212. package/src/core/configurators/slash/kilocode.ts +0 -70
  213. package/src/core/configurators/slash/opencode.ts +0 -70
  214. package/src/core/configurators/slash/registry.ts +0 -51
  215. package/src/core/converters/json-converter.ts +0 -62
  216. package/src/core/global-config.ts +0 -136
  217. package/src/core/parsers/change-parser.ts +0 -234
  218. package/src/core/parsers/markdown-parser.ts +0 -237
  219. package/src/core/parsers/requirement-blocks.ts +0 -234
  220. package/src/core/prompt-templates.ts +0 -3504
  221. package/src/core/schemas/base.schema.ts +0 -20
  222. package/src/core/schemas/change.schema.ts +0 -42
  223. package/src/core/schemas/index.ts +0 -20
  224. package/src/core/schemas/spec.schema.ts +0 -17
  225. package/src/core/skill-discovery.ts +0 -68
  226. package/src/core/specs-apply.ts +0 -483
  227. package/src/core/styles/palette.ts +0 -8
  228. package/src/core/templates/agents-template.ts +0 -459
  229. package/src/core/templates/claude-template.ts +0 -2
  230. package/src/core/templates/index.ts +0 -4
  231. package/src/core/templates/project-template.ts +0 -32
  232. package/src/core/templates/slash-command-templates.ts +0 -1068
  233. package/src/core/validation/constants.ts +0 -48
  234. package/src/core/validation/types.ts +0 -19
  235. package/src/core/validation/validator.ts +0 -449
  236. package/src/core/view.ts +0 -219
  237. package/src/index.ts +0 -1
  238. package/src/utils/change-metadata.ts +0 -171
  239. package/src/utils/change-utils.ts +0 -131
  240. package/src/utils/file-system.ts +0 -252
  241. package/src/utils/index.ts +0 -12
  242. package/src/utils/interactive.ts +0 -29
  243. package/src/utils/item-discovery.ts +0 -66
  244. package/src/utils/match.ts +0 -26
  245. package/src/utils/shell-detection.ts +0 -62
  246. package/src/utils/task-progress.ts +0 -43
  247. package/tsconfig.json +0 -28
@@ -1,194 +0,0 @@
1
- import { promises as fs } from 'fs';
2
- import path from 'path';
3
- import { getTaskProgressForChange, formatTaskStatus } from '../utils/task-progress.js';
4
- import { readFileSync } from 'fs';
5
- import { join } from 'path';
6
- import { MarkdownParser } from '../core/parsers/markdown-parser.js';
7
-
8
- interface ChangeInfo {
9
- name: string;
10
- completedTasks: number;
11
- totalTasks: number;
12
- lastModified: Date;
13
- }
14
-
15
- interface ListOptions {
16
- sort?: 'recent' | 'name';
17
- json?: boolean;
18
- }
19
-
20
- /**
21
- * Get the most recent modification time of any file in a directory (recursive).
22
- * Falls back to the directory's own mtime if no files are found.
23
- */
24
- async function getLastModified(dirPath: string): Promise<Date> {
25
- let latest: Date | null = null;
26
-
27
- async function walk(dir: string): Promise<void> {
28
- const entries = await fs.readdir(dir, { withFileTypes: true });
29
- for (const entry of entries) {
30
- const fullPath = path.join(dir, entry.name);
31
- if (entry.isDirectory()) {
32
- await walk(fullPath);
33
- } else {
34
- const stat = await fs.stat(fullPath);
35
- if (latest === null || stat.mtime > latest) {
36
- latest = stat.mtime;
37
- }
38
- }
39
- }
40
- }
41
-
42
- await walk(dirPath);
43
-
44
- // If no files found, use the directory's own modification time
45
- if (latest === null) {
46
- const dirStat = await fs.stat(dirPath);
47
- return dirStat.mtime;
48
- }
49
-
50
- return latest;
51
- }
52
-
53
- /**
54
- * Format a date as relative time (e.g., "2 hours ago", "3 days ago")
55
- */
56
- function formatRelativeTime(date: Date): string {
57
- const now = new Date();
58
- const diffMs = now.getTime() - date.getTime();
59
- const diffSecs = Math.floor(diffMs / 1000);
60
- const diffMins = Math.floor(diffSecs / 60);
61
- const diffHours = Math.floor(diffMins / 60);
62
- const diffDays = Math.floor(diffHours / 24);
63
-
64
- if (diffDays > 30) {
65
- return date.toLocaleDateString();
66
- } else if (diffDays > 0) {
67
- return `${diffDays}d ago`;
68
- } else if (diffHours > 0) {
69
- return `${diffHours}h ago`;
70
- } else if (diffMins > 0) {
71
- return `${diffMins}m ago`;
72
- } else {
73
- return 'just now';
74
- }
75
- }
76
-
77
- export class ListCommand {
78
- async execute(targetPath: string = '.', mode: 'changes' | 'specs' = 'changes', options: ListOptions = {}): Promise<void> {
79
- const { sort = 'recent', json = false } = options;
80
-
81
- if (mode === 'changes') {
82
- const changesDir = path.join(targetPath, 'prompter', 'changes');
83
-
84
- // Check if changes directory exists
85
- try {
86
- await fs.access(changesDir);
87
- } catch {
88
- throw new Error("No Prompter changes directory found. Run 'prompter init' first.");
89
- }
90
-
91
- // Get all directories in changes (excluding archive)
92
- const entries = await fs.readdir(changesDir, { withFileTypes: true });
93
- const changeDirs = entries
94
- .filter(entry => entry.isDirectory() && entry.name !== 'archive')
95
- .map(entry => entry.name);
96
-
97
- if (changeDirs.length === 0) {
98
- if (json) {
99
- console.log(JSON.stringify({ changes: [] }));
100
- } else {
101
- console.log('No active changes found.');
102
- }
103
- return;
104
- }
105
-
106
- // Collect information about each change
107
- const changes: ChangeInfo[] = [];
108
-
109
- for (const changeDir of changeDirs) {
110
- const progress = await getTaskProgressForChange(changesDir, changeDir);
111
- const changePath = path.join(changesDir, changeDir);
112
- const lastModified = await getLastModified(changePath);
113
- changes.push({
114
- name: changeDir,
115
- completedTasks: progress.completed,
116
- totalTasks: progress.total,
117
- lastModified
118
- });
119
- }
120
-
121
- // Sort by preference (default: recent first)
122
- if (sort === 'recent') {
123
- changes.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());
124
- } else {
125
- changes.sort((a, b) => a.name.localeCompare(b.name));
126
- }
127
-
128
- // JSON output for programmatic use
129
- if (json) {
130
- const jsonOutput = changes.map(c => ({
131
- name: c.name,
132
- completedTasks: c.completedTasks,
133
- totalTasks: c.totalTasks,
134
- lastModified: c.lastModified.toISOString(),
135
- status: c.totalTasks === 0 ? 'no-tasks' : c.completedTasks === c.totalTasks ? 'complete' : 'in-progress'
136
- }));
137
- console.log(JSON.stringify({ changes: jsonOutput }, null, 2));
138
- return;
139
- }
140
-
141
- // Display results
142
- console.log('Changes:');
143
- const padding = ' ';
144
- const nameWidth = Math.max(...changes.map(c => c.name.length));
145
- for (const change of changes) {
146
- const paddedName = change.name.padEnd(nameWidth);
147
- const status = formatTaskStatus({ total: change.totalTasks, completed: change.completedTasks });
148
- const timeAgo = formatRelativeTime(change.lastModified);
149
- console.log(`${padding}${paddedName} ${status.padEnd(12)} ${timeAgo}`);
150
- }
151
- return;
152
- }
153
-
154
- // specs mode
155
- const specsDir = path.join(targetPath, 'prompter', 'specs');
156
- try {
157
- await fs.access(specsDir);
158
- } catch {
159
- console.log('No specs found.');
160
- return;
161
- }
162
-
163
- const entries = await fs.readdir(specsDir, { withFileTypes: true });
164
- const specDirs = entries.filter(e => e.isDirectory()).map(e => e.name);
165
- if (specDirs.length === 0) {
166
- console.log('No specs found.');
167
- return;
168
- }
169
-
170
- type SpecInfo = { id: string; requirementCount: number };
171
- const specs: SpecInfo[] = [];
172
- for (const id of specDirs) {
173
- const specPath = join(specsDir, id, 'spec.md');
174
- try {
175
- const content = readFileSync(specPath, 'utf-8');
176
- const parser = new MarkdownParser(content);
177
- const spec = parser.parseSpec(id);
178
- specs.push({ id, requirementCount: spec.requirements.length });
179
- } catch {
180
- // If spec cannot be read or parsed, include with 0 count
181
- specs.push({ id, requirementCount: 0 });
182
- }
183
- }
184
-
185
- specs.sort((a, b) => a.id.localeCompare(b.id));
186
- console.log('Specs:');
187
- const padding = ' ';
188
- const nameWidth = Math.max(...specs.map(s => s.id.length));
189
- for (const spec of specs) {
190
- const padded = spec.id.padEnd(nameWidth);
191
- console.log(`${padding}${padded} requirements ${spec.requirementCount}`);
192
- }
193
- }
194
- }
@@ -1,138 +0,0 @@
1
- import path from 'path';
2
- import { isInteractive } from '../utils/interactive.js';
3
- import { getActiveChangeIds, getSpecIds } from '../utils/item-discovery.js';
4
- import { ChangeCommand } from './change.js';
5
- import { SpecCommand } from './spec.js';
6
- import { nearestMatches } from '../utils/match.js';
7
-
8
- type ItemType = 'change' | 'spec';
9
-
10
- const CHANGE_FLAG_KEYS = new Set(['deltasOnly', 'requirementsOnly']);
11
- const SPEC_FLAG_KEYS = new Set(['requirements', 'scenarios', 'requirement']);
12
-
13
- export class ShowCommand {
14
- async execute(itemName?: string, options: { json?: boolean; type?: string; noInteractive?: boolean; [k: string]: any } = {}): Promise<void> {
15
- const interactive = isInteractive(options);
16
- const typeOverride = this.normalizeType(options.type);
17
-
18
- if (!itemName) {
19
- if (interactive) {
20
- const { select } = await import('@inquirer/prompts');
21
- const type = await select<ItemType>({
22
- message: 'What would you like to show?',
23
- choices: [
24
- { name: 'Change', value: 'change' as const },
25
- { name: 'Spec', value: 'spec' as const },
26
- ],
27
- });
28
- await this.runInteractiveByType(type, options);
29
- return;
30
- }
31
- this.printNonInteractiveHint();
32
- process.exitCode = 1;
33
- return;
34
- }
35
-
36
- await this.showDirect(itemName, { typeOverride, options });
37
- }
38
-
39
- private normalizeType(value?: string): ItemType | undefined {
40
- if (!value) return undefined;
41
- const v = value.toLowerCase();
42
- if (v === 'change' || v === 'spec') return v;
43
- return undefined;
44
- }
45
-
46
- private async runInteractiveByType(type: ItemType, options: { json?: boolean; noInteractive?: boolean; [k: string]: any }): Promise<void> {
47
- const { select } = await import('@inquirer/prompts');
48
- if (type === 'change') {
49
- const changes = await getActiveChangeIds();
50
- if (changes.length === 0) {
51
- console.error('No changes found.');
52
- process.exitCode = 1;
53
- return;
54
- }
55
- const picked = await select<string>({ message: 'Pick a change', choices: changes.map(id => ({ name: id, value: id })) });
56
- const cmd = new ChangeCommand();
57
- await cmd.show(picked, options as any);
58
- return;
59
- }
60
-
61
- const specs = await getSpecIds();
62
- if (specs.length === 0) {
63
- console.error('No specs found.');
64
- process.exitCode = 1;
65
- return;
66
- }
67
- const picked = await select<string>({ message: 'Pick a spec', choices: specs.map(id => ({ name: id, value: id })) });
68
- const cmd = new SpecCommand();
69
- await cmd.show(picked, options as any);
70
- }
71
-
72
- private async showDirect(itemName: string, params: { typeOverride?: ItemType; options: { json?: boolean; [k: string]: any } }): Promise<void> {
73
- // Optimize lookups when type is pre-specified
74
- let isChange = false;
75
- let isSpec = false;
76
- let changes: string[] = [];
77
- let specs: string[] = [];
78
- if (params.typeOverride === 'change') {
79
- changes = await getActiveChangeIds();
80
- isChange = changes.includes(itemName);
81
- } else if (params.typeOverride === 'spec') {
82
- specs = await getSpecIds();
83
- isSpec = specs.includes(itemName);
84
- } else {
85
- [changes, specs] = await Promise.all([getActiveChangeIds(), getSpecIds()]);
86
- isChange = changes.includes(itemName);
87
- isSpec = specs.includes(itemName);
88
- }
89
-
90
- const resolvedType = params.typeOverride ?? (isChange ? 'change' : isSpec ? 'spec' : undefined);
91
-
92
- if (!resolvedType) {
93
- console.error(`Unknown item '${itemName}'`);
94
- const suggestions = nearestMatches(itemName, [...changes, ...specs]);
95
- if (suggestions.length) console.error(`Did you mean: ${suggestions.join(', ')}?`);
96
- process.exitCode = 1;
97
- return;
98
- }
99
-
100
- if (!params.typeOverride && isChange && isSpec) {
101
- console.error(`Ambiguous item '${itemName}' matches both a change and a spec.`);
102
- console.error('Pass --type change|spec, or use: prompter change show / prompter spec show');
103
- process.exitCode = 1;
104
- return;
105
- }
106
-
107
- this.warnIrrelevantFlags(resolvedType, params.options);
108
- if (resolvedType === 'change') {
109
- const cmd = new ChangeCommand();
110
- await cmd.show(itemName, params.options as any);
111
- return;
112
- }
113
- const cmd = new SpecCommand();
114
- await cmd.show(itemName, params.options as any);
115
- }
116
-
117
- private printNonInteractiveHint(): void {
118
- console.error('Nothing to show. Try one of:');
119
- console.error(' prompter show <item>');
120
- console.error(' prompter change show');
121
- console.error(' prompter spec show');
122
- console.error('Or run in an interactive terminal.');
123
- }
124
-
125
- private warnIrrelevantFlags(type: ItemType, options: { [k: string]: any }): boolean {
126
- const irrelevant: string[] = [];
127
- if (type === 'change') {
128
- for (const k of SPEC_FLAG_KEYS) if (k in options) irrelevant.push(k);
129
- } else {
130
- for (const k of CHANGE_FLAG_KEYS) if (k in options) irrelevant.push(k);
131
- }
132
- if (irrelevant.length > 0) {
133
- console.error(`Warning: Ignoring flags not applicable to ${type}: ${irrelevant.join(', ')}`);
134
- return true;
135
- }
136
- return false;
137
- }
138
- }
@@ -1,251 +0,0 @@
1
- import { program } from 'commander';
2
- import { existsSync, readdirSync, readFileSync } from 'fs';
3
- import { join } from 'path';
4
- import { MarkdownParser } from '../core/parsers/markdown-parser.js';
5
- import { Validator } from '../core/validation/validator.js';
6
- import type { Spec } from '../core/schemas/index.js';
7
- import { isInteractive } from '../utils/interactive.js';
8
- import { getSpecIds } from '../utils/item-discovery.js';
9
-
10
- const SPECS_DIR = 'prompter/specs';
11
-
12
- interface ShowOptions {
13
- json?: boolean;
14
- // JSON-only filters (raw-first text has no filters)
15
- requirements?: boolean;
16
- scenarios?: boolean; // --no-scenarios sets this to false (JSON only)
17
- requirement?: string; // JSON only
18
- noInteractive?: boolean;
19
- }
20
-
21
- function parseSpecFromFile(specPath: string, specId: string): Spec {
22
- const content = readFileSync(specPath, 'utf-8');
23
- const parser = new MarkdownParser(content);
24
- return parser.parseSpec(specId);
25
- }
26
-
27
- function validateRequirementIndex(spec: Spec, requirementOpt?: string): number | undefined {
28
- if (!requirementOpt) return undefined;
29
- const index = Number.parseInt(requirementOpt, 10);
30
- if (!Number.isInteger(index) || index < 1 || index > spec.requirements.length) {
31
- throw new Error(`Requirement ${requirementOpt} not found`);
32
- }
33
- return index - 1; // convert to 0-based
34
- }
35
-
36
- function filterSpec(spec: Spec, options: ShowOptions): Spec {
37
- const requirementIndex = validateRequirementIndex(spec, options.requirement);
38
- const includeScenarios = options.scenarios !== false && !options.requirements;
39
-
40
- const filteredRequirements = (requirementIndex !== undefined
41
- ? [spec.requirements[requirementIndex]]
42
- : spec.requirements
43
- ).map(req => ({
44
- text: req.text,
45
- scenarios: includeScenarios ? req.scenarios : [],
46
- }));
47
-
48
- const metadata = spec.metadata ?? { version: '1.0.0', format: 'prompter' as const };
49
-
50
- return {
51
- name: spec.name,
52
- overview: spec.overview,
53
- requirements: filteredRequirements,
54
- metadata,
55
- };
56
- }
57
-
58
- /**
59
- * Print the raw markdown content for a spec file without any formatting.
60
- * Raw-first behavior ensures text mode is a passthrough for deterministic output.
61
- */
62
- function printSpecTextRaw(specPath: string): void {
63
- const content = readFileSync(specPath, 'utf-8');
64
- console.log(content);
65
- }
66
-
67
- export class SpecCommand {
68
- private SPECS_DIR = 'prompter/specs';
69
-
70
- async show(specId?: string, options: ShowOptions = {}): Promise<void> {
71
- if (!specId) {
72
- const canPrompt = isInteractive(options);
73
- const specIds = await getSpecIds();
74
- if (canPrompt && specIds.length > 0) {
75
- const { select } = await import('@inquirer/prompts');
76
- specId = await select({
77
- message: 'Select a spec to show',
78
- choices: specIds.map(id => ({ name: id, value: id })),
79
- });
80
- } else {
81
- throw new Error('Missing required argument <spec-id>');
82
- }
83
- }
84
-
85
- const specPath = join(this.SPECS_DIR, specId, 'spec.md');
86
- if (!existsSync(specPath)) {
87
- throw new Error(`Spec '${specId}' not found at prompter/specs/${specId}/spec.md`);
88
- }
89
-
90
- if (options.json) {
91
- if (options.requirements && options.requirement) {
92
- throw new Error('Options --requirements and --requirement cannot be used together');
93
- }
94
- const parsed = parseSpecFromFile(specPath, specId);
95
- const filtered = filterSpec(parsed, options);
96
- const output = {
97
- id: specId,
98
- title: parsed.name,
99
- overview: parsed.overview,
100
- requirementCount: filtered.requirements.length,
101
- requirements: filtered.requirements,
102
- metadata: parsed.metadata ?? { version: '1.0.0', format: 'prompter' as const },
103
- };
104
- console.log(JSON.stringify(output, null, 2));
105
- return;
106
- }
107
- printSpecTextRaw(specPath);
108
- }
109
- }
110
-
111
- export function registerSpecCommand(rootProgram: typeof program) {
112
- const specCommand = rootProgram
113
- .command('spec')
114
- .description('Manage and view Prompter specifications');
115
-
116
- // Deprecation notice for noun-based commands
117
- specCommand.hook('preAction', () => {
118
- console.error('Warning: The "prompter spec ..." commands are deprecated. Prefer verb-first commands (e.g., "prompter show", "prompter validate --specs").');
119
- });
120
-
121
- specCommand
122
- .command('show [spec-id]')
123
- .description('Display a specific specification')
124
- .option('--json', 'Output as JSON')
125
- .option('--requirements', 'JSON only: Show only requirements (exclude scenarios)')
126
- .option('--no-scenarios', 'JSON only: Exclude scenario content')
127
- .option('-r, --requirement <id>', 'JSON only: Show specific requirement by ID (1-based)')
128
- .option('--no-interactive', 'Disable interactive prompts')
129
- .action(async (specId: string | undefined, options: ShowOptions & { noInteractive?: boolean }) => {
130
- try {
131
- const cmd = new SpecCommand();
132
- await cmd.show(specId, options as any);
133
- } catch (error) {
134
- console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
135
- process.exitCode = 1;
136
- }
137
- });
138
-
139
- specCommand
140
- .command('list')
141
- .description('List all available specifications')
142
- .option('--json', 'Output as JSON')
143
- .option('--long', 'Show id and title with counts')
144
- .action((options: { json?: boolean; long?: boolean }) => {
145
- try {
146
- if (!existsSync(SPECS_DIR)) {
147
- console.log('No items found');
148
- return;
149
- }
150
-
151
- const specs = readdirSync(SPECS_DIR, { withFileTypes: true })
152
- .filter(dirent => dirent.isDirectory())
153
- .map(dirent => {
154
- const specPath = join(SPECS_DIR, dirent.name, 'spec.md');
155
- if (existsSync(specPath)) {
156
- try {
157
- const spec = parseSpecFromFile(specPath, dirent.name);
158
-
159
- return {
160
- id: dirent.name,
161
- title: spec.name,
162
- requirementCount: spec.requirements.length
163
- };
164
- } catch {
165
- return {
166
- id: dirent.name,
167
- title: dirent.name,
168
- requirementCount: 0
169
- };
170
- }
171
- }
172
- return null;
173
- })
174
- .filter((spec): spec is { id: string; title: string; requirementCount: number } => spec !== null)
175
- .sort((a, b) => a.id.localeCompare(b.id));
176
-
177
- if (options.json) {
178
- console.log(JSON.stringify(specs, null, 2));
179
- } else {
180
- if (specs.length === 0) {
181
- console.log('No items found');
182
- return;
183
- }
184
- if (!options.long) {
185
- specs.forEach(spec => console.log(spec.id));
186
- return;
187
- }
188
- specs.forEach(spec => {
189
- console.log(`${spec.id}: ${spec.title} [requirements ${spec.requirementCount}]`);
190
- });
191
- }
192
- } catch (error) {
193
- console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
194
- process.exitCode = 1;
195
- }
196
- });
197
-
198
- specCommand
199
- .command('validate [spec-id]')
200
- .description('Validate a specification structure')
201
- .option('--strict', 'Enable strict validation mode')
202
- .option('--json', 'Output validation report as JSON')
203
- .option('--no-interactive', 'Disable interactive prompts')
204
- .action(async (specId: string | undefined, options: { strict?: boolean; json?: boolean; noInteractive?: boolean }) => {
205
- try {
206
- if (!specId) {
207
- const canPrompt = isInteractive(options);
208
- const specIds = await getSpecIds();
209
- if (canPrompt && specIds.length > 0) {
210
- const { select } = await import('@inquirer/prompts');
211
- specId = await select({
212
- message: 'Select a spec to validate',
213
- choices: specIds.map(id => ({ name: id, value: id })),
214
- });
215
- } else {
216
- throw new Error('Missing required argument <spec-id>');
217
- }
218
- }
219
-
220
- const specPath = join(SPECS_DIR, specId, 'spec.md');
221
-
222
- if (!existsSync(specPath)) {
223
- throw new Error(`Spec '${specId}' not found at prompter/specs/${specId}/spec.md`);
224
- }
225
-
226
- const validator = new Validator(options.strict);
227
- const report = await validator.validateSpec(specPath);
228
-
229
- if (options.json) {
230
- console.log(JSON.stringify(report, null, 2));
231
- } else {
232
- if (report.valid) {
233
- console.log(`Specification '${specId}' is valid`);
234
- } else {
235
- console.error(`Specification '${specId}' has issues`);
236
- report.issues.forEach(issue => {
237
- const label = issue.level === 'ERROR' ? 'ERROR' : issue.level;
238
- const prefix = issue.level === 'ERROR' ? '✗' : issue.level === 'WARNING' ? '⚠' : 'ℹ';
239
- console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`);
240
- });
241
- }
242
- }
243
- process.exitCode = report.valid ? 0 : 1;
244
- } catch (error) {
245
- console.error(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
246
- process.exitCode = 1;
247
- }
248
- });
249
-
250
- return specCommand;
251
- }