@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,62 +0,0 @@
1
- import { readFileSync } from 'fs';
2
- import path from 'path';
3
- import { MarkdownParser } from '../parsers/markdown-parser.js';
4
- import { ChangeParser } from '../parsers/change-parser.js';
5
- import { Spec, Change } from '../schemas/index.js';
6
- import { FileSystemUtils } from '../../utils/file-system.js';
7
-
8
- export class JsonConverter {
9
- convertSpecToJson(filePath: string): string {
10
- const content = readFileSync(filePath, 'utf-8');
11
- const parser = new MarkdownParser(content);
12
- const specName = this.extractNameFromPath(filePath);
13
-
14
- const spec = parser.parseSpec(specName);
15
-
16
- const jsonSpec = {
17
- ...spec,
18
- metadata: {
19
- ...spec.metadata,
20
- sourcePath: filePath,
21
- },
22
- };
23
-
24
- return JSON.stringify(jsonSpec, null, 2);
25
- }
26
-
27
- async convertChangeToJson(filePath: string): Promise<string> {
28
- const content = readFileSync(filePath, 'utf-8');
29
- const changeName = this.extractNameFromPath(filePath);
30
- const changeDir = path.dirname(filePath);
31
- const parser = new ChangeParser(content, changeDir);
32
-
33
- const change = await parser.parseChangeWithDeltas(changeName);
34
-
35
- const jsonChange = {
36
- ...change,
37
- metadata: {
38
- ...change.metadata,
39
- sourcePath: filePath,
40
- },
41
- };
42
-
43
- return JSON.stringify(jsonChange, null, 2);
44
- }
45
-
46
- private extractNameFromPath(filePath: string): string {
47
- const normalizedPath = FileSystemUtils.toPosixPath(filePath);
48
- const parts = normalizedPath.split('/');
49
-
50
- for (let i = parts.length - 1; i >= 0; i--) {
51
- if (parts[i] === 'specs' || parts[i] === 'changes') {
52
- if (i < parts.length - 1) {
53
- return parts[i + 1];
54
- }
55
- }
56
- }
57
-
58
- const fileName = parts[parts.length - 1] ?? '';
59
- const dotIndex = fileName.lastIndexOf('.');
60
- return dotIndex > 0 ? fileName.slice(0, dotIndex) : fileName;
61
- }
62
- }
@@ -1,136 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as path from 'node:path';
3
- import * as os from 'node:os';
4
-
5
- // Constants
6
- export const GLOBAL_CONFIG_DIR_NAME = 'prompter';
7
- export const GLOBAL_CONFIG_FILE_NAME = 'config.json';
8
- export const GLOBAL_DATA_DIR_NAME = 'prompter';
9
-
10
- // TypeScript interfaces
11
- export interface GlobalConfig {
12
- featureFlags?: Record<string, boolean>;
13
- }
14
-
15
- const DEFAULT_CONFIG: GlobalConfig = {
16
- featureFlags: {}
17
- };
18
-
19
- /**
20
- * Gets the global configuration directory path following XDG Base Directory Specification.
21
- *
22
- * - All platforms: $XDG_CONFIG_HOME/prompter/ if XDG_CONFIG_HOME is set
23
- * - Unix/macOS fallback: ~/.config/prompter/
24
- * - Windows fallback: %APPDATA%/prompter/
25
- */
26
- export function getGlobalConfigDir(): string {
27
- // XDG_CONFIG_HOME takes precedence on all platforms when explicitly set
28
- const xdgConfigHome = process.env.XDG_CONFIG_HOME;
29
- if (xdgConfigHome) {
30
- return path.join(xdgConfigHome, GLOBAL_CONFIG_DIR_NAME);
31
- }
32
-
33
- const platform = os.platform();
34
-
35
- if (platform === 'win32') {
36
- // Windows: use %APPDATA%
37
- const appData = process.env.APPDATA;
38
- if (appData) {
39
- return path.join(appData, GLOBAL_CONFIG_DIR_NAME);
40
- }
41
- // Fallback for Windows if APPDATA is not set
42
- return path.join(os.homedir(), 'AppData', 'Roaming', GLOBAL_CONFIG_DIR_NAME);
43
- }
44
-
45
- // Unix/macOS fallback: ~/.config
46
- return path.join(os.homedir(), '.config', GLOBAL_CONFIG_DIR_NAME);
47
- }
48
-
49
- /**
50
- * Gets the global data directory path following XDG Base Directory Specification.
51
- * Used for user data like schema overrides.
52
- *
53
- * - All platforms: $XDG_DATA_HOME/prompter/ if XDG_DATA_HOME is set
54
- * - Unix/macOS fallback: ~/.local/share/prompter/
55
- * - Windows fallback: %LOCALAPPDATA%/prompter/
56
- */
57
- export function getGlobalDataDir(): string {
58
- // XDG_DATA_HOME takes precedence on all platforms when explicitly set
59
- const xdgDataHome = process.env.XDG_DATA_HOME;
60
- if (xdgDataHome) {
61
- return path.join(xdgDataHome, GLOBAL_DATA_DIR_NAME);
62
- }
63
-
64
- const platform = os.platform();
65
-
66
- if (platform === 'win32') {
67
- // Windows: use %LOCALAPPDATA%
68
- const localAppData = process.env.LOCALAPPDATA;
69
- if (localAppData) {
70
- return path.join(localAppData, GLOBAL_DATA_DIR_NAME);
71
- }
72
- // Fallback for Windows if LOCALAPPDATA is not set
73
- return path.join(os.homedir(), 'AppData', 'Local', GLOBAL_DATA_DIR_NAME);
74
- }
75
-
76
- // Unix/macOS fallback: ~/.local/share
77
- return path.join(os.homedir(), '.local', 'share', GLOBAL_DATA_DIR_NAME);
78
- }
79
-
80
- /**
81
- * Gets the path to the global config file.
82
- */
83
- export function getGlobalConfigPath(): string {
84
- return path.join(getGlobalConfigDir(), GLOBAL_CONFIG_FILE_NAME);
85
- }
86
-
87
- /**
88
- * Loads the global configuration from disk.
89
- * Returns default configuration if file doesn't exist or is invalid.
90
- * Merges loaded config with defaults to ensure new fields are available.
91
- */
92
- export function getGlobalConfig(): GlobalConfig {
93
- const configPath = getGlobalConfigPath();
94
-
95
- try {
96
- if (!fs.existsSync(configPath)) {
97
- return { ...DEFAULT_CONFIG };
98
- }
99
-
100
- const content = fs.readFileSync(configPath, 'utf-8');
101
- const parsed = JSON.parse(content);
102
-
103
- // Merge with defaults (loaded values take precedence)
104
- return {
105
- ...DEFAULT_CONFIG,
106
- ...parsed,
107
- // Deep merge featureFlags
108
- featureFlags: {
109
- ...DEFAULT_CONFIG.featureFlags,
110
- ...(parsed.featureFlags || {})
111
- }
112
- };
113
- } catch (error) {
114
- // Log warning for parse errors, but not for missing files
115
- if (error instanceof SyntaxError) {
116
- console.error(`Warning: Invalid JSON in ${configPath}, using defaults`);
117
- }
118
- return { ...DEFAULT_CONFIG };
119
- }
120
- }
121
-
122
- /**
123
- * Saves the global configuration to disk.
124
- * Creates the config directory if it doesn't exist.
125
- */
126
- export function saveGlobalConfig(config: GlobalConfig): void {
127
- const configDir = getGlobalConfigDir();
128
- const configPath = getGlobalConfigPath();
129
-
130
- // Create directory if it doesn't exist
131
- if (!fs.existsSync(configDir)) {
132
- fs.mkdirSync(configDir, { recursive: true });
133
- }
134
-
135
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
136
- }
@@ -1,234 +0,0 @@
1
- import { MarkdownParser, Section } from './markdown-parser.js';
2
- import { Change, Delta, DeltaOperation, Requirement } from '../schemas/index.js';
3
- import path from 'path';
4
- import { promises as fs } from 'fs';
5
-
6
- interface DeltaSection {
7
- operation: DeltaOperation;
8
- requirements: Requirement[];
9
- renames?: Array<{ from: string; to: string }>;
10
- }
11
-
12
- export class ChangeParser extends MarkdownParser {
13
- private changeDir: string;
14
-
15
- constructor(content: string, changeDir: string) {
16
- super(content);
17
- this.changeDir = changeDir;
18
- }
19
-
20
- async parseChangeWithDeltas(name: string): Promise<Change> {
21
- const sections = this.parseSections();
22
- const why = this.findSection(sections, 'Why')?.content || '';
23
- const whatChanges = this.findSection(sections, 'What Changes')?.content || '';
24
-
25
- if (!why) {
26
- throw new Error('Change must have a Why section');
27
- }
28
-
29
- if (!whatChanges) {
30
- throw new Error('Change must have a What Changes section');
31
- }
32
-
33
- // Parse deltas from the What Changes section (simple format)
34
- const simpleDeltas = this.parseDeltas(whatChanges);
35
-
36
- // Check if there are spec files with delta format
37
- const specsDir = path.join(this.changeDir, 'specs');
38
- const deltaDeltas = await this.parseDeltaSpecs(specsDir);
39
-
40
- // Combine both types of deltas, preferring delta format if available
41
- const deltas = deltaDeltas.length > 0 ? deltaDeltas : simpleDeltas;
42
-
43
- return {
44
- name,
45
- why: why.trim(),
46
- whatChanges: whatChanges.trim(),
47
- deltas,
48
- metadata: {
49
- version: '1.0.0',
50
- format: 'prompter-change',
51
- },
52
- };
53
- }
54
-
55
- private async parseDeltaSpecs(specsDir: string): Promise<Delta[]> {
56
- const deltas: Delta[] = [];
57
-
58
- try {
59
- const specDirs = await fs.readdir(specsDir, { withFileTypes: true });
60
-
61
- for (const dir of specDirs) {
62
- if (!dir.isDirectory()) continue;
63
-
64
- const specName = dir.name;
65
- const specFile = path.join(specsDir, specName, 'spec.md');
66
-
67
- try {
68
- const content = await fs.readFile(specFile, 'utf-8');
69
- const specDeltas = this.parseSpecDeltas(specName, content);
70
- deltas.push(...specDeltas);
71
- } catch (error) {
72
- // Spec file might not exist, which is okay
73
- continue;
74
- }
75
- }
76
- } catch (error) {
77
- // Specs directory might not exist, which is okay
78
- return [];
79
- }
80
-
81
- return deltas;
82
- }
83
-
84
- private parseSpecDeltas(specName: string, content: string): Delta[] {
85
- const deltas: Delta[] = [];
86
- const sections = this.parseSectionsFromContent(content);
87
-
88
- // Parse ADDED requirements
89
- const addedSection = this.findSection(sections, 'ADDED Requirements');
90
- if (addedSection) {
91
- const requirements = this.parseRequirements(addedSection);
92
- requirements.forEach(req => {
93
- deltas.push({
94
- spec: specName,
95
- operation: 'ADDED' as DeltaOperation,
96
- description: `Add requirement: ${req.text}`,
97
- // Provide both single and plural forms for compatibility
98
- requirement: req,
99
- requirements: [req],
100
- });
101
- });
102
- }
103
-
104
- // Parse MODIFIED requirements
105
- const modifiedSection = this.findSection(sections, 'MODIFIED Requirements');
106
- if (modifiedSection) {
107
- const requirements = this.parseRequirements(modifiedSection);
108
- requirements.forEach(req => {
109
- deltas.push({
110
- spec: specName,
111
- operation: 'MODIFIED' as DeltaOperation,
112
- description: `Modify requirement: ${req.text}`,
113
- requirement: req,
114
- requirements: [req],
115
- });
116
- });
117
- }
118
-
119
- // Parse REMOVED requirements
120
- const removedSection = this.findSection(sections, 'REMOVED Requirements');
121
- if (removedSection) {
122
- const requirements = this.parseRequirements(removedSection);
123
- requirements.forEach(req => {
124
- deltas.push({
125
- spec: specName,
126
- operation: 'REMOVED' as DeltaOperation,
127
- description: `Remove requirement: ${req.text}`,
128
- requirement: req,
129
- requirements: [req],
130
- });
131
- });
132
- }
133
-
134
- // Parse RENAMED requirements
135
- const renamedSection = this.findSection(sections, 'RENAMED Requirements');
136
- if (renamedSection) {
137
- const renames = this.parseRenames(renamedSection.content);
138
- renames.forEach(rename => {
139
- deltas.push({
140
- spec: specName,
141
- operation: 'RENAMED' as DeltaOperation,
142
- description: `Rename requirement from "${rename.from}" to "${rename.to}"`,
143
- rename,
144
- });
145
- });
146
- }
147
-
148
- return deltas;
149
- }
150
-
151
- private parseRenames(content: string): Array<{ from: string; to: string }> {
152
- const renames: Array<{ from: string; to: string }> = [];
153
- const lines = ChangeParser.normalizeContent(content).split('\n');
154
-
155
- let currentRename: { from?: string; to?: string } = {};
156
-
157
- for (const line of lines) {
158
- const fromMatch = line.match(/^\s*-?\s*FROM:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
159
- const toMatch = line.match(/^\s*-?\s*TO:\s*`?###\s*Requirement:\s*(.+?)`?\s*$/);
160
-
161
- if (fromMatch) {
162
- currentRename.from = fromMatch[1].trim();
163
- } else if (toMatch) {
164
- currentRename.to = toMatch[1].trim();
165
-
166
- if (currentRename.from && currentRename.to) {
167
- renames.push({
168
- from: currentRename.from,
169
- to: currentRename.to,
170
- });
171
- currentRename = {};
172
- }
173
- }
174
- }
175
-
176
- return renames;
177
- }
178
-
179
- private parseSectionsFromContent(content: string): Section[] {
180
- const normalizedContent = ChangeParser.normalizeContent(content);
181
- const lines = normalizedContent.split('\n');
182
- const sections: Section[] = [];
183
- const stack: Section[] = [];
184
-
185
- for (let i = 0; i < lines.length; i++) {
186
- const line = lines[i];
187
- const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
188
-
189
- if (headerMatch) {
190
- const level = headerMatch[1].length;
191
- const title = headerMatch[2].trim();
192
- const contentLines = this.getContentUntilNextHeaderFromLines(lines, i + 1, level);
193
-
194
- const section = {
195
- level,
196
- title,
197
- content: contentLines.join('\n').trim(),
198
- children: [],
199
- };
200
-
201
- while (stack.length > 0 && stack[stack.length - 1].level >= level) {
202
- stack.pop();
203
- }
204
-
205
- if (stack.length === 0) {
206
- sections.push(section);
207
- } else {
208
- stack[stack.length - 1].children.push(section);
209
- }
210
-
211
- stack.push(section);
212
- }
213
- }
214
-
215
- return sections;
216
- }
217
-
218
- private getContentUntilNextHeaderFromLines(lines: string[], startLine: number, currentLevel: number): string[] {
219
- const contentLines: string[] = [];
220
-
221
- for (let i = startLine; i < lines.length; i++) {
222
- const line = lines[i];
223
- const headerMatch = line.match(/^(#{1,6})\s+/);
224
-
225
- if (headerMatch && headerMatch[1].length <= currentLevel) {
226
- break;
227
- }
228
-
229
- contentLines.push(line);
230
- }
231
-
232
- return contentLines;
233
- }
234
- }
@@ -1,237 +0,0 @@
1
- import { Spec, Change, Requirement, Scenario, Delta, DeltaOperation } from '../schemas/index.js';
2
-
3
- export interface Section {
4
- level: number;
5
- title: string;
6
- content: string;
7
- children: Section[];
8
- }
9
-
10
- export class MarkdownParser {
11
- private lines: string[];
12
- private currentLine: number;
13
-
14
- constructor(content: string) {
15
- const normalized = MarkdownParser.normalizeContent(content);
16
- this.lines = normalized.split('\n');
17
- this.currentLine = 0;
18
- }
19
-
20
- protected static normalizeContent(content: string): string {
21
- return content.replace(/\r\n?/g, '\n');
22
- }
23
-
24
- parseSpec(name: string): Spec {
25
- const sections = this.parseSections();
26
- const purpose = this.findSection(sections, 'Purpose')?.content || '';
27
-
28
- const requirementsSection = this.findSection(sections, 'Requirements');
29
-
30
- if (!purpose) {
31
- throw new Error('Spec must have a Purpose section');
32
- }
33
-
34
- if (!requirementsSection) {
35
- throw new Error('Spec must have a Requirements section');
36
- }
37
-
38
- const requirements = this.parseRequirements(requirementsSection);
39
-
40
- return {
41
- name,
42
- overview: purpose.trim(),
43
- requirements,
44
- metadata: {
45
- version: '1.0.0',
46
- format: 'prompter',
47
- },
48
- };
49
- }
50
-
51
- parseChange(name: string): Change {
52
- const sections = this.parseSections();
53
- const why = this.findSection(sections, 'Why')?.content || '';
54
- const whatChanges = this.findSection(sections, 'What Changes')?.content || '';
55
-
56
- if (!why) {
57
- throw new Error('Change must have a Why section');
58
- }
59
-
60
- if (!whatChanges) {
61
- throw new Error('Change must have a What Changes section');
62
- }
63
-
64
- const deltas = this.parseDeltas(whatChanges);
65
-
66
- return {
67
- name,
68
- why: why.trim(),
69
- whatChanges: whatChanges.trim(),
70
- deltas,
71
- metadata: {
72
- version: '1.0.0',
73
- format: 'prompter-change',
74
- },
75
- };
76
- }
77
-
78
- protected parseSections(): Section[] {
79
- const sections: Section[] = [];
80
- const stack: Section[] = [];
81
-
82
- for (let i = 0; i < this.lines.length; i++) {
83
- const line = this.lines[i];
84
- const headerMatch = line.match(/^(#{1,6})\s+(.+)$/);
85
-
86
- if (headerMatch) {
87
- const level = headerMatch[1].length;
88
- const title = headerMatch[2].trim();
89
- const content = this.getContentUntilNextHeader(i + 1, level);
90
-
91
- const section: Section = {
92
- level,
93
- title,
94
- content,
95
- children: [],
96
- };
97
-
98
- while (stack.length > 0 && stack[stack.length - 1].level >= level) {
99
- stack.pop();
100
- }
101
-
102
- if (stack.length === 0) {
103
- sections.push(section);
104
- } else {
105
- stack[stack.length - 1].children.push(section);
106
- }
107
-
108
- stack.push(section);
109
- }
110
- }
111
-
112
- return sections;
113
- }
114
-
115
- protected getContentUntilNextHeader(startLine: number, currentLevel: number): string {
116
- const contentLines: string[] = [];
117
-
118
- for (let i = startLine; i < this.lines.length; i++) {
119
- const line = this.lines[i];
120
- const headerMatch = line.match(/^(#{1,6})\s+/);
121
-
122
- if (headerMatch && headerMatch[1].length <= currentLevel) {
123
- break;
124
- }
125
-
126
- contentLines.push(line);
127
- }
128
-
129
- return contentLines.join('\n').trim();
130
- }
131
-
132
- protected findSection(sections: Section[], title: string): Section | undefined {
133
- for (const section of sections) {
134
- if (section.title.toLowerCase() === title.toLowerCase()) {
135
- return section;
136
- }
137
- const child = this.findSection(section.children, title);
138
- if (child) {
139
- return child;
140
- }
141
- }
142
- return undefined;
143
- }
144
-
145
- protected parseRequirements(section: Section): Requirement[] {
146
- const requirements: Requirement[] = [];
147
-
148
- for (const child of section.children) {
149
- // Extract requirement text from first non-empty content line, fall back to heading
150
- let text = child.title;
151
-
152
- // Get content before any child sections (scenarios)
153
- if (child.content.trim()) {
154
- // Split content into lines and find content before any child headers
155
- const lines = child.content.split('\n');
156
- const contentBeforeChildren: string[] = [];
157
-
158
- for (const line of lines) {
159
- // Stop at child headers (scenarios start with ####)
160
- if (line.trim().startsWith('#')) {
161
- break;
162
- }
163
- contentBeforeChildren.push(line);
164
- }
165
-
166
- // Find first non-empty line
167
- const directContent = contentBeforeChildren.join('\n').trim();
168
- if (directContent) {
169
- const firstLine = directContent.split('\n').find(l => l.trim());
170
- if (firstLine) {
171
- text = firstLine.trim();
172
- }
173
- }
174
- }
175
-
176
- const scenarios = this.parseScenarios(child);
177
-
178
- requirements.push({
179
- text,
180
- scenarios,
181
- });
182
- }
183
-
184
- return requirements;
185
- }
186
-
187
- protected parseScenarios(requirementSection: Section): Scenario[] {
188
- const scenarios: Scenario[] = [];
189
-
190
- for (const scenarioSection of requirementSection.children) {
191
- // Store the raw text content of the scenario section
192
- if (scenarioSection.content.trim()) {
193
- scenarios.push({
194
- rawText: scenarioSection.content
195
- });
196
- }
197
- }
198
-
199
- return scenarios;
200
- }
201
-
202
-
203
- protected parseDeltas(content: string): Delta[] {
204
- const deltas: Delta[] = [];
205
- const lines = content.split('\n');
206
-
207
- for (const line of lines) {
208
- // Match both formats: **spec:** and **spec**:
209
- const deltaMatch = line.match(/^\s*-\s*\*\*([^*:]+)(?::\*\*|\*\*:)\s*(.+)$/);
210
- if (deltaMatch) {
211
- const specName = deltaMatch[1].trim();
212
- const description = deltaMatch[2].trim();
213
-
214
- let operation: DeltaOperation = 'MODIFIED';
215
- const lowerDesc = description.toLowerCase();
216
-
217
- // Use word boundaries to avoid false matches (e.g., "address" matching "add")
218
- // Check RENAMED first since it's more specific than patterns containing "new"
219
- if (/\brename(s|d|ing)?\b/.test(lowerDesc) || /\brenamed\s+(to|from)\b/.test(lowerDesc)) {
220
- operation = 'RENAMED';
221
- } else if (/\badd(s|ed|ing)?\b/.test(lowerDesc) || /\bcreate(s|d|ing)?\b/.test(lowerDesc) || /\bnew\b/.test(lowerDesc)) {
222
- operation = 'ADDED';
223
- } else if (/\bremove(s|d|ing)?\b/.test(lowerDesc) || /\bdelete(s|d|ing)?\b/.test(lowerDesc)) {
224
- operation = 'REMOVED';
225
- }
226
-
227
- deltas.push({
228
- spec: specName,
229
- operation,
230
- description,
231
- });
232
- }
233
- }
234
-
235
- return deltas;
236
- }
237
- }