@dedesfr/prompter 0.9.0 → 1.1.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 (225) hide show
  1. package/CHANGELOG.md +35 -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 +35 -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 +0 -2
  17. package/dist/commands/update.d.ts.map +1 -1
  18. package/dist/commands/update.js +19 -48
  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/config.d.ts +0 -7
  29. package/dist/core/config.d.ts.map +1 -1
  30. package/dist/core/config.js +0 -128
  31. package/dist/core/config.js.map +1 -1
  32. package/dist/core/registry.d.ts +18 -0
  33. package/dist/core/registry.d.ts.map +1 -0
  34. package/dist/core/registry.js +94 -0
  35. package/dist/core/registry.js.map +1 -0
  36. package/package.json +7 -1
  37. package/AGENTS.md +0 -123
  38. package/CLAUDE.md +0 -17
  39. package/build.js +0 -20
  40. package/convex-setup.md +0 -403
  41. package/dist/core/prompt-templates.d.ts +0 -23
  42. package/dist/core/prompt-templates.d.ts.map +0 -1
  43. package/dist/core/prompt-templates.js +0 -3485
  44. package/dist/core/prompt-templates.js.map +0 -1
  45. package/prompt/ai-humanizer.md +0 -45
  46. package/prompt/api-contract-generator.md +0 -234
  47. package/prompt/apply.md +0 -17
  48. package/prompt/archive.md +0 -21
  49. package/prompt/design-system.md +0 -210
  50. package/prompt/document-explainer.md +0 -149
  51. package/prompt/epic-generator.md +0 -198
  52. package/prompt/epic-single.md +0 -47
  53. package/prompt/erd-generator.md +0 -130
  54. package/prompt/fsd-generator.md +0 -157
  55. package/prompt/prd-agent-generator.md +0 -147
  56. package/prompt/prd-generator.md +0 -195
  57. package/prompt/product-brief.md +0 -289
  58. package/prompt/proposal.md +0 -22
  59. package/prompt/qa-test-scenario.md +0 -133
  60. package/prompt/skill-creator.md +0 -350
  61. package/prompt/story-generator.md +0 -278
  62. package/prompt/story-single.md +0 -70
  63. package/prompt/tdd-generator.md +0 -294
  64. package/prompt/tdd-lite-generator.md +0 -224
  65. package/prompt/wireframe-generator.md +0 -219
  66. package/skills/ai-context-generator/SKILL.md +0 -54
  67. package/skills/ai-context-generator/references/AGENTS.template.md +0 -83
  68. package/skills/ai-context-generator/references/CLAUDE.template.md +0 -39
  69. package/skills/ai-context-generator/references/behavioral-guidelines.md +0 -71
  70. package/skills/ai-context-generator/references/discovery-checklist.md +0 -40
  71. package/skills/ai-context-generator/references/examples/AGENTS.good.md +0 -103
  72. package/skills/ai-context-generator/references/extraction-checklist.md +0 -23
  73. package/skills/ai-context-generator/references/overlays/laravel.md +0 -44
  74. package/skills/ai-humanizer/SKILL.md +0 -50
  75. package/skills/api-contract-generator/SKILL.md +0 -243
  76. package/skills/apply/SKILL.md +0 -23
  77. package/skills/archive/SKILL.md +0 -27
  78. package/skills/cerebro/SKILL.md +0 -187
  79. package/skills/cerebro/references/agents.md +0 -213
  80. package/skills/code-review/SKILL.md +0 -373
  81. package/skills/code-review/assets/report-template-agent.md +0 -212
  82. package/skills/code-review/assets/report-template-compact.md +0 -81
  83. package/skills/code-review/assets/report-template-full.md +0 -264
  84. package/skills/code-review/assets/report-template-human.md +0 -168
  85. package/skills/code-review/references/universal-patterns.md +0 -495
  86. package/skills/design-md/README.md +0 -34
  87. package/skills/design-md/SKILL.md +0 -172
  88. package/skills/design-md/examples/DESIGN.md +0 -154
  89. package/skills/design-system/SKILL.md +0 -216
  90. package/skills/design-system-generator/SKILL.md +0 -324
  91. package/skills/design-system-generator/assets/design-system-template.md +0 -348
  92. package/skills/design-system-generator/references/extraction-patterns.md +0 -321
  93. package/skills/doc-builder/SKILL.md +0 -115
  94. package/skills/doc-builder/references/ui-patterns.md +0 -394
  95. package/skills/document-explainer/SKILL.md +0 -155
  96. package/skills/document-translator/SKILL.md +0 -58
  97. package/skills/enhance/SKILL.md +0 -47
  98. package/skills/enhance-prompt/README.md +0 -34
  99. package/skills/enhance-prompt/SKILL.md +0 -204
  100. package/skills/enhance-prompt/references/KEYWORDS.md +0 -114
  101. package/skills/epic-generator/SKILL.md +0 -204
  102. package/skills/epic-single/SKILL.md +0 -63
  103. package/skills/erd-generator/SKILL.md +0 -138
  104. package/skills/feature-planner/SKILL.md +0 -305
  105. package/skills/feature-planner/assets/implementation-plan-template.md +0 -85
  106. package/skills/frontend-design/LICENSE.txt +0 -177
  107. package/skills/frontend-design/SKILL.md +0 -42
  108. package/skills/fsd-generator/SKILL.md +0 -163
  109. package/skills/gamma-builder/SKILL.md +0 -134
  110. package/skills/laravel-code-review/SKILL.md +0 -383
  111. package/skills/laravel-code-review/assets/report-template-agent.md +0 -195
  112. package/skills/laravel-code-review/assets/report-template-compact.md +0 -79
  113. package/skills/laravel-code-review/assets/report-template-full.md +0 -253
  114. package/skills/laravel-code-review/assets/report-template-human.md +0 -159
  115. package/skills/laravel-code-review/references/laravel-patterns.md +0 -571
  116. package/skills/laravel-code-review/references/php84-features.md +0 -442
  117. package/skills/mcp-builder/LICENSE.txt +0 -202
  118. package/skills/mcp-builder/SKILL.md +0 -236
  119. package/skills/mcp-builder/reference/evaluation.md +0 -602
  120. package/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  121. package/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  122. package/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  123. package/skills/mcp-builder/scripts/connections.py +0 -151
  124. package/skills/mcp-builder/scripts/evaluation.py +0 -373
  125. package/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  126. package/skills/mcp-builder/scripts/requirements.txt +0 -2
  127. package/skills/meeting-notes/SKILL.md +0 -159
  128. package/skills/meeting-notes/evals/evals.json +0 -23
  129. package/skills/prd-agent-generator/SKILL.md +0 -132
  130. package/skills/prd-generator/SKILL.md +0 -211
  131. package/skills/product-brief/SKILL.md +0 -141
  132. package/skills/project-orchestrator/SKILL.md +0 -487
  133. package/skills/project-orchestrator/assets/caddy-vps-setup.md +0 -180
  134. package/skills/project-orchestrator/assets/plan-summary-template.md +0 -159
  135. package/skills/prompter-specs/SKILL.md +0 -115
  136. package/skills/prompter-workflow/SKILL.md +0 -166
  137. package/skills/prompter-workflow/evals/evals.json +0 -89
  138. package/skills/proposal/SKILL.md +0 -28
  139. package/skills/qa-test-scenario/SKILL.md +0 -149
  140. package/skills/skill-creator/SKILL.md +0 -173
  141. package/skills/sph-generator/SKILL.md +0 -488
  142. package/skills/story-generator/SKILL.md +0 -285
  143. package/skills/story-single/SKILL.md +0 -86
  144. package/skills/tdd-generator/SKILL.md +0 -300
  145. package/skills/tdd-lite-generator/SKILL.md +0 -230
  146. package/skills/ui-ux-pro/SKILL.md +0 -199
  147. package/skills/ui-ux-pro/assets/design-spec-template.md +0 -173
  148. package/skills/ui-ux-pro/references/component-patterns.md +0 -255
  149. package/skills/ui-ux-pro/references/design-principles.md +0 -167
  150. package/skills/wireframe-generator/SKILL.md +0 -227
  151. package/src/cli/index.ts +0 -223
  152. package/src/commands/archive.ts +0 -302
  153. package/src/commands/change.ts +0 -292
  154. package/src/commands/config.ts +0 -233
  155. package/src/commands/guide.ts +0 -50
  156. package/src/commands/init.ts +0 -597
  157. package/src/commands/list.ts +0 -194
  158. package/src/commands/show.ts +0 -138
  159. package/src/commands/spec.ts +0 -251
  160. package/src/commands/update.ts +0 -129
  161. package/src/commands/upgrade.ts +0 -30
  162. package/src/commands/validate.ts +0 -326
  163. package/src/core/artifact-graph/graph.ts +0 -167
  164. package/src/core/artifact-graph/index.ts +0 -44
  165. package/src/core/artifact-graph/instruction-loader.ts +0 -302
  166. package/src/core/artifact-graph/resolver.ts +0 -226
  167. package/src/core/artifact-graph/schema.ts +0 -124
  168. package/src/core/artifact-graph/state.ts +0 -64
  169. package/src/core/artifact-graph/types.ts +0 -65
  170. package/src/core/completions/command-registry.ts +0 -382
  171. package/src/core/completions/completion-provider.ts +0 -128
  172. package/src/core/completions/generators/bash-generator.ts +0 -191
  173. package/src/core/completions/generators/fish-generator.ts +0 -188
  174. package/src/core/completions/generators/powershell-generator.ts +0 -223
  175. package/src/core/completions/generators/zsh-generator.ts +0 -281
  176. package/src/core/completions/templates/bash-templates.ts +0 -24
  177. package/src/core/completions/templates/fish-templates.ts +0 -40
  178. package/src/core/completions/templates/powershell-templates.ts +0 -25
  179. package/src/core/completions/templates/zsh-templates.ts +0 -36
  180. package/src/core/completions/types.ts +0 -90
  181. package/src/core/config-schema.ts +0 -230
  182. package/src/core/config.ts +0 -181
  183. package/src/core/configurators/slash/antigravity.ts +0 -10
  184. package/src/core/configurators/slash/base.ts +0 -109
  185. package/src/core/configurators/slash/claude.ts +0 -10
  186. package/src/core/configurators/slash/codex.ts +0 -10
  187. package/src/core/configurators/slash/droid.ts +0 -10
  188. package/src/core/configurators/slash/forge.ts +0 -10
  189. package/src/core/configurators/slash/github-copilot.ts +0 -10
  190. package/src/core/configurators/slash/index.ts +0 -10
  191. package/src/core/configurators/slash/kilocode.ts +0 -10
  192. package/src/core/configurators/slash/opencode.ts +0 -10
  193. package/src/core/configurators/slash/registry.ts +0 -51
  194. package/src/core/converters/json-converter.ts +0 -62
  195. package/src/core/global-config.ts +0 -136
  196. package/src/core/parsers/change-parser.ts +0 -234
  197. package/src/core/parsers/markdown-parser.ts +0 -237
  198. package/src/core/parsers/requirement-blocks.ts +0 -234
  199. package/src/core/prompt-templates.ts +0 -3504
  200. package/src/core/schemas/base.schema.ts +0 -20
  201. package/src/core/schemas/change.schema.ts +0 -42
  202. package/src/core/schemas/index.ts +0 -20
  203. package/src/core/schemas/spec.schema.ts +0 -17
  204. package/src/core/skill-discovery.ts +0 -68
  205. package/src/core/specs-apply.ts +0 -483
  206. package/src/core/styles/palette.ts +0 -8
  207. package/src/core/templates/agents-template.ts +0 -459
  208. package/src/core/templates/claude-template.ts +0 -2
  209. package/src/core/templates/index.ts +0 -3
  210. package/src/core/templates/project-template.ts +0 -32
  211. package/src/core/validation/constants.ts +0 -48
  212. package/src/core/validation/types.ts +0 -19
  213. package/src/core/validation/validator.ts +0 -449
  214. package/src/core/view.ts +0 -219
  215. package/src/index.ts +0 -1
  216. package/src/utils/change-metadata.ts +0 -171
  217. package/src/utils/change-utils.ts +0 -131
  218. package/src/utils/file-system.ts +0 -252
  219. package/src/utils/index.ts +0 -12
  220. package/src/utils/interactive.ts +0 -29
  221. package/src/utils/item-discovery.ts +0 -66
  222. package/src/utils/match.ts +0 -26
  223. package/src/utils/shell-detection.ts +0 -62
  224. package/src/utils/task-progress.ts +0 -43
  225. package/tsconfig.json +0 -28
@@ -1,449 +0,0 @@
1
- import { z, ZodError } from 'zod';
2
- import { readFileSync, promises as fs } from 'fs';
3
- import path from 'path';
4
- import { SpecSchema, ChangeSchema, Spec, Change } from '../schemas/index.js';
5
- import { MarkdownParser } from '../parsers/markdown-parser.js';
6
- import { ChangeParser } from '../parsers/change-parser.js';
7
- import { ValidationReport, ValidationIssue, ValidationLevel } from './types.js';
8
- import {
9
- MIN_PURPOSE_LENGTH,
10
- MAX_REQUIREMENT_TEXT_LENGTH,
11
- VALIDATION_MESSAGES
12
- } from './constants.js';
13
- import { parseDeltaSpec, normalizeRequirementName } from '../parsers/requirement-blocks.js';
14
- import { FileSystemUtils } from '../../utils/file-system.js';
15
-
16
- export class Validator {
17
- private strictMode: boolean;
18
-
19
- constructor(strictMode: boolean = false) {
20
- this.strictMode = strictMode;
21
- }
22
-
23
- async validateSpec(filePath: string): Promise<ValidationReport> {
24
- const issues: ValidationIssue[] = [];
25
- const specName = this.extractNameFromPath(filePath);
26
- try {
27
- const content = readFileSync(filePath, 'utf-8');
28
- const parser = new MarkdownParser(content);
29
-
30
- const spec = parser.parseSpec(specName);
31
-
32
- const result = SpecSchema.safeParse(spec);
33
-
34
- if (!result.success) {
35
- issues.push(...this.convertZodErrors(result.error));
36
- }
37
-
38
- issues.push(...this.applySpecRules(spec, content));
39
-
40
- } catch (error) {
41
- const baseMessage = error instanceof Error ? error.message : 'Unknown error';
42
- const enriched = this.enrichTopLevelError(specName, baseMessage);
43
- issues.push({
44
- level: 'ERROR',
45
- path: 'file',
46
- message: enriched,
47
- });
48
- }
49
-
50
- return this.createReport(issues);
51
- }
52
-
53
- /**
54
- * Validate spec content from a string (used for pre-write validation of rebuilt specs)
55
- */
56
- async validateSpecContent(specName: string, content: string): Promise<ValidationReport> {
57
- const issues: ValidationIssue[] = [];
58
- try {
59
- const parser = new MarkdownParser(content);
60
- const spec = parser.parseSpec(specName);
61
- const result = SpecSchema.safeParse(spec);
62
- if (!result.success) {
63
- issues.push(...this.convertZodErrors(result.error));
64
- }
65
- issues.push(...this.applySpecRules(spec, content));
66
- } catch (error) {
67
- const baseMessage = error instanceof Error ? error.message : 'Unknown error';
68
- const enriched = this.enrichTopLevelError(specName, baseMessage);
69
- issues.push({ level: 'ERROR', path: 'file', message: enriched });
70
- }
71
- return this.createReport(issues);
72
- }
73
-
74
- async validateChange(filePath: string): Promise<ValidationReport> {
75
- const issues: ValidationIssue[] = [];
76
- const changeName = this.extractNameFromPath(filePath);
77
- try {
78
- const content = readFileSync(filePath, 'utf-8');
79
- const changeDir = path.dirname(filePath);
80
- const parser = new ChangeParser(content, changeDir);
81
-
82
- const change = await parser.parseChangeWithDeltas(changeName);
83
-
84
- const result = ChangeSchema.safeParse(change);
85
-
86
- if (!result.success) {
87
- issues.push(...this.convertZodErrors(result.error));
88
- }
89
-
90
- issues.push(...this.applyChangeRules(change, content));
91
-
92
- } catch (error) {
93
- const baseMessage = error instanceof Error ? error.message : 'Unknown error';
94
- const enriched = this.enrichTopLevelError(changeName, baseMessage);
95
- issues.push({
96
- level: 'ERROR',
97
- path: 'file',
98
- message: enriched,
99
- });
100
- }
101
-
102
- return this.createReport(issues);
103
- }
104
-
105
- /**
106
- * Validate delta-formatted spec files under a change directory.
107
- * Enforces:
108
- * - At least one delta across all files
109
- * - ADDED/MODIFIED: each requirement has SHALL/MUST and at least one scenario
110
- * - REMOVED: names only; no scenario/description required
111
- * - RENAMED: pairs well-formed
112
- * - No duplicates within sections; no cross-section conflicts per spec
113
- */
114
- async validateChangeDeltaSpecs(changeDir: string): Promise<ValidationReport> {
115
- const issues: ValidationIssue[] = [];
116
- const specsDir = path.join(changeDir, 'specs');
117
- let totalDeltas = 0;
118
- const missingHeaderSpecs: string[] = [];
119
- const emptySectionSpecs: Array<{ path: string; sections: string[] }> = [];
120
-
121
- try {
122
- const entries = await fs.readdir(specsDir, { withFileTypes: true });
123
- for (const entry of entries) {
124
- if (!entry.isDirectory()) continue;
125
- const specName = entry.name;
126
- const specFile = path.join(specsDir, specName, 'spec.md');
127
- let content: string | undefined;
128
- try {
129
- content = await fs.readFile(specFile, 'utf-8');
130
- } catch {
131
- continue;
132
- }
133
-
134
- const plan = parseDeltaSpec(content);
135
- const entryPath = `${specName}/spec.md`;
136
- const sectionNames: string[] = [];
137
- if (plan.sectionPresence.added) sectionNames.push('## ADDED Requirements');
138
- if (plan.sectionPresence.modified) sectionNames.push('## MODIFIED Requirements');
139
- if (plan.sectionPresence.removed) sectionNames.push('## REMOVED Requirements');
140
- if (plan.sectionPresence.renamed) sectionNames.push('## RENAMED Requirements');
141
- const hasSections = sectionNames.length > 0;
142
- const hasEntries = plan.added.length + plan.modified.length + plan.removed.length + plan.renamed.length > 0;
143
- if (!hasEntries) {
144
- if (hasSections) emptySectionSpecs.push({ path: entryPath, sections: sectionNames });
145
- else missingHeaderSpecs.push(entryPath);
146
- }
147
-
148
- const addedNames = new Set<string>();
149
- const modifiedNames = new Set<string>();
150
- const removedNames = new Set<string>();
151
- const renamedFrom = new Set<string>();
152
- const renamedTo = new Set<string>();
153
-
154
- // Validate ADDED
155
- for (const block of plan.added) {
156
- const key = normalizeRequirementName(block.name);
157
- totalDeltas++;
158
- if (addedNames.has(key)) {
159
- issues.push({ level: 'ERROR', path: entryPath, message: `Duplicate requirement in ADDED: "${block.name}"` });
160
- } else {
161
- addedNames.add(key);
162
- }
163
- const requirementText = this.extractRequirementText(block.raw);
164
- if (!requirementText) {
165
- issues.push({ level: 'ERROR', path: entryPath, message: `ADDED "${block.name}" is missing requirement text` });
166
- } else if (!this.containsShallOrMust(requirementText)) {
167
- issues.push({ level: 'ERROR', path: entryPath, message: `ADDED "${block.name}" must contain SHALL or MUST` });
168
- }
169
- const scenarioCount = this.countScenarios(block.raw);
170
- if (scenarioCount < 1) {
171
- issues.push({ level: 'ERROR', path: entryPath, message: `ADDED "${block.name}" must include at least one scenario` });
172
- }
173
- }
174
-
175
- // Validate MODIFIED
176
- for (const block of plan.modified) {
177
- const key = normalizeRequirementName(block.name);
178
- totalDeltas++;
179
- if (modifiedNames.has(key)) {
180
- issues.push({ level: 'ERROR', path: entryPath, message: `Duplicate requirement in MODIFIED: "${block.name}"` });
181
- } else {
182
- modifiedNames.add(key);
183
- }
184
- const requirementText = this.extractRequirementText(block.raw);
185
- if (!requirementText) {
186
- issues.push({ level: 'ERROR', path: entryPath, message: `MODIFIED "${block.name}" is missing requirement text` });
187
- } else if (!this.containsShallOrMust(requirementText)) {
188
- issues.push({ level: 'ERROR', path: entryPath, message: `MODIFIED "${block.name}" must contain SHALL or MUST` });
189
- }
190
- const scenarioCount = this.countScenarios(block.raw);
191
- if (scenarioCount < 1) {
192
- issues.push({ level: 'ERROR', path: entryPath, message: `MODIFIED "${block.name}" must include at least one scenario` });
193
- }
194
- }
195
-
196
- // Validate REMOVED (names only)
197
- for (const name of plan.removed) {
198
- const key = normalizeRequirementName(name);
199
- totalDeltas++;
200
- if (removedNames.has(key)) {
201
- issues.push({ level: 'ERROR', path: entryPath, message: `Duplicate requirement in REMOVED: "${name}"` });
202
- } else {
203
- removedNames.add(key);
204
- }
205
- }
206
-
207
- // Validate RENAMED pairs
208
- for (const { from, to } of plan.renamed) {
209
- const fromKey = normalizeRequirementName(from);
210
- const toKey = normalizeRequirementName(to);
211
- totalDeltas++;
212
- if (renamedFrom.has(fromKey)) {
213
- issues.push({ level: 'ERROR', path: entryPath, message: `Duplicate FROM in RENAMED: "${from}"` });
214
- } else {
215
- renamedFrom.add(fromKey);
216
- }
217
- if (renamedTo.has(toKey)) {
218
- issues.push({ level: 'ERROR', path: entryPath, message: `Duplicate TO in RENAMED: "${to}"` });
219
- } else {
220
- renamedTo.add(toKey);
221
- }
222
- }
223
-
224
- // Cross-section conflicts (within the same spec file)
225
- for (const n of modifiedNames) {
226
- if (removedNames.has(n)) {
227
- issues.push({ level: 'ERROR', path: entryPath, message: `Requirement present in both MODIFIED and REMOVED: "${n}"` });
228
- }
229
- if (addedNames.has(n)) {
230
- issues.push({ level: 'ERROR', path: entryPath, message: `Requirement present in both MODIFIED and ADDED: "${n}"` });
231
- }
232
- }
233
- for (const n of addedNames) {
234
- if (removedNames.has(n)) {
235
- issues.push({ level: 'ERROR', path: entryPath, message: `Requirement present in both ADDED and REMOVED: "${n}"` });
236
- }
237
- }
238
- for (const { from, to } of plan.renamed) {
239
- const fromKey = normalizeRequirementName(from);
240
- const toKey = normalizeRequirementName(to);
241
- if (modifiedNames.has(fromKey)) {
242
- issues.push({ level: 'ERROR', path: entryPath, message: `MODIFIED references old name from RENAMED. Use new header for "${to}"` });
243
- }
244
- if (addedNames.has(toKey)) {
245
- issues.push({ level: 'ERROR', path: entryPath, message: `RENAMED TO collides with ADDED for "${to}"` });
246
- }
247
- }
248
- }
249
- } catch {
250
- // If no specs dir, treat as no deltas
251
- }
252
-
253
- for (const { path: specPath, sections } of emptySectionSpecs) {
254
- issues.push({
255
- level: 'ERROR',
256
- path: specPath,
257
- message: `Delta sections ${this.formatSectionList(sections)} were found, but no requirement entries parsed. Ensure each section includes at least one "### Requirement:" block (REMOVED may use bullet list syntax).`,
258
- });
259
- }
260
- for (const path of missingHeaderSpecs) {
261
- issues.push({
262
- level: 'ERROR',
263
- path,
264
- message: 'No delta sections found. Add headers such as "## ADDED Requirements" or move non-delta notes outside specs/.',
265
- });
266
- }
267
-
268
- if (totalDeltas === 0) {
269
- issues.push({ level: 'ERROR', path: 'file', message: this.enrichTopLevelError('change', VALIDATION_MESSAGES.CHANGE_NO_DELTAS) });
270
- }
271
-
272
- return this.createReport(issues);
273
- }
274
-
275
- private convertZodErrors(error: ZodError): ValidationIssue[] {
276
- return error.issues.map(err => {
277
- let message = err.message;
278
- if (message === VALIDATION_MESSAGES.CHANGE_NO_DELTAS) {
279
- message = `${message}. ${VALIDATION_MESSAGES.GUIDE_NO_DELTAS}`;
280
- }
281
- return {
282
- level: 'ERROR' as ValidationLevel,
283
- path: err.path.join('.'),
284
- message,
285
- };
286
- });
287
- }
288
-
289
- private applySpecRules(spec: Spec, content: string): ValidationIssue[] {
290
- const issues: ValidationIssue[] = [];
291
-
292
- if (spec.overview.length < MIN_PURPOSE_LENGTH) {
293
- issues.push({
294
- level: 'WARNING',
295
- path: 'overview',
296
- message: VALIDATION_MESSAGES.PURPOSE_TOO_BRIEF,
297
- });
298
- }
299
-
300
- spec.requirements.forEach((req, index) => {
301
- if (req.text.length > MAX_REQUIREMENT_TEXT_LENGTH) {
302
- issues.push({
303
- level: 'INFO',
304
- path: `requirements[${index}]`,
305
- message: VALIDATION_MESSAGES.REQUIREMENT_TOO_LONG,
306
- });
307
- }
308
-
309
- if (req.scenarios.length === 0) {
310
- issues.push({
311
- level: 'WARNING',
312
- path: `requirements[${index}].scenarios`,
313
- message: `${VALIDATION_MESSAGES.REQUIREMENT_NO_SCENARIOS}. ${VALIDATION_MESSAGES.GUIDE_SCENARIO_FORMAT}`,
314
- });
315
- }
316
- });
317
-
318
- return issues;
319
- }
320
-
321
- private applyChangeRules(change: Change, content: string): ValidationIssue[] {
322
- const issues: ValidationIssue[] = [];
323
-
324
- const MIN_DELTA_DESCRIPTION_LENGTH = 10;
325
-
326
- change.deltas.forEach((delta, index) => {
327
- if (!delta.description || delta.description.length < MIN_DELTA_DESCRIPTION_LENGTH) {
328
- issues.push({
329
- level: 'WARNING',
330
- path: `deltas[${index}].description`,
331
- message: VALIDATION_MESSAGES.DELTA_DESCRIPTION_TOO_BRIEF,
332
- });
333
- }
334
-
335
- if ((delta.operation === 'ADDED' || delta.operation === 'MODIFIED') &&
336
- (!delta.requirements || delta.requirements.length === 0)) {
337
- issues.push({
338
- level: 'WARNING',
339
- path: `deltas[${index}].requirements`,
340
- message: `${delta.operation} ${VALIDATION_MESSAGES.DELTA_MISSING_REQUIREMENTS}`,
341
- });
342
- }
343
- });
344
-
345
- return issues;
346
- }
347
-
348
- private enrichTopLevelError(itemId: string, baseMessage: string): string {
349
- const msg = baseMessage.trim();
350
- if (msg === VALIDATION_MESSAGES.CHANGE_NO_DELTAS) {
351
- return `${msg}. ${VALIDATION_MESSAGES.GUIDE_NO_DELTAS}`;
352
- }
353
- if (msg.includes('Spec must have a Purpose section') || msg.includes('Spec must have a Requirements section')) {
354
- return `${msg}. ${VALIDATION_MESSAGES.GUIDE_MISSING_SPEC_SECTIONS}`;
355
- }
356
- if (msg.includes('Change must have a Why section') || msg.includes('Change must have a What Changes section')) {
357
- return `${msg}. ${VALIDATION_MESSAGES.GUIDE_MISSING_CHANGE_SECTIONS}`;
358
- }
359
- return msg;
360
- }
361
-
362
- private extractNameFromPath(filePath: string): string {
363
- const normalizedPath = FileSystemUtils.toPosixPath(filePath);
364
- const parts = normalizedPath.split('/');
365
-
366
- // Look for the directory name after 'specs' or 'changes'
367
- for (let i = parts.length - 1; i >= 0; i--) {
368
- if (parts[i] === 'specs' || parts[i] === 'changes') {
369
- if (i < parts.length - 1) {
370
- return parts[i + 1];
371
- }
372
- }
373
- }
374
-
375
- // Fallback to filename without extension if not in expected structure
376
- const fileName = parts[parts.length - 1] ?? '';
377
- const dotIndex = fileName.lastIndexOf('.');
378
- return dotIndex > 0 ? fileName.slice(0, dotIndex) : fileName;
379
- }
380
-
381
- private createReport(issues: ValidationIssue[]): ValidationReport {
382
- const errors = issues.filter(i => i.level === 'ERROR').length;
383
- const warnings = issues.filter(i => i.level === 'WARNING').length;
384
- const info = issues.filter(i => i.level === 'INFO').length;
385
-
386
- const valid = this.strictMode
387
- ? errors === 0 && warnings === 0
388
- : errors === 0;
389
-
390
- return {
391
- valid,
392
- issues,
393
- summary: {
394
- errors,
395
- warnings,
396
- info,
397
- },
398
- };
399
- }
400
-
401
- isValid(report: ValidationReport): boolean {
402
- return report.valid;
403
- }
404
-
405
- private extractRequirementText(blockRaw: string): string | undefined {
406
- const lines = blockRaw.split('\n');
407
- // Skip header line (index 0)
408
- let i = 1;
409
-
410
- // Find the first substantial text line, skipping metadata and blank lines
411
- for (; i < lines.length; i++) {
412
- const line = lines[i];
413
-
414
- // Stop at scenario headers
415
- if (/^####\s+/.test(line)) break;
416
-
417
- const trimmed = line.trim();
418
-
419
- // Skip blank lines
420
- if (trimmed.length === 0) continue;
421
-
422
- // Skip metadata lines (lines starting with ** like **ID**, **Priority**, etc.)
423
- if (/^\*\*[^*]+\*\*:/.test(trimmed)) continue;
424
-
425
- // Found first non-metadata, non-blank line - this is the requirement text
426
- return trimmed;
427
- }
428
-
429
- // No requirement text found
430
- return undefined;
431
- }
432
-
433
- private containsShallOrMust(text: string): boolean {
434
- return /\b(SHALL|MUST)\b/.test(text);
435
- }
436
-
437
- private countScenarios(blockRaw: string): number {
438
- const matches = blockRaw.match(/^####\s+/gm);
439
- return matches ? matches.length : 0;
440
- }
441
-
442
- private formatSectionList(sections: string[]): string {
443
- if (sections.length === 0) return '';
444
- if (sections.length === 1) return sections[0];
445
- const head = sections.slice(0, -1);
446
- const last = sections[sections.length - 1];
447
- return `${head.join(', ')} and ${last}`;
448
- }
449
- }
package/src/core/view.ts DELETED
@@ -1,219 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import chalk from 'chalk';
4
- import { getTaskProgressForChange, formatTaskStatus } from '../utils/task-progress.js';
5
- import { MarkdownParser } from './parsers/markdown-parser.js';
6
-
7
- export class ViewCommand {
8
- async execute(targetPath: string = '.'): Promise<void> {
9
- const prompterDir = path.join(targetPath, 'prompter');
10
-
11
- if (!fs.existsSync(prompterDir)) {
12
- console.error(chalk.red('No prompter directory found'));
13
- process.exit(1);
14
- }
15
-
16
- console.log(chalk.bold('\nPrompter Dashboard\n'));
17
- console.log('═'.repeat(60));
18
-
19
- // Get changes and specs data
20
- const changesData = await this.getChangesData(prompterDir);
21
- const specsData = await this.getSpecsData(prompterDir);
22
-
23
- // Display summary metrics
24
- this.displaySummary(changesData, specsData);
25
-
26
- // Display draft changes
27
- if (changesData.draft.length > 0) {
28
- console.log(chalk.bold.gray('\nDraft Changes'));
29
- console.log('─'.repeat(60));
30
- changesData.draft.forEach((change) => {
31
- console.log(` ${chalk.gray('○')} ${change.name}`);
32
- });
33
- }
34
-
35
- // Display active changes
36
- if (changesData.active.length > 0) {
37
- console.log(chalk.bold.cyan('\nActive Changes'));
38
- console.log('─'.repeat(60));
39
- changesData.active.forEach((change) => {
40
- const progressBar = this.createProgressBar(change.progress.completed, change.progress.total);
41
- const percentage =
42
- change.progress.total > 0
43
- ? Math.round((change.progress.completed / change.progress.total) * 100)
44
- : 0;
45
-
46
- console.log(
47
- ` ${chalk.yellow('◉')} ${chalk.bold(change.name.padEnd(30))} ${progressBar} ${chalk.dim(`${percentage}%`)}`
48
- );
49
- });
50
- }
51
-
52
- // Display completed changes
53
- if (changesData.completed.length > 0) {
54
- console.log(chalk.bold.green('\nCompleted Changes'));
55
- console.log('─'.repeat(60));
56
- changesData.completed.forEach((change) => {
57
- console.log(` ${chalk.green('✓')} ${change.name}`);
58
- });
59
- }
60
-
61
- // Display specifications
62
- if (specsData.length > 0) {
63
- console.log(chalk.bold.blue('\nSpecifications'));
64
- console.log('─'.repeat(60));
65
-
66
- // Sort specs by requirement count (descending)
67
- specsData.sort((a, b) => b.requirementCount - a.requirementCount);
68
-
69
- specsData.forEach(spec => {
70
- const reqLabel = spec.requirementCount === 1 ? 'requirement' : 'requirements';
71
- console.log(
72
- ` ${chalk.blue('▪')} ${chalk.bold(spec.name.padEnd(30))} ${chalk.dim(`${spec.requirementCount} ${reqLabel}`)}`
73
- );
74
- });
75
- }
76
-
77
- console.log('\n' + '═'.repeat(60));
78
- console.log(chalk.dim(`\nUse ${chalk.white('prompter list --changes')} or ${chalk.white('prompter list --specs')} for detailed views`));
79
- }
80
-
81
- private async getChangesData(prompterDir: string): Promise<{
82
- draft: Array<{ name: string }>;
83
- active: Array<{ name: string; progress: { total: number; completed: number } }>;
84
- completed: Array<{ name: string }>;
85
- }> {
86
- const changesDir = path.join(prompterDir, 'changes');
87
-
88
- if (!fs.existsSync(changesDir)) {
89
- return { draft: [], active: [], completed: [] };
90
- }
91
-
92
- const draft: Array<{ name: string }> = [];
93
- const active: Array<{ name: string; progress: { total: number; completed: number } }> = [];
94
- const completed: Array<{ name: string }> = [];
95
-
96
- const entries = fs.readdirSync(changesDir, { withFileTypes: true });
97
-
98
- for (const entry of entries) {
99
- if (entry.isDirectory() && entry.name !== 'archive') {
100
- const progress = await getTaskProgressForChange(changesDir, entry.name);
101
-
102
- if (progress.total === 0) {
103
- // No tasks defined yet - still in planning/draft phase
104
- draft.push({ name: entry.name });
105
- } else if (progress.completed === progress.total) {
106
- // All tasks complete
107
- completed.push({ name: entry.name });
108
- } else {
109
- // Has tasks but not all complete
110
- active.push({ name: entry.name, progress });
111
- }
112
- }
113
- }
114
-
115
- // Sort all categories by name for deterministic ordering
116
- draft.sort((a, b) => a.name.localeCompare(b.name));
117
-
118
- // Sort active changes by completion percentage (ascending) and then by name
119
- active.sort((a, b) => {
120
- const percentageA = a.progress.total > 0 ? a.progress.completed / a.progress.total : 0;
121
- const percentageB = b.progress.total > 0 ? b.progress.completed / b.progress.total : 0;
122
-
123
- if (percentageA < percentageB) return -1;
124
- if (percentageA > percentageB) return 1;
125
- return a.name.localeCompare(b.name);
126
- });
127
- completed.sort((a, b) => a.name.localeCompare(b.name));
128
-
129
- return { draft, active, completed };
130
- }
131
-
132
- private async getSpecsData(prompterDir: string): Promise<Array<{ name: string; requirementCount: number }>> {
133
- const specsDir = path.join(prompterDir, 'specs');
134
-
135
- if (!fs.existsSync(specsDir)) {
136
- return [];
137
- }
138
-
139
- const specs: Array<{ name: string; requirementCount: number }> = [];
140
- const entries = fs.readdirSync(specsDir, { withFileTypes: true });
141
-
142
- for (const entry of entries) {
143
- if (entry.isDirectory()) {
144
- const specFile = path.join(specsDir, entry.name, 'spec.md');
145
-
146
- if (fs.existsSync(specFile)) {
147
- try {
148
- const content = fs.readFileSync(specFile, 'utf-8');
149
- const parser = new MarkdownParser(content);
150
- const spec = parser.parseSpec(entry.name);
151
- const requirementCount = spec.requirements.length;
152
- specs.push({ name: entry.name, requirementCount });
153
- } catch (error) {
154
- // If spec cannot be parsed, include with 0 count
155
- specs.push({ name: entry.name, requirementCount: 0 });
156
- }
157
- }
158
- }
159
- }
160
-
161
- return specs;
162
- }
163
-
164
- private displaySummary(
165
- changesData: { draft: any[]; active: any[]; completed: any[] },
166
- specsData: any[]
167
- ): void {
168
- const totalChanges =
169
- changesData.draft.length + changesData.active.length + changesData.completed.length;
170
- const totalSpecs = specsData.length;
171
- const totalRequirements = specsData.reduce((sum, spec) => sum + spec.requirementCount, 0);
172
-
173
- // Calculate total task progress
174
- let totalTasks = 0;
175
- let completedTasks = 0;
176
-
177
- changesData.active.forEach((change) => {
178
- totalTasks += change.progress.total;
179
- completedTasks += change.progress.completed;
180
- });
181
-
182
- changesData.completed.forEach(() => {
183
- // Completed changes count as 100% done (we don't know exact task count)
184
- // This is a simplification
185
- });
186
-
187
- console.log(chalk.bold('Summary:'));
188
- console.log(
189
- ` ${chalk.cyan('●')} Specifications: ${chalk.bold(totalSpecs)} specs, ${chalk.bold(totalRequirements)} requirements`
190
- );
191
- if (changesData.draft.length > 0) {
192
- console.log(` ${chalk.gray('●')} Draft Changes: ${chalk.bold(changesData.draft.length)}`);
193
- }
194
- console.log(
195
- ` ${chalk.yellow('●')} Active Changes: ${chalk.bold(changesData.active.length)} in progress`
196
- );
197
- console.log(` ${chalk.green('●')} Completed Changes: ${chalk.bold(changesData.completed.length)}`);
198
-
199
- if (totalTasks > 0) {
200
- const overallProgress = Math.round((completedTasks / totalTasks) * 100);
201
- console.log(
202
- ` ${chalk.magenta('●')} Task Progress: ${chalk.bold(`${completedTasks}/${totalTasks}`)} (${overallProgress}% complete)`
203
- );
204
- }
205
- }
206
-
207
- private createProgressBar(completed: number, total: number, width: number = 20): string {
208
- if (total === 0) return chalk.dim('─'.repeat(width));
209
-
210
- const percentage = completed / total;
211
- const filled = Math.round(percentage * width);
212
- const empty = width - filled;
213
-
214
- const filledBar = chalk.green('█'.repeat(filled));
215
- const emptyBar = chalk.dim('░'.repeat(empty));
216
-
217
- return `[${filledBar}${emptyBar}]`;
218
- }
219
- }
package/src/index.ts DELETED
@@ -1 +0,0 @@
1
- export { PrompterConfig } from './core/config.js';