@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,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';