@dedesfr/prompter 0.9.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.md +105 -77
  3. package/dist/cli/index.js +25 -1
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +32 -9
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/login.d.ts +4 -0
  9. package/dist/commands/login.d.ts.map +1 -0
  10. package/dist/commands/login.js +56 -0
  11. package/dist/commands/login.js.map +1 -0
  12. package/dist/commands/logout.d.ts +4 -0
  13. package/dist/commands/logout.d.ts.map +1 -0
  14. package/dist/commands/logout.js +14 -0
  15. package/dist/commands/logout.js.map +1 -0
  16. package/dist/commands/update.d.ts.map +1 -1
  17. package/dist/commands/update.js +18 -5
  18. package/dist/commands/update.js.map +1 -1
  19. package/dist/commands/whoami.d.ts +4 -0
  20. package/dist/commands/whoami.d.ts.map +1 -0
  21. package/dist/commands/whoami.js +42 -0
  22. package/dist/commands/whoami.js.map +1 -0
  23. package/dist/core/auth-store.d.ts +10 -0
  24. package/dist/core/auth-store.d.ts.map +1 -0
  25. package/dist/core/auth-store.js +39 -0
  26. package/dist/core/auth-store.js.map +1 -0
  27. package/dist/core/registry.d.ts +18 -0
  28. package/dist/core/registry.d.ts.map +1 -0
  29. package/dist/core/registry.js +94 -0
  30. package/dist/core/registry.js.map +1 -0
  31. package/package.json +7 -1
  32. package/AGENTS.md +0 -123
  33. package/CLAUDE.md +0 -17
  34. package/build.js +0 -20
  35. package/convex-setup.md +0 -403
  36. package/prompt/ai-humanizer.md +0 -45
  37. package/prompt/api-contract-generator.md +0 -234
  38. package/prompt/apply.md +0 -17
  39. package/prompt/archive.md +0 -21
  40. package/prompt/design-system.md +0 -210
  41. package/prompt/document-explainer.md +0 -149
  42. package/prompt/epic-generator.md +0 -198
  43. package/prompt/epic-single.md +0 -47
  44. package/prompt/erd-generator.md +0 -130
  45. package/prompt/fsd-generator.md +0 -157
  46. package/prompt/prd-agent-generator.md +0 -147
  47. package/prompt/prd-generator.md +0 -195
  48. package/prompt/product-brief.md +0 -289
  49. package/prompt/proposal.md +0 -22
  50. package/prompt/qa-test-scenario.md +0 -133
  51. package/prompt/skill-creator.md +0 -350
  52. package/prompt/story-generator.md +0 -278
  53. package/prompt/story-single.md +0 -70
  54. package/prompt/tdd-generator.md +0 -294
  55. package/prompt/tdd-lite-generator.md +0 -224
  56. package/prompt/wireframe-generator.md +0 -219
  57. package/skills/ai-context-generator/SKILL.md +0 -54
  58. package/skills/ai-context-generator/references/AGENTS.template.md +0 -83
  59. package/skills/ai-context-generator/references/CLAUDE.template.md +0 -39
  60. package/skills/ai-context-generator/references/behavioral-guidelines.md +0 -71
  61. package/skills/ai-context-generator/references/discovery-checklist.md +0 -40
  62. package/skills/ai-context-generator/references/examples/AGENTS.good.md +0 -103
  63. package/skills/ai-context-generator/references/extraction-checklist.md +0 -23
  64. package/skills/ai-context-generator/references/overlays/laravel.md +0 -44
  65. package/skills/ai-humanizer/SKILL.md +0 -50
  66. package/skills/api-contract-generator/SKILL.md +0 -243
  67. package/skills/apply/SKILL.md +0 -23
  68. package/skills/archive/SKILL.md +0 -27
  69. package/skills/cerebro/SKILL.md +0 -187
  70. package/skills/cerebro/references/agents.md +0 -213
  71. package/skills/code-review/SKILL.md +0 -373
  72. package/skills/code-review/assets/report-template-agent.md +0 -212
  73. package/skills/code-review/assets/report-template-compact.md +0 -81
  74. package/skills/code-review/assets/report-template-full.md +0 -264
  75. package/skills/code-review/assets/report-template-human.md +0 -168
  76. package/skills/code-review/references/universal-patterns.md +0 -495
  77. package/skills/design-md/README.md +0 -34
  78. package/skills/design-md/SKILL.md +0 -172
  79. package/skills/design-md/examples/DESIGN.md +0 -154
  80. package/skills/design-system/SKILL.md +0 -216
  81. package/skills/design-system-generator/SKILL.md +0 -324
  82. package/skills/design-system-generator/assets/design-system-template.md +0 -348
  83. package/skills/design-system-generator/references/extraction-patterns.md +0 -321
  84. package/skills/doc-builder/SKILL.md +0 -115
  85. package/skills/doc-builder/references/ui-patterns.md +0 -394
  86. package/skills/document-explainer/SKILL.md +0 -155
  87. package/skills/document-translator/SKILL.md +0 -58
  88. package/skills/enhance/SKILL.md +0 -47
  89. package/skills/enhance-prompt/README.md +0 -34
  90. package/skills/enhance-prompt/SKILL.md +0 -204
  91. package/skills/enhance-prompt/references/KEYWORDS.md +0 -114
  92. package/skills/epic-generator/SKILL.md +0 -204
  93. package/skills/epic-single/SKILL.md +0 -63
  94. package/skills/erd-generator/SKILL.md +0 -138
  95. package/skills/feature-planner/SKILL.md +0 -305
  96. package/skills/feature-planner/assets/implementation-plan-template.md +0 -85
  97. package/skills/frontend-design/LICENSE.txt +0 -177
  98. package/skills/frontend-design/SKILL.md +0 -42
  99. package/skills/fsd-generator/SKILL.md +0 -163
  100. package/skills/gamma-builder/SKILL.md +0 -134
  101. package/skills/laravel-code-review/SKILL.md +0 -383
  102. package/skills/laravel-code-review/assets/report-template-agent.md +0 -195
  103. package/skills/laravel-code-review/assets/report-template-compact.md +0 -79
  104. package/skills/laravel-code-review/assets/report-template-full.md +0 -253
  105. package/skills/laravel-code-review/assets/report-template-human.md +0 -159
  106. package/skills/laravel-code-review/references/laravel-patterns.md +0 -571
  107. package/skills/laravel-code-review/references/php84-features.md +0 -442
  108. package/skills/mcp-builder/LICENSE.txt +0 -202
  109. package/skills/mcp-builder/SKILL.md +0 -236
  110. package/skills/mcp-builder/reference/evaluation.md +0 -602
  111. package/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  112. package/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  113. package/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  114. package/skills/mcp-builder/scripts/connections.py +0 -151
  115. package/skills/mcp-builder/scripts/evaluation.py +0 -373
  116. package/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  117. package/skills/mcp-builder/scripts/requirements.txt +0 -2
  118. package/skills/meeting-notes/SKILL.md +0 -159
  119. package/skills/meeting-notes/evals/evals.json +0 -23
  120. package/skills/prd-agent-generator/SKILL.md +0 -132
  121. package/skills/prd-generator/SKILL.md +0 -211
  122. package/skills/product-brief/SKILL.md +0 -141
  123. package/skills/project-orchestrator/SKILL.md +0 -487
  124. package/skills/project-orchestrator/assets/caddy-vps-setup.md +0 -180
  125. package/skills/project-orchestrator/assets/plan-summary-template.md +0 -159
  126. package/skills/prompter-specs/SKILL.md +0 -115
  127. package/skills/prompter-workflow/SKILL.md +0 -166
  128. package/skills/prompter-workflow/evals/evals.json +0 -89
  129. package/skills/proposal/SKILL.md +0 -28
  130. package/skills/qa-test-scenario/SKILL.md +0 -149
  131. package/skills/skill-creator/SKILL.md +0 -173
  132. package/skills/sph-generator/SKILL.md +0 -488
  133. package/skills/story-generator/SKILL.md +0 -285
  134. package/skills/story-single/SKILL.md +0 -86
  135. package/skills/tdd-generator/SKILL.md +0 -300
  136. package/skills/tdd-lite-generator/SKILL.md +0 -230
  137. package/skills/ui-ux-pro/SKILL.md +0 -199
  138. package/skills/ui-ux-pro/assets/design-spec-template.md +0 -173
  139. package/skills/ui-ux-pro/references/component-patterns.md +0 -255
  140. package/skills/ui-ux-pro/references/design-principles.md +0 -167
  141. package/skills/wireframe-generator/SKILL.md +0 -227
  142. package/src/cli/index.ts +0 -223
  143. package/src/commands/archive.ts +0 -302
  144. package/src/commands/change.ts +0 -292
  145. package/src/commands/config.ts +0 -233
  146. package/src/commands/guide.ts +0 -50
  147. package/src/commands/init.ts +0 -597
  148. package/src/commands/list.ts +0 -194
  149. package/src/commands/show.ts +0 -138
  150. package/src/commands/spec.ts +0 -251
  151. package/src/commands/update.ts +0 -129
  152. package/src/commands/upgrade.ts +0 -30
  153. package/src/commands/validate.ts +0 -326
  154. package/src/core/artifact-graph/graph.ts +0 -167
  155. package/src/core/artifact-graph/index.ts +0 -44
  156. package/src/core/artifact-graph/instruction-loader.ts +0 -302
  157. package/src/core/artifact-graph/resolver.ts +0 -226
  158. package/src/core/artifact-graph/schema.ts +0 -124
  159. package/src/core/artifact-graph/state.ts +0 -64
  160. package/src/core/artifact-graph/types.ts +0 -65
  161. package/src/core/completions/command-registry.ts +0 -382
  162. package/src/core/completions/completion-provider.ts +0 -128
  163. package/src/core/completions/generators/bash-generator.ts +0 -191
  164. package/src/core/completions/generators/fish-generator.ts +0 -188
  165. package/src/core/completions/generators/powershell-generator.ts +0 -223
  166. package/src/core/completions/generators/zsh-generator.ts +0 -281
  167. package/src/core/completions/templates/bash-templates.ts +0 -24
  168. package/src/core/completions/templates/fish-templates.ts +0 -40
  169. package/src/core/completions/templates/powershell-templates.ts +0 -25
  170. package/src/core/completions/templates/zsh-templates.ts +0 -36
  171. package/src/core/completions/types.ts +0 -90
  172. package/src/core/config-schema.ts +0 -230
  173. package/src/core/config.ts +0 -181
  174. package/src/core/configurators/slash/antigravity.ts +0 -10
  175. package/src/core/configurators/slash/base.ts +0 -109
  176. package/src/core/configurators/slash/claude.ts +0 -10
  177. package/src/core/configurators/slash/codex.ts +0 -10
  178. package/src/core/configurators/slash/droid.ts +0 -10
  179. package/src/core/configurators/slash/forge.ts +0 -10
  180. package/src/core/configurators/slash/github-copilot.ts +0 -10
  181. package/src/core/configurators/slash/index.ts +0 -10
  182. package/src/core/configurators/slash/kilocode.ts +0 -10
  183. package/src/core/configurators/slash/opencode.ts +0 -10
  184. package/src/core/configurators/slash/registry.ts +0 -51
  185. package/src/core/converters/json-converter.ts +0 -62
  186. package/src/core/global-config.ts +0 -136
  187. package/src/core/parsers/change-parser.ts +0 -234
  188. package/src/core/parsers/markdown-parser.ts +0 -237
  189. package/src/core/parsers/requirement-blocks.ts +0 -234
  190. package/src/core/prompt-templates.ts +0 -3504
  191. package/src/core/schemas/base.schema.ts +0 -20
  192. package/src/core/schemas/change.schema.ts +0 -42
  193. package/src/core/schemas/index.ts +0 -20
  194. package/src/core/schemas/spec.schema.ts +0 -17
  195. package/src/core/skill-discovery.ts +0 -68
  196. package/src/core/specs-apply.ts +0 -483
  197. package/src/core/styles/palette.ts +0 -8
  198. package/src/core/templates/agents-template.ts +0 -459
  199. package/src/core/templates/claude-template.ts +0 -2
  200. package/src/core/templates/index.ts +0 -3
  201. package/src/core/templates/project-template.ts +0 -32
  202. package/src/core/validation/constants.ts +0 -48
  203. package/src/core/validation/types.ts +0 -19
  204. package/src/core/validation/validator.ts +0 -449
  205. package/src/core/view.ts +0 -219
  206. package/src/index.ts +0 -1
  207. package/src/utils/change-metadata.ts +0 -171
  208. package/src/utils/change-utils.ts +0 -131
  209. package/src/utils/file-system.ts +0 -252
  210. package/src/utils/index.ts +0 -12
  211. package/src/utils/interactive.ts +0 -29
  212. package/src/utils/item-discovery.ts +0 -66
  213. package/src/utils/match.ts +0 -26
  214. package/src/utils/shell-detection.ts +0 -62
  215. package/src/utils/task-progress.ts +0 -43
  216. package/tsconfig.json +0 -28
@@ -1,129 +0,0 @@
1
- import chalk from 'chalk';
2
- import { promises as fs } from 'fs';
3
- import path from 'path';
4
- import { fileURLToPath } from 'url';
5
- import { PrompterConfig, AVAILABLE_PROMPTS, PROMPTER_DIR } from '../core/config.js';
6
- import { registry } from '../core/configurators/slash/index.js';
7
- import { PROMPT_TEMPLATES } from '../core/prompt-templates.js';
8
- import { discoverSkills } from '../core/skill-discovery.js';
9
-
10
- export class UpdateCommand {
11
- async execute(): Promise<void> {
12
- const projectPath = process.cwd();
13
-
14
- console.log(chalk.blue('\n🔄 Updating Prompter workflow files...\n'));
15
-
16
- // Check if initialized
17
- if (!await PrompterConfig.prompterDirExists(projectPath)) {
18
- console.log(chalk.red('❌ Prompter is not initialized in this project.'));
19
- console.log(chalk.gray(' Run `prompter init` first.\n'));
20
- process.exitCode = 1;
21
- return;
22
- }
23
-
24
- // Detect configured tools
25
- const configuredTools = await this.detectConfiguredTools(projectPath);
26
-
27
- if (configuredTools.length === 0) {
28
- console.log(chalk.yellow('⚠️ No configured tools found.'));
29
- console.log(chalk.gray(' Run `prompter init` to configure tools.\n'));
30
- process.exitCode = 1;
31
- return;
32
- }
33
-
34
- let updatedCount = 0;
35
-
36
- // Clean up legacy slash command files and update skills for each tool
37
- const packageDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..');
38
- const availableSkills = await discoverSkills(path.join(packageDir, 'skills'));
39
-
40
- for (const toolId of configuredTools) {
41
- const configurator = registry.get(toolId);
42
- if (!configurator) continue;
43
-
44
- try {
45
- if (availableSkills.length > 0) {
46
- const updatedSkillFiles = await configurator.updateExistingSkills(projectPath, availableSkills);
47
- for (const file of updatedSkillFiles) {
48
- console.log(chalk.green('✓') + ` Updated ${chalk.cyan(file)}`);
49
- updatedCount++;
50
- }
51
- }
52
- } catch (error) {
53
- console.log(chalk.red('✗') + ` Failed to update ${configurator.toolId}: ${error}`);
54
- }
55
- }
56
-
57
- // Update existing prompts in prompter/core/
58
- const prompterPath = path.join(projectPath, PROMPTER_DIR);
59
- const updatedCorePrompts = await this.updateCorePrompts(prompterPath);
60
- for (const promptName of updatedCorePrompts) {
61
- console.log(chalk.green('✓') + ` Updated ${chalk.cyan(`${PROMPTER_DIR}/core/${promptName}`)}`);
62
- updatedCount++;
63
- }
64
-
65
- if (updatedCount === 0) {
66
- console.log(chalk.yellow('⚠️ No workflow files found to update.'));
67
- console.log(chalk.gray(' Run `prompter init` to create them.\n'));
68
- } else {
69
- console.log(chalk.green(`\n✅ ${updatedCount} file(s) updated.\n`));
70
- }
71
- }
72
-
73
- private async fileExists(filePath: string): Promise<boolean> {
74
- try {
75
- await fs.access(filePath);
76
- return true;
77
- } catch {
78
- return false;
79
- }
80
- }
81
-
82
- private async detectConfiguredTools(projectPath: string): Promise<string[]> {
83
- const configuredTools: string[] = [];
84
- const allConfigurators = registry.getAll();
85
-
86
- for (const configurator of allConfigurators) {
87
- const skillsRoot = path.join(projectPath, configurator.getSkillsRootDir());
88
- try {
89
- const stat = await fs.stat(skillsRoot);
90
- if (stat.isDirectory()) configuredTools.push(configurator.toolId);
91
- } catch {
92
- // directory doesn't exist — tool not configured
93
- }
94
- }
95
-
96
- return configuredTools;
97
- }
98
-
99
- private async updateCorePrompts(prompterPath: string): Promise<string[]> {
100
- const updatedPrompts: string[] = [];
101
- const corePath = path.join(prompterPath, 'core');
102
-
103
- if (!await this.fileExists(corePath)) {
104
- return updatedPrompts;
105
- }
106
-
107
- for (const prompt of AVAILABLE_PROMPTS) {
108
- const promptFilePath = path.join(corePath, prompt.sourceFile);
109
-
110
- if (await this.fileExists(promptFilePath)) {
111
- try {
112
- const content = PROMPT_TEMPLATES[prompt.value];
113
-
114
- if (!content) {
115
- console.log(chalk.yellow(` Warning: Template not found for ${prompt.name}`));
116
- continue;
117
- }
118
-
119
- await fs.writeFile(promptFilePath, content, 'utf-8');
120
- updatedPrompts.push(prompt.sourceFile);
121
- } catch (error) {
122
- console.log(chalk.red(` Error updating ${prompt.name}: ${error}`));
123
- }
124
- }
125
- }
126
-
127
- return updatedPrompts;
128
- }
129
- }
@@ -1,30 +0,0 @@
1
- import { spawn } from 'child_process';
2
- import chalk from 'chalk';
3
-
4
- export class UpgradeCommand {
5
- async execute(): Promise<void> {
6
- console.log(chalk.cyan('\n🔄 Upgrading Prompter to the latest version...\n'));
7
-
8
- const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
9
-
10
- const upgrade = spawn(npmCommand, ['install', '-g', '@dedesfr/prompter@latest'], {
11
- stdio: 'inherit',
12
- shell: true
13
- });
14
-
15
- upgrade.on('close', (code) => {
16
- if (code === 0) {
17
- console.log(chalk.green('\n✓ Prompter has been upgraded successfully!\n'));
18
- } else {
19
- console.error(chalk.red(`\n✗ Upgrade failed with exit code ${code}\n`));
20
- process.exit(code || 1);
21
- }
22
- });
23
-
24
- upgrade.on('error', (error) => {
25
- console.error(chalk.red('\n✗ Failed to run upgrade command:'), error.message);
26
- console.log(chalk.yellow('\nPlease run manually: npm install -g @dedesfr/prompter@latest\n'));
27
- process.exit(1);
28
- });
29
- }
30
- }
@@ -1,326 +0,0 @@
1
- import ora from 'ora';
2
- import path from 'path';
3
- import { Validator } from '../core/validation/validator.js';
4
- import { isInteractive, resolveNoInteractive } from '../utils/interactive.js';
5
- import { getActiveChangeIds, getSpecIds } from '../utils/item-discovery.js';
6
- import { nearestMatches } from '../utils/match.js';
7
-
8
- type ItemType = 'change' | 'spec';
9
-
10
- interface ExecuteOptions {
11
- all?: boolean;
12
- changes?: boolean;
13
- specs?: boolean;
14
- type?: string;
15
- strict?: boolean;
16
- json?: boolean;
17
- noInteractive?: boolean;
18
- interactive?: boolean; // Commander sets this to false when --no-interactive is used
19
- concurrency?: string;
20
- }
21
-
22
- interface BulkItemResult {
23
- id: string;
24
- type: ItemType;
25
- valid: boolean;
26
- issues: { level: 'ERROR' | 'WARNING' | 'INFO'; path: string; message: string }[];
27
- durationMs: number;
28
- }
29
-
30
- export class ValidateCommand {
31
- async execute(itemName: string | undefined, options: ExecuteOptions = {}): Promise<void> {
32
- const interactive = isInteractive(options);
33
-
34
- // Handle bulk flags first
35
- if (options.all || options.changes || options.specs) {
36
- await this.runBulkValidation({
37
- changes: !!options.all || !!options.changes,
38
- specs: !!options.all || !!options.specs,
39
- }, { strict: !!options.strict, json: !!options.json, concurrency: options.concurrency, noInteractive: resolveNoInteractive(options) });
40
- return;
41
- }
42
-
43
- // No item and no flags
44
- if (!itemName) {
45
- if (interactive) {
46
- await this.runInteractiveSelector({ strict: !!options.strict, json: !!options.json, concurrency: options.concurrency });
47
- return;
48
- }
49
- this.printNonInteractiveHint();
50
- process.exitCode = 1;
51
- return;
52
- }
53
-
54
- // Direct item validation with type detection or override
55
- const typeOverride = this.normalizeType(options.type);
56
- await this.validateDirectItem(itemName, { typeOverride, strict: !!options.strict, json: !!options.json });
57
- }
58
-
59
- private normalizeType(value?: string): ItemType | undefined {
60
- if (!value) return undefined;
61
- const v = value.toLowerCase();
62
- if (v === 'change' || v === 'spec') return v;
63
- return undefined;
64
- }
65
-
66
- private async runInteractiveSelector(opts: { strict: boolean; json: boolean; concurrency?: string }): Promise<void> {
67
- const { select } = await import('@inquirer/prompts');
68
- const choice = await select({
69
- message: 'What would you like to validate?',
70
- choices: [
71
- { name: 'All (changes + specs)', value: 'all' },
72
- { name: 'All changes', value: 'changes' },
73
- { name: 'All specs', value: 'specs' },
74
- { name: 'Pick a specific change or spec', value: 'one' },
75
- ],
76
- });
77
-
78
- if (choice === 'all') return this.runBulkValidation({ changes: true, specs: true }, opts);
79
- if (choice === 'changes') return this.runBulkValidation({ changes: true, specs: false }, opts);
80
- if (choice === 'specs') return this.runBulkValidation({ changes: false, specs: true }, opts);
81
-
82
- // one
83
- const [changes, specs] = await Promise.all([getActiveChangeIds(), getSpecIds()]);
84
- const items: { name: string; value: { type: ItemType; id: string } }[] = [];
85
- items.push(...changes.map(id => ({ name: `change/${id}`, value: { type: 'change' as const, id } })));
86
- items.push(...specs.map(id => ({ name: `spec/${id}`, value: { type: 'spec' as const, id } })));
87
- if (items.length === 0) {
88
- console.error('No items found to validate.');
89
- process.exitCode = 1;
90
- return;
91
- }
92
- const picked = await select<{ type: ItemType; id: string }>({ message: 'Pick an item', choices: items });
93
- await this.validateByType(picked.type, picked.id, opts);
94
- }
95
-
96
- private printNonInteractiveHint(): void {
97
- console.error('Nothing to validate. Try one of:');
98
- console.error(' prompter validate --all');
99
- console.error(' prompter validate --changes');
100
- console.error(' prompter validate --specs');
101
- console.error(' prompter validate <item-name>');
102
- console.error('Or run in an interactive terminal.');
103
- }
104
-
105
- private async validateDirectItem(itemName: string, opts: { typeOverride?: ItemType; strict: boolean; json: boolean }): Promise<void> {
106
- const [changes, specs] = await Promise.all([getActiveChangeIds(), getSpecIds()]);
107
- const isChange = changes.includes(itemName);
108
- const isSpec = specs.includes(itemName);
109
-
110
- const type = opts.typeOverride ?? (isChange ? 'change' : isSpec ? 'spec' : undefined);
111
-
112
- if (!type) {
113
- console.error(`Unknown item '${itemName}'`);
114
- const suggestions = nearestMatches(itemName, [...changes, ...specs]);
115
- if (suggestions.length) console.error(`Did you mean: ${suggestions.join(', ')}?`);
116
- process.exitCode = 1;
117
- return;
118
- }
119
-
120
- if (!opts.typeOverride && isChange && isSpec) {
121
- console.error(`Ambiguous item '${itemName}' matches both a change and a spec.`);
122
- console.error('Pass --type change|spec, or use: prompter change validate / prompter spec validate');
123
- process.exitCode = 1;
124
- return;
125
- }
126
-
127
- await this.validateByType(type, itemName, opts);
128
- }
129
-
130
- private async validateByType(type: ItemType, id: string, opts: { strict: boolean; json: boolean }): Promise<void> {
131
- const validator = new Validator(opts.strict);
132
- if (type === 'change') {
133
- const changeDir = path.join(process.cwd(), 'prompter', 'changes', id);
134
- const start = Date.now();
135
- const report = await validator.validateChangeDeltaSpecs(changeDir);
136
- const durationMs = Date.now() - start;
137
- this.printReport('change', id, report, durationMs, opts.json);
138
- // Non-zero exit if invalid (keeps enriched output test semantics)
139
- process.exitCode = report.valid ? 0 : 1;
140
- return;
141
- }
142
- const file = path.join(process.cwd(), 'prompter', 'specs', id, 'spec.md');
143
- const start = Date.now();
144
- const report = await validator.validateSpec(file);
145
- const durationMs = Date.now() - start;
146
- this.printReport('spec', id, report, durationMs, opts.json);
147
- process.exitCode = report.valid ? 0 : 1;
148
- }
149
-
150
- private printReport(type: ItemType, id: string, report: { valid: boolean; issues: any[] }, durationMs: number, json: boolean): void {
151
- if (json) {
152
- const out = { items: [{ id, type, valid: report.valid, issues: report.issues, durationMs }], summary: { totals: { items: 1, passed: report.valid ? 1 : 0, failed: report.valid ? 0 : 1 }, byType: { [type]: { items: 1, passed: report.valid ? 1 : 0, failed: report.valid ? 0 : 1 } } }, version: '1.0' };
153
- console.log(JSON.stringify(out, null, 2));
154
- return;
155
- }
156
- if (report.valid) {
157
- console.log(`${type === 'change' ? 'Change' : 'Specification'} '${id}' is valid`);
158
- } else {
159
- console.error(`${type === 'change' ? 'Change' : 'Specification'} '${id}' has issues`);
160
- for (const issue of report.issues) {
161
- const label = issue.level === 'ERROR' ? 'ERROR' : issue.level;
162
- const prefix = issue.level === 'ERROR' ? '✗' : issue.level === 'WARNING' ? '⚠' : 'ℹ';
163
- console.error(`${prefix} [${label}] ${issue.path}: ${issue.message}`);
164
- }
165
- this.printNextSteps(type);
166
- }
167
- }
168
-
169
- private printNextSteps(type: ItemType): void {
170
- const bullets: string[] = [];
171
- if (type === 'change') {
172
- bullets.push('- Ensure change has deltas in specs/: use headers ## ADDED/MODIFIED/REMOVED/RENAMED Requirements');
173
- bullets.push('- Each requirement MUST include at least one #### Scenario: block');
174
- bullets.push('- Debug parsed deltas: prompter change show <id> --json --deltas-only');
175
- } else {
176
- bullets.push('- Ensure spec includes ## Purpose and ## Requirements sections');
177
- bullets.push('- Each requirement MUST include at least one #### Scenario: block');
178
- bullets.push('- Re-run with --json to see structured report');
179
- }
180
- console.error('Next steps:');
181
- bullets.forEach(b => console.error(` ${b}`));
182
- }
183
-
184
- private async runBulkValidation(scope: { changes: boolean; specs: boolean }, opts: { strict: boolean; json: boolean; concurrency?: string; noInteractive?: boolean }): Promise<void> {
185
- const spinner = !opts.json && !opts.noInteractive ? ora('Validating...').start() : undefined;
186
- const [changeIds, specIds] = await Promise.all([
187
- scope.changes ? getActiveChangeIds() : Promise.resolve<string[]>([]),
188
- scope.specs ? getSpecIds() : Promise.resolve<string[]>([]),
189
- ]);
190
-
191
- const DEFAULT_CONCURRENCY = 6;
192
- const maxSuggestions = 5; // used by nearestMatches
193
- const concurrency = normalizeConcurrency(opts.concurrency) ?? normalizeConcurrency(process.env.PROMPTER_CONCURRENCY) ?? DEFAULT_CONCURRENCY;
194
- const validator = new Validator(opts.strict);
195
- const queue: Array<() => Promise<BulkItemResult>> = [];
196
-
197
- for (const id of changeIds) {
198
- queue.push(async () => {
199
- const start = Date.now();
200
- const changeDir = path.join(process.cwd(), 'prompter', 'changes', id);
201
- const report = await validator.validateChangeDeltaSpecs(changeDir);
202
- const durationMs = Date.now() - start;
203
- return { id, type: 'change' as const, valid: report.valid, issues: report.issues, durationMs };
204
- });
205
- }
206
- for (const id of specIds) {
207
- queue.push(async () => {
208
- const start = Date.now();
209
- const file = path.join(process.cwd(), 'prompter', 'specs', id, 'spec.md');
210
- const report = await validator.validateSpec(file);
211
- const durationMs = Date.now() - start;
212
- return { id, type: 'spec' as const, valid: report.valid, issues: report.issues, durationMs };
213
- });
214
- }
215
-
216
- if (queue.length === 0) {
217
- spinner?.stop();
218
-
219
- const summary = {
220
- totals: { items: 0, passed: 0, failed: 0 },
221
- byType: {
222
- ...(scope.changes ? { change: { items: 0, passed: 0, failed: 0 } } : {}),
223
- ...(scope.specs ? { spec: { items: 0, passed: 0, failed: 0 } } : {}),
224
- },
225
- } as const;
226
-
227
- if (opts.json) {
228
- const out = { items: [] as BulkItemResult[], summary, version: '1.0' };
229
- console.log(JSON.stringify(out, null, 2));
230
- } else {
231
- console.log('No items found to validate.');
232
- }
233
-
234
- process.exitCode = 0;
235
- return;
236
- }
237
-
238
- const results: BulkItemResult[] = [];
239
- let index = 0;
240
- let running = 0;
241
- let passed = 0;
242
- let failed = 0;
243
-
244
- await new Promise<void>((resolve) => {
245
- const next = () => {
246
- while (running < concurrency && index < queue.length) {
247
- const currentIndex = index++;
248
- const task = queue[currentIndex];
249
- running++;
250
- if (spinner) spinner.text = `Validating (${currentIndex + 1}/${queue.length})...`;
251
- task()
252
- .then(res => {
253
- results.push(res);
254
- if (res.valid) passed++; else failed++;
255
- })
256
- .catch((error: any) => {
257
- const message = error?.message || 'Unknown error';
258
- const res: BulkItemResult = { id: getPlannedId(currentIndex, changeIds, specIds) ?? 'unknown', type: getPlannedType(currentIndex, changeIds, specIds) ?? 'change', valid: false, issues: [{ level: 'ERROR', path: 'file', message }], durationMs: 0 };
259
- results.push(res);
260
- failed++;
261
- })
262
- .finally(() => {
263
- running--;
264
- if (index >= queue.length && running === 0) resolve();
265
- else next();
266
- });
267
- }
268
- };
269
- next();
270
- });
271
-
272
- spinner?.stop();
273
-
274
- results.sort((a, b) => a.id.localeCompare(b.id));
275
- const summary = {
276
- totals: { items: results.length, passed, failed },
277
- byType: {
278
- ...(scope.changes ? { change: summarizeType(results, 'change') } : {}),
279
- ...(scope.specs ? { spec: summarizeType(results, 'spec') } : {}),
280
- },
281
- } as const;
282
-
283
- if (opts.json) {
284
- const out = { items: results, summary, version: '1.0' };
285
- console.log(JSON.stringify(out, null, 2));
286
- } else {
287
- for (const res of results) {
288
- if (res.valid) console.log(`✓ ${res.type}/${res.id}`);
289
- else console.error(`✗ ${res.type}/${res.id}`);
290
- }
291
- console.log(`Totals: ${summary.totals.passed} passed, ${summary.totals.failed} failed (${summary.totals.items} items)`);
292
- }
293
-
294
- process.exitCode = failed > 0 ? 1 : 0;
295
- }
296
- }
297
-
298
- function summarizeType(results: BulkItemResult[], type: ItemType) {
299
- const filtered = results.filter(r => r.type === type);
300
- const items = filtered.length;
301
- const passed = filtered.filter(r => r.valid).length;
302
- const failed = items - passed;
303
- return { items, passed, failed };
304
- }
305
-
306
- function normalizeConcurrency(value?: string): number | undefined {
307
- if (!value) return undefined;
308
- const n = parseInt(value, 10);
309
- if (Number.isNaN(n) || n <= 0) return undefined;
310
- return n;
311
- }
312
-
313
- function getPlannedId(index: number, changeIds: string[], specIds: string[]): string | undefined {
314
- const totalChanges = changeIds.length;
315
- if (index < totalChanges) return changeIds[index];
316
- const specIndex = index - totalChanges;
317
- return specIds[specIndex];
318
- }
319
-
320
- function getPlannedType(index: number, changeIds: string[], specIds: string[]): ItemType | undefined {
321
- const totalChanges = changeIds.length;
322
- if (index < totalChanges) return 'change';
323
- const specIndex = index - totalChanges;
324
- if (specIndex >= 0 && specIndex < specIds.length) return 'spec';
325
- return undefined;
326
- }
@@ -1,167 +0,0 @@
1
- import type { Artifact, SchemaYaml, CompletedSet, BlockedArtifacts } from './types.js';
2
- import { loadSchema, parseSchema } from './schema.js';
3
-
4
- /**
5
- * Represents an artifact dependency graph.
6
- * Provides methods for querying build order, ready artifacts, and completion status.
7
- */
8
- export class ArtifactGraph {
9
- private artifacts: Map<string, Artifact>;
10
- private schema: SchemaYaml;
11
-
12
- private constructor(schema: SchemaYaml) {
13
- this.schema = schema;
14
- this.artifacts = new Map(schema.artifacts.map(a => [a.id, a]));
15
- }
16
-
17
- /**
18
- * Creates an ArtifactGraph from a YAML file path.
19
- */
20
- static fromYaml(filePath: string): ArtifactGraph {
21
- const schema = loadSchema(filePath);
22
- return new ArtifactGraph(schema);
23
- }
24
-
25
- /**
26
- * Creates an ArtifactGraph from YAML content string.
27
- */
28
- static fromYamlContent(yamlContent: string): ArtifactGraph {
29
- const schema = parseSchema(yamlContent);
30
- return new ArtifactGraph(schema);
31
- }
32
-
33
- /**
34
- * Creates an ArtifactGraph from a pre-validated schema object.
35
- */
36
- static fromSchema(schema: SchemaYaml): ArtifactGraph {
37
- return new ArtifactGraph(schema);
38
- }
39
-
40
- /**
41
- * Gets a single artifact by ID.
42
- */
43
- getArtifact(id: string): Artifact | undefined {
44
- return this.artifacts.get(id);
45
- }
46
-
47
- /**
48
- * Gets all artifacts in the graph.
49
- */
50
- getAllArtifacts(): Artifact[] {
51
- return Array.from(this.artifacts.values());
52
- }
53
-
54
- /**
55
- * Gets the schema name.
56
- */
57
- getName(): string {
58
- return this.schema.name;
59
- }
60
-
61
- /**
62
- * Gets the schema version.
63
- */
64
- getVersion(): number {
65
- return this.schema.version;
66
- }
67
-
68
- /**
69
- * Computes the topological build order using Kahn's algorithm.
70
- * Returns artifact IDs in the order they should be built.
71
- */
72
- getBuildOrder(): string[] {
73
- const inDegree = new Map<string, number>();
74
- const dependents = new Map<string, string[]>();
75
-
76
- // Initialize all artifacts
77
- for (const artifact of this.artifacts.values()) {
78
- inDegree.set(artifact.id, artifact.requires.length);
79
- dependents.set(artifact.id, []);
80
- }
81
-
82
- // Build reverse adjacency (who depends on whom)
83
- for (const artifact of this.artifacts.values()) {
84
- for (const req of artifact.requires) {
85
- dependents.get(req)!.push(artifact.id);
86
- }
87
- }
88
-
89
- // Start with roots (in-degree 0), sorted for determinism
90
- const queue = [...this.artifacts.keys()]
91
- .filter(id => inDegree.get(id) === 0)
92
- .sort();
93
-
94
- const result: string[] = [];
95
-
96
- while (queue.length > 0) {
97
- const current = queue.shift()!;
98
- result.push(current);
99
-
100
- // Collect newly ready artifacts, then sort before adding
101
- const newlyReady: string[] = [];
102
- for (const dep of dependents.get(current)!) {
103
- const newDegree = inDegree.get(dep)! - 1;
104
- inDegree.set(dep, newDegree);
105
- if (newDegree === 0) {
106
- newlyReady.push(dep);
107
- }
108
- }
109
- queue.push(...newlyReady.sort());
110
- }
111
-
112
- return result;
113
- }
114
-
115
- /**
116
- * Gets artifacts that are ready to be created (all dependencies completed).
117
- */
118
- getNextArtifacts(completed: CompletedSet): string[] {
119
- const ready: string[] = [];
120
-
121
- for (const artifact of this.artifacts.values()) {
122
- if (completed.has(artifact.id)) {
123
- continue; // Already completed
124
- }
125
-
126
- const allDepsCompleted = artifact.requires.every(req => completed.has(req));
127
- if (allDepsCompleted) {
128
- ready.push(artifact.id);
129
- }
130
- }
131
-
132
- // Sort for deterministic ordering
133
- return ready.sort();
134
- }
135
-
136
- /**
137
- * Checks if all artifacts in the graph are completed.
138
- */
139
- isComplete(completed: CompletedSet): boolean {
140
- for (const artifact of this.artifacts.values()) {
141
- if (!completed.has(artifact.id)) {
142
- return false;
143
- }
144
- }
145
- return true;
146
- }
147
-
148
- /**
149
- * Gets blocked artifacts and their unmet dependencies.
150
- */
151
- getBlocked(completed: CompletedSet): BlockedArtifacts {
152
- const blocked: BlockedArtifacts = {};
153
-
154
- for (const artifact of this.artifacts.values()) {
155
- if (completed.has(artifact.id)) {
156
- continue; // Already completed
157
- }
158
-
159
- const unmetDeps = artifact.requires.filter(req => !completed.has(req));
160
- if (unmetDeps.length > 0) {
161
- blocked[artifact.id] = unmetDeps.sort();
162
- }
163
- }
164
-
165
- return blocked;
166
- }
167
- }
@@ -1,44 +0,0 @@
1
- // Types
2
- export {
3
- ArtifactSchema,
4
- SchemaYamlSchema,
5
- type Artifact,
6
- type SchemaYaml,
7
- type CompletedSet,
8
- type BlockedArtifacts,
9
- } from './types.js';
10
-
11
- // Schema loading and validation
12
- export { loadSchema, parseSchema, SchemaValidationError } from './schema.js';
13
-
14
- // Graph operations
15
- export { ArtifactGraph } from './graph.js';
16
-
17
- // State detection
18
- export { detectCompleted } from './state.js';
19
-
20
- // Schema resolution
21
- export {
22
- resolveSchema,
23
- listSchemas,
24
- listSchemasWithInfo,
25
- getSchemaDir,
26
- getPackageSchemasDir,
27
- getUserSchemasDir,
28
- SchemaLoadError,
29
- type SchemaInfo,
30
- } from './resolver.js';
31
-
32
- // Instruction loading
33
- export {
34
- loadTemplate,
35
- loadChangeContext,
36
- generateInstructions,
37
- formatChangeStatus,
38
- TemplateLoadError,
39
- type ChangeContext,
40
- type ArtifactInstructions,
41
- type DependencyInfo,
42
- type ArtifactStatus,
43
- type ChangeStatus,
44
- } from './instruction-loader.js';