@dedesfr/prompter 0.8.23 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +105 -77
  3. package/dist/cli/index.js +25 -1
  4. package/dist/cli/index.js.map +1 -1
  5. package/dist/commands/init.d.ts +1 -7
  6. package/dist/commands/init.d.ts.map +1 -1
  7. package/dist/commands/init.js +60 -299
  8. package/dist/commands/init.js.map +1 -1
  9. package/dist/commands/login.d.ts +4 -0
  10. package/dist/commands/login.d.ts.map +1 -0
  11. package/dist/commands/login.js +56 -0
  12. package/dist/commands/login.js.map +1 -0
  13. package/dist/commands/logout.d.ts +4 -0
  14. package/dist/commands/logout.d.ts.map +1 -0
  15. package/dist/commands/logout.js +14 -0
  16. package/dist/commands/logout.js.map +1 -0
  17. package/dist/commands/update.d.ts.map +1 -1
  18. package/dist/commands/update.js +31 -41
  19. package/dist/commands/update.js.map +1 -1
  20. package/dist/commands/whoami.d.ts +4 -0
  21. package/dist/commands/whoami.d.ts.map +1 -0
  22. package/dist/commands/whoami.js +42 -0
  23. package/dist/commands/whoami.js.map +1 -0
  24. package/dist/core/auth-store.d.ts +10 -0
  25. package/dist/core/auth-store.d.ts.map +1 -0
  26. package/dist/core/auth-store.js +39 -0
  27. package/dist/core/auth-store.js.map +1 -0
  28. package/dist/core/configurators/slash/antigravity.d.ts +2 -5
  29. package/dist/core/configurators/slash/antigravity.d.ts.map +1 -1
  30. package/dist/core/configurators/slash/antigravity.js +2 -57
  31. package/dist/core/configurators/slash/antigravity.js.map +1 -1
  32. package/dist/core/configurators/slash/base.d.ts +6 -18
  33. package/dist/core/configurators/slash/base.d.ts.map +1 -1
  34. package/dist/core/configurators/slash/base.js +8 -77
  35. package/dist/core/configurators/slash/base.js.map +1 -1
  36. package/dist/core/configurators/slash/claude.d.ts +2 -5
  37. package/dist/core/configurators/slash/claude.d.ts.map +1 -1
  38. package/dist/core/configurators/slash/claude.js +2 -57
  39. package/dist/core/configurators/slash/claude.js.map +1 -1
  40. package/dist/core/configurators/slash/codex.d.ts +2 -5
  41. package/dist/core/configurators/slash/codex.d.ts.map +1 -1
  42. package/dist/core/configurators/slash/codex.js +2 -57
  43. package/dist/core/configurators/slash/codex.js.map +1 -1
  44. package/dist/core/configurators/slash/droid.d.ts +2 -5
  45. package/dist/core/configurators/slash/droid.d.ts.map +1 -1
  46. package/dist/core/configurators/slash/droid.js +2 -32
  47. package/dist/core/configurators/slash/droid.js.map +1 -1
  48. package/dist/core/configurators/slash/forge.d.ts +2 -5
  49. package/dist/core/configurators/slash/forge.d.ts.map +1 -1
  50. package/dist/core/configurators/slash/forge.js +2 -32
  51. package/dist/core/configurators/slash/forge.js.map +1 -1
  52. package/dist/core/configurators/slash/github-copilot.d.ts +2 -7
  53. package/dist/core/configurators/slash/github-copilot.d.ts.map +1 -1
  54. package/dist/core/configurators/slash/github-copilot.js +2 -96
  55. package/dist/core/configurators/slash/github-copilot.js.map +1 -1
  56. package/dist/core/configurators/slash/index.d.ts +1 -1
  57. package/dist/core/configurators/slash/index.d.ts.map +1 -1
  58. package/dist/core/configurators/slash/index.js +1 -1
  59. package/dist/core/configurators/slash/index.js.map +1 -1
  60. package/dist/core/configurators/slash/kilocode.d.ts +2 -5
  61. package/dist/core/configurators/slash/kilocode.d.ts.map +1 -1
  62. package/dist/core/configurators/slash/kilocode.js +2 -57
  63. package/dist/core/configurators/slash/kilocode.js.map +1 -1
  64. package/dist/core/configurators/slash/opencode.d.ts +2 -5
  65. package/dist/core/configurators/slash/opencode.d.ts.map +1 -1
  66. package/dist/core/configurators/slash/opencode.js +2 -57
  67. package/dist/core/configurators/slash/opencode.js.map +1 -1
  68. package/dist/core/configurators/slash/registry.d.ts +4 -4
  69. package/dist/core/configurators/slash/registry.d.ts.map +1 -1
  70. package/dist/core/configurators/slash/registry.js.map +1 -1
  71. package/dist/core/registry.d.ts +18 -0
  72. package/dist/core/registry.d.ts.map +1 -0
  73. package/dist/core/registry.js +94 -0
  74. package/dist/core/registry.js.map +1 -0
  75. package/dist/core/templates/index.d.ts +0 -1
  76. package/dist/core/templates/index.d.ts.map +1 -1
  77. package/dist/core/templates/index.js +0 -1
  78. package/dist/core/templates/index.js.map +1 -1
  79. package/package.json +7 -1
  80. package/AGENTS.md +0 -123
  81. package/CLAUDE.md +0 -17
  82. package/build.js +0 -20
  83. package/convex-setup.md +0 -403
  84. package/dist/core/templates/slash-command-templates.d.ts +0 -7
  85. package/dist/core/templates/slash-command-templates.d.ts.map +0 -1
  86. package/dist/core/templates/slash-command-templates.js +0 -1041
  87. package/dist/core/templates/slash-command-templates.js.map +0 -1
  88. package/prompt/ai-humanizer.md +0 -45
  89. package/prompt/api-contract-generator.md +0 -234
  90. package/prompt/apply.md +0 -17
  91. package/prompt/archive.md +0 -21
  92. package/prompt/design-system.md +0 -210
  93. package/prompt/document-explainer.md +0 -149
  94. package/prompt/epic-generator.md +0 -198
  95. package/prompt/epic-single.md +0 -47
  96. package/prompt/erd-generator.md +0 -130
  97. package/prompt/fsd-generator.md +0 -157
  98. package/prompt/prd-agent-generator.md +0 -147
  99. package/prompt/prd-generator.md +0 -195
  100. package/prompt/product-brief.md +0 -289
  101. package/prompt/proposal.md +0 -22
  102. package/prompt/qa-test-scenario.md +0 -133
  103. package/prompt/skill-creator.md +0 -350
  104. package/prompt/story-generator.md +0 -278
  105. package/prompt/story-single.md +0 -70
  106. package/prompt/tdd-generator.md +0 -294
  107. package/prompt/tdd-lite-generator.md +0 -224
  108. package/prompt/wireframe-generator.md +0 -219
  109. package/skills/ai-context-generator/SKILL.md +0 -54
  110. package/skills/ai-context-generator/references/AGENTS.template.md +0 -83
  111. package/skills/ai-context-generator/references/CLAUDE.template.md +0 -39
  112. package/skills/ai-context-generator/references/behavioral-guidelines.md +0 -71
  113. package/skills/ai-context-generator/references/discovery-checklist.md +0 -40
  114. package/skills/ai-context-generator/references/examples/AGENTS.good.md +0 -103
  115. package/skills/ai-context-generator/references/extraction-checklist.md +0 -23
  116. package/skills/ai-context-generator/references/overlays/laravel.md +0 -44
  117. package/skills/cerebro/SKILL.md +0 -187
  118. package/skills/cerebro/references/agents.md +0 -213
  119. package/skills/code-review/SKILL.md +0 -373
  120. package/skills/code-review/assets/report-template-agent.md +0 -212
  121. package/skills/code-review/assets/report-template-compact.md +0 -81
  122. package/skills/code-review/assets/report-template-full.md +0 -264
  123. package/skills/code-review/assets/report-template-human.md +0 -168
  124. package/skills/code-review/references/universal-patterns.md +0 -495
  125. package/skills/design-md/README.md +0 -34
  126. package/skills/design-md/SKILL.md +0 -172
  127. package/skills/design-md/examples/DESIGN.md +0 -154
  128. package/skills/design-system-generator/SKILL.md +0 -324
  129. package/skills/design-system-generator/assets/design-system-template.md +0 -348
  130. package/skills/design-system-generator/references/extraction-patterns.md +0 -321
  131. package/skills/doc-builder/SKILL.md +0 -115
  132. package/skills/doc-builder/references/ui-patterns.md +0 -394
  133. package/skills/document-translator/SKILL.md +0 -58
  134. package/skills/enhance-prompt/README.md +0 -34
  135. package/skills/enhance-prompt/SKILL.md +0 -204
  136. package/skills/enhance-prompt/references/KEYWORDS.md +0 -114
  137. package/skills/feature-planner/SKILL.md +0 -305
  138. package/skills/feature-planner/assets/implementation-plan-template.md +0 -85
  139. package/skills/frontend-design/LICENSE.txt +0 -177
  140. package/skills/frontend-design/SKILL.md +0 -42
  141. package/skills/gamma-builder/SKILL.md +0 -134
  142. package/skills/laravel-code-review/SKILL.md +0 -383
  143. package/skills/laravel-code-review/assets/report-template-agent.md +0 -195
  144. package/skills/laravel-code-review/assets/report-template-compact.md +0 -79
  145. package/skills/laravel-code-review/assets/report-template-full.md +0 -253
  146. package/skills/laravel-code-review/assets/report-template-human.md +0 -159
  147. package/skills/laravel-code-review/references/laravel-patterns.md +0 -571
  148. package/skills/laravel-code-review/references/php84-features.md +0 -442
  149. package/skills/mcp-builder/LICENSE.txt +0 -202
  150. package/skills/mcp-builder/SKILL.md +0 -236
  151. package/skills/mcp-builder/reference/evaluation.md +0 -602
  152. package/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  153. package/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  154. package/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  155. package/skills/mcp-builder/scripts/connections.py +0 -151
  156. package/skills/mcp-builder/scripts/evaluation.py +0 -373
  157. package/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  158. package/skills/mcp-builder/scripts/requirements.txt +0 -2
  159. package/skills/meeting-notes/SKILL.md +0 -159
  160. package/skills/meeting-notes/evals/evals.json +0 -23
  161. package/skills/project-orchestrator/SKILL.md +0 -487
  162. package/skills/project-orchestrator/assets/caddy-vps-setup.md +0 -180
  163. package/skills/project-orchestrator/assets/plan-summary-template.md +0 -159
  164. package/skills/prompter-specs/SKILL.md +0 -115
  165. package/skills/prompter-workflow/SKILL.md +0 -166
  166. package/skills/prompter-workflow/evals/evals.json +0 -89
  167. package/skills/sph-generator/SKILL.md +0 -488
  168. package/skills/ui-ux-pro/SKILL.md +0 -199
  169. package/skills/ui-ux-pro/assets/design-spec-template.md +0 -173
  170. package/skills/ui-ux-pro/references/component-patterns.md +0 -255
  171. package/skills/ui-ux-pro/references/design-principles.md +0 -167
  172. package/src/cli/index.ts +0 -223
  173. package/src/commands/archive.ts +0 -302
  174. package/src/commands/change.ts +0 -292
  175. package/src/commands/config.ts +0 -233
  176. package/src/commands/guide.ts +0 -50
  177. package/src/commands/init.ts +0 -899
  178. package/src/commands/list.ts +0 -194
  179. package/src/commands/show.ts +0 -138
  180. package/src/commands/spec.ts +0 -251
  181. package/src/commands/update.ts +0 -156
  182. package/src/commands/upgrade.ts +0 -30
  183. package/src/commands/validate.ts +0 -326
  184. package/src/core/artifact-graph/graph.ts +0 -167
  185. package/src/core/artifact-graph/index.ts +0 -44
  186. package/src/core/artifact-graph/instruction-loader.ts +0 -302
  187. package/src/core/artifact-graph/resolver.ts +0 -226
  188. package/src/core/artifact-graph/schema.ts +0 -124
  189. package/src/core/artifact-graph/state.ts +0 -64
  190. package/src/core/artifact-graph/types.ts +0 -65
  191. package/src/core/completions/command-registry.ts +0 -382
  192. package/src/core/completions/completion-provider.ts +0 -128
  193. package/src/core/completions/generators/bash-generator.ts +0 -191
  194. package/src/core/completions/generators/fish-generator.ts +0 -188
  195. package/src/core/completions/generators/powershell-generator.ts +0 -223
  196. package/src/core/completions/generators/zsh-generator.ts +0 -281
  197. package/src/core/completions/templates/bash-templates.ts +0 -24
  198. package/src/core/completions/templates/fish-templates.ts +0 -40
  199. package/src/core/completions/templates/powershell-templates.ts +0 -25
  200. package/src/core/completions/templates/zsh-templates.ts +0 -36
  201. package/src/core/completions/types.ts +0 -90
  202. package/src/core/config-schema.ts +0 -230
  203. package/src/core/config.ts +0 -181
  204. package/src/core/configurators/slash/antigravity.ts +0 -70
  205. package/src/core/configurators/slash/base.ts +0 -203
  206. package/src/core/configurators/slash/claude.ts +0 -70
  207. package/src/core/configurators/slash/codex.ts +0 -70
  208. package/src/core/configurators/slash/droid.ts +0 -44
  209. package/src/core/configurators/slash/forge.ts +0 -44
  210. package/src/core/configurators/slash/github-copilot.ts +0 -114
  211. package/src/core/configurators/slash/index.ts +0 -10
  212. package/src/core/configurators/slash/kilocode.ts +0 -70
  213. package/src/core/configurators/slash/opencode.ts +0 -70
  214. package/src/core/configurators/slash/registry.ts +0 -51
  215. package/src/core/converters/json-converter.ts +0 -62
  216. package/src/core/global-config.ts +0 -136
  217. package/src/core/parsers/change-parser.ts +0 -234
  218. package/src/core/parsers/markdown-parser.ts +0 -237
  219. package/src/core/parsers/requirement-blocks.ts +0 -234
  220. package/src/core/prompt-templates.ts +0 -3504
  221. package/src/core/schemas/base.schema.ts +0 -20
  222. package/src/core/schemas/change.schema.ts +0 -42
  223. package/src/core/schemas/index.ts +0 -20
  224. package/src/core/schemas/spec.schema.ts +0 -17
  225. package/src/core/skill-discovery.ts +0 -68
  226. package/src/core/specs-apply.ts +0 -483
  227. package/src/core/styles/palette.ts +0 -8
  228. package/src/core/templates/agents-template.ts +0 -459
  229. package/src/core/templates/claude-template.ts +0 -2
  230. package/src/core/templates/index.ts +0 -4
  231. package/src/core/templates/project-template.ts +0 -32
  232. package/src/core/templates/slash-command-templates.ts +0 -1068
  233. package/src/core/validation/constants.ts +0 -48
  234. package/src/core/validation/types.ts +0 -19
  235. package/src/core/validation/validator.ts +0 -449
  236. package/src/core/view.ts +0 -219
  237. package/src/index.ts +0 -1
  238. package/src/utils/change-metadata.ts +0 -171
  239. package/src/utils/change-utils.ts +0 -131
  240. package/src/utils/file-system.ts +0 -252
  241. package/src/utils/index.ts +0 -12
  242. package/src/utils/interactive.ts +0 -29
  243. package/src/utils/item-discovery.ts +0 -66
  244. package/src/utils/match.ts +0 -26
  245. package/src/utils/shell-detection.ts +0 -62
  246. package/src/utils/task-progress.ts +0 -43
  247. package/tsconfig.json +0 -28
@@ -1,20 +0,0 @@
1
- import { z } from 'zod';
2
- import { VALIDATION_MESSAGES } from '../validation/constants.js';
3
-
4
- export const ScenarioSchema = z.object({
5
- rawText: z.string().min(1, VALIDATION_MESSAGES.SCENARIO_EMPTY),
6
- });
7
-
8
- export const RequirementSchema = z.object({
9
- text: z.string()
10
- .min(1, VALIDATION_MESSAGES.REQUIREMENT_EMPTY)
11
- .refine(
12
- (text) => text.includes('SHALL') || text.includes('MUST'),
13
- VALIDATION_MESSAGES.REQUIREMENT_NO_SHALL
14
- ),
15
- scenarios: z.array(ScenarioSchema)
16
- .min(1, VALIDATION_MESSAGES.REQUIREMENT_NO_SCENARIOS),
17
- });
18
-
19
- export type Scenario = z.infer<typeof ScenarioSchema>;
20
- export type Requirement = z.infer<typeof RequirementSchema>;
@@ -1,42 +0,0 @@
1
- import { z } from 'zod';
2
- import { RequirementSchema } from './base.schema.js';
3
- import {
4
- MIN_WHY_SECTION_LENGTH,
5
- MAX_WHY_SECTION_LENGTH,
6
- MAX_DELTAS_PER_CHANGE,
7
- VALIDATION_MESSAGES
8
- } from '../validation/constants.js';
9
-
10
- export const DeltaOperationType = z.enum(['ADDED', 'MODIFIED', 'REMOVED', 'RENAMED']);
11
-
12
- export const DeltaSchema = z.object({
13
- spec: z.string().min(1, VALIDATION_MESSAGES.DELTA_SPEC_EMPTY),
14
- operation: DeltaOperationType,
15
- description: z.string().min(1, VALIDATION_MESSAGES.DELTA_DESCRIPTION_EMPTY),
16
- requirement: RequirementSchema.optional(),
17
- requirements: z.array(RequirementSchema).optional(),
18
- rename: z.object({
19
- from: z.string(),
20
- to: z.string(),
21
- }).optional(),
22
- });
23
-
24
- export const ChangeSchema = z.object({
25
- name: z.string().min(1, VALIDATION_MESSAGES.CHANGE_NAME_EMPTY),
26
- why: z.string()
27
- .min(MIN_WHY_SECTION_LENGTH, VALIDATION_MESSAGES.CHANGE_WHY_TOO_SHORT)
28
- .max(MAX_WHY_SECTION_LENGTH, VALIDATION_MESSAGES.CHANGE_WHY_TOO_LONG),
29
- whatChanges: z.string().min(1, VALIDATION_MESSAGES.CHANGE_WHAT_EMPTY),
30
- deltas: z.array(DeltaSchema)
31
- .min(1, VALIDATION_MESSAGES.CHANGE_NO_DELTAS)
32
- .max(MAX_DELTAS_PER_CHANGE, VALIDATION_MESSAGES.CHANGE_TOO_MANY_DELTAS),
33
- metadata: z.object({
34
- version: z.string().default('1.0.0'),
35
- format: z.literal('prompter-change'),
36
- sourcePath: z.string().optional(),
37
- }).optional(),
38
- });
39
-
40
- export type DeltaOperation = z.infer<typeof DeltaOperationType>;
41
- export type Delta = z.infer<typeof DeltaSchema>;
42
- export type Change = z.infer<typeof ChangeSchema>;
@@ -1,20 +0,0 @@
1
- export {
2
- ScenarioSchema,
3
- RequirementSchema,
4
- type Scenario,
5
- type Requirement,
6
- } from './base.schema.js';
7
-
8
- export {
9
- SpecSchema,
10
- type Spec,
11
- } from './spec.schema.js';
12
-
13
- export {
14
- DeltaOperationType,
15
- DeltaSchema,
16
- ChangeSchema,
17
- type DeltaOperation,
18
- type Delta,
19
- type Change,
20
- } from './change.schema.js';
@@ -1,17 +0,0 @@
1
- import { z } from 'zod';
2
- import { RequirementSchema } from './base.schema.js';
3
- import { VALIDATION_MESSAGES } from '../validation/constants.js';
4
-
5
- export const SpecSchema = z.object({
6
- name: z.string().min(1, VALIDATION_MESSAGES.SPEC_NAME_EMPTY),
7
- overview: z.string().min(1, VALIDATION_MESSAGES.SPEC_PURPOSE_EMPTY),
8
- requirements: z.array(RequirementSchema)
9
- .min(1, VALIDATION_MESSAGES.SPEC_NO_REQUIREMENTS),
10
- metadata: z.object({
11
- version: z.string().default('1.0.0'),
12
- format: z.literal('prompter'),
13
- sourcePath: z.string().optional(),
14
- }).optional(),
15
- });
16
-
17
- export type Spec = z.infer<typeof SpecSchema>;
@@ -1,68 +0,0 @@
1
- import { promises as fs } from 'fs';
2
- import path from 'path';
3
- import yaml from 'yaml';
4
-
5
- export interface SkillMetadata {
6
- name: string;
7
- description: string;
8
- sourcePath: string;
9
- body: string;
10
- }
11
-
12
- /**
13
- * Discover skills in a directory by scanning for subdirectories containing SKILL.md files.
14
- * Parses YAML frontmatter (name, description) and extracts the markdown body.
15
- */
16
- export async function discoverSkills(skillsDir: string): Promise<SkillMetadata[]> {
17
- const skills: SkillMetadata[] = [];
18
-
19
- let entries: import('fs').Dirent[];
20
- try {
21
- entries = await fs.readdir(skillsDir, { withFileTypes: true });
22
- } catch {
23
- return skills;
24
- }
25
-
26
- for (const entry of entries) {
27
- if (!entry.isDirectory()) continue;
28
-
29
- const skillDir = path.join(skillsDir, entry.name);
30
- const skillMdPath = path.join(skillDir, 'SKILL.md');
31
-
32
- try {
33
- const content = await fs.readFile(skillMdPath, 'utf-8');
34
- const parsed = parseSkillMd(content);
35
-
36
- if (parsed) {
37
- skills.push({
38
- name: parsed.name,
39
- description: parsed.description,
40
- sourcePath: skillDir,
41
- body: parsed.body
42
- });
43
- }
44
- } catch {
45
- // SKILL.md not found or unreadable, skip this directory
46
- }
47
- }
48
-
49
- return skills.sort((a, b) => a.name.localeCompare(b.name));
50
- }
51
-
52
- function parseSkillMd(content: string): { name: string; description: string; body: string } | null {
53
- const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
54
- if (!match) return null;
55
-
56
- try {
57
- const meta = yaml.parse(match[1]);
58
- if (!meta.name || !meta.description) return null;
59
-
60
- return {
61
- name: String(meta.name),
62
- description: String(meta.description),
63
- body: match[2].trim()
64
- };
65
- } catch {
66
- return null;
67
- }
68
- }
@@ -1,483 +0,0 @@
1
- /**
2
- * Spec Application Logic
3
- *
4
- * Extracted from ArchiveCommand to enable standalone spec application.
5
- * Applies delta specs from a change to main specs without archiving.
6
- */
7
-
8
- import { promises as fs } from 'fs';
9
- import path from 'path';
10
- import chalk from 'chalk';
11
- import {
12
- extractRequirementsSection,
13
- parseDeltaSpec,
14
- normalizeRequirementName,
15
- type RequirementBlock,
16
- } from './parsers/requirement-blocks.js';
17
- import { Validator } from './validation/validator.js';
18
-
19
- // -----------------------------------------------------------------------------
20
- // Types
21
- // -----------------------------------------------------------------------------
22
-
23
- export interface SpecUpdate {
24
- source: string;
25
- target: string;
26
- exists: boolean;
27
- }
28
-
29
- export interface ApplyResult {
30
- capability: string;
31
- added: number;
32
- modified: number;
33
- removed: number;
34
- renamed: number;
35
- }
36
-
37
- export interface SpecsApplyOutput {
38
- changeName: string;
39
- capabilities: ApplyResult[];
40
- totals: {
41
- added: number;
42
- modified: number;
43
- removed: number;
44
- renamed: number;
45
- };
46
- noChanges: boolean;
47
- }
48
-
49
- // -----------------------------------------------------------------------------
50
- // Public API
51
- // -----------------------------------------------------------------------------
52
-
53
- /**
54
- * Find all delta spec files that need to be applied from a change.
55
- */
56
- export async function findSpecUpdates(changeDir: string, mainSpecsDir: string): Promise<SpecUpdate[]> {
57
- const updates: SpecUpdate[] = [];
58
- const changeSpecsDir = path.join(changeDir, 'specs');
59
-
60
- try {
61
- const entries = await fs.readdir(changeSpecsDir, { withFileTypes: true });
62
-
63
- for (const entry of entries) {
64
- if (entry.isDirectory()) {
65
- const specFile = path.join(changeSpecsDir, entry.name, 'spec.md');
66
- const targetFile = path.join(mainSpecsDir, entry.name, 'spec.md');
67
-
68
- try {
69
- await fs.access(specFile);
70
-
71
- // Check if target exists
72
- let exists = false;
73
- try {
74
- await fs.access(targetFile);
75
- exists = true;
76
- } catch {
77
- exists = false;
78
- }
79
-
80
- updates.push({
81
- source: specFile,
82
- target: targetFile,
83
- exists,
84
- });
85
- } catch {
86
- // Source spec doesn't exist, skip
87
- }
88
- }
89
- }
90
- } catch {
91
- // No specs directory in change
92
- }
93
-
94
- return updates;
95
- }
96
-
97
- /**
98
- * Build an updated spec by applying delta operations.
99
- * Returns the rebuilt content and counts of operations.
100
- */
101
- export async function buildUpdatedSpec(
102
- update: SpecUpdate,
103
- changeName: string
104
- ): Promise<{ rebuilt: string; counts: { added: number; modified: number; removed: number; renamed: number } }> {
105
- // Read change spec content (delta-format expected)
106
- const changeContent = await fs.readFile(update.source, 'utf-8');
107
-
108
- // Parse deltas from the change spec file
109
- const plan = parseDeltaSpec(changeContent);
110
- const specName = path.basename(path.dirname(update.target));
111
-
112
- // Pre-validate duplicates within sections
113
- const addedNames = new Set<string>();
114
- for (const add of plan.added) {
115
- const name = normalizeRequirementName(add.name);
116
- if (addedNames.has(name)) {
117
- throw new Error(
118
- `${specName} validation failed - duplicate requirement in ADDED for header "### Requirement: ${add.name}"`
119
- );
120
- }
121
- addedNames.add(name);
122
- }
123
- const modifiedNames = new Set<string>();
124
- for (const mod of plan.modified) {
125
- const name = normalizeRequirementName(mod.name);
126
- if (modifiedNames.has(name)) {
127
- throw new Error(
128
- `${specName} validation failed - duplicate requirement in MODIFIED for header "### Requirement: ${mod.name}"`
129
- );
130
- }
131
- modifiedNames.add(name);
132
- }
133
- const removedNamesSet = new Set<string>();
134
- for (const rem of plan.removed) {
135
- const name = normalizeRequirementName(rem);
136
- if (removedNamesSet.has(name)) {
137
- throw new Error(
138
- `${specName} validation failed - duplicate requirement in REMOVED for header "### Requirement: ${rem}"`
139
- );
140
- }
141
- removedNamesSet.add(name);
142
- }
143
- const renamedFromSet = new Set<string>();
144
- const renamedToSet = new Set<string>();
145
- for (const { from, to } of plan.renamed) {
146
- const fromNorm = normalizeRequirementName(from);
147
- const toNorm = normalizeRequirementName(to);
148
- if (renamedFromSet.has(fromNorm)) {
149
- throw new Error(
150
- `${specName} validation failed - duplicate FROM in RENAMED for header "### Requirement: ${from}"`
151
- );
152
- }
153
- if (renamedToSet.has(toNorm)) {
154
- throw new Error(
155
- `${specName} validation failed - duplicate TO in RENAMED for header "### Requirement: ${to}"`
156
- );
157
- }
158
- renamedFromSet.add(fromNorm);
159
- renamedToSet.add(toNorm);
160
- }
161
-
162
- // Pre-validate cross-section conflicts
163
- const conflicts: Array<{ name: string; a: string; b: string }> = [];
164
- for (const n of modifiedNames) {
165
- if (removedNamesSet.has(n)) conflicts.push({ name: n, a: 'MODIFIED', b: 'REMOVED' });
166
- if (addedNames.has(n)) conflicts.push({ name: n, a: 'MODIFIED', b: 'ADDED' });
167
- }
168
- for (const n of addedNames) {
169
- if (removedNamesSet.has(n)) conflicts.push({ name: n, a: 'ADDED', b: 'REMOVED' });
170
- }
171
- // Renamed interplay: MODIFIED must reference the NEW header, not FROM
172
- for (const { from, to } of plan.renamed) {
173
- const fromNorm = normalizeRequirementName(from);
174
- const toNorm = normalizeRequirementName(to);
175
- if (modifiedNames.has(fromNorm)) {
176
- throw new Error(
177
- `${specName} validation failed - when a rename exists, MODIFIED must reference the NEW header "### Requirement: ${to}"`
178
- );
179
- }
180
- // Detect ADDED colliding with a RENAMED TO
181
- if (addedNames.has(toNorm)) {
182
- throw new Error(
183
- `${specName} validation failed - RENAMED TO header collides with ADDED for "### Requirement: ${to}"`
184
- );
185
- }
186
- }
187
- if (conflicts.length > 0) {
188
- const c = conflicts[0];
189
- throw new Error(
190
- `${specName} validation failed - requirement present in multiple sections (${c.a} and ${c.b}) for header "### Requirement: ${c.name}"`
191
- );
192
- }
193
- const hasAnyDelta = plan.added.length + plan.modified.length + plan.removed.length + plan.renamed.length > 0;
194
- if (!hasAnyDelta) {
195
- throw new Error(
196
- `Delta parsing found no operations for ${path.basename(path.dirname(update.source))}. ` +
197
- `Provide ADDED/MODIFIED/REMOVED/RENAMED sections in change spec.`
198
- );
199
- }
200
-
201
- // Load or create base target content
202
- let targetContent: string;
203
- let isNewSpec = false;
204
- try {
205
- targetContent = await fs.readFile(update.target, 'utf-8');
206
- } catch {
207
- // Target spec does not exist; MODIFIED and RENAMED are not allowed for new specs
208
- // REMOVED will be ignored with a warning since there's nothing to remove
209
- if (plan.modified.length > 0 || plan.renamed.length > 0) {
210
- throw new Error(
211
- `${specName}: target spec does not exist; only ADDED requirements are allowed for new specs. MODIFIED and RENAMED operations require an existing spec.`
212
- );
213
- }
214
- // Warn about REMOVED requirements being ignored for new specs
215
- if (plan.removed.length > 0) {
216
- console.log(
217
- chalk.yellow(
218
- `⚠️ Warning: ${specName} - ${plan.removed.length} REMOVED requirement(s) ignored for new spec (nothing to remove).`
219
- )
220
- );
221
- }
222
- isNewSpec = true;
223
- targetContent = buildSpecSkeleton(specName, changeName);
224
- }
225
-
226
- // Extract requirements section and build name->block map
227
- const parts = extractRequirementsSection(targetContent);
228
- const nameToBlock = new Map<string, RequirementBlock>();
229
- for (const block of parts.bodyBlocks) {
230
- nameToBlock.set(normalizeRequirementName(block.name), block);
231
- }
232
-
233
- // Apply operations in order: RENAMED → REMOVED → MODIFIED → ADDED
234
- // RENAMED
235
- for (const r of plan.renamed) {
236
- const from = normalizeRequirementName(r.from);
237
- const to = normalizeRequirementName(r.to);
238
- if (!nameToBlock.has(from)) {
239
- throw new Error(`${specName} RENAMED failed for header "### Requirement: ${r.from}" - source not found`);
240
- }
241
- if (nameToBlock.has(to)) {
242
- throw new Error(`${specName} RENAMED failed for header "### Requirement: ${r.to}" - target already exists`);
243
- }
244
- const block = nameToBlock.get(from)!;
245
- const newHeader = `### Requirement: ${to}`;
246
- const rawLines = block.raw.split('\n');
247
- rawLines[0] = newHeader;
248
- const renamedBlock: RequirementBlock = {
249
- headerLine: newHeader,
250
- name: to,
251
- raw: rawLines.join('\n'),
252
- };
253
- nameToBlock.delete(from);
254
- nameToBlock.set(to, renamedBlock);
255
- }
256
-
257
- // REMOVED
258
- for (const name of plan.removed) {
259
- const key = normalizeRequirementName(name);
260
- if (!nameToBlock.has(key)) {
261
- // For new specs, REMOVED requirements are already warned about and ignored
262
- // For existing specs, missing requirements are an error
263
- if (!isNewSpec) {
264
- throw new Error(`${specName} REMOVED failed for header "### Requirement: ${name}" - not found`);
265
- }
266
- // Skip removal for new specs (already warned above)
267
- continue;
268
- }
269
- nameToBlock.delete(key);
270
- }
271
-
272
- // MODIFIED
273
- for (const mod of plan.modified) {
274
- const key = normalizeRequirementName(mod.name);
275
- if (!nameToBlock.has(key)) {
276
- throw new Error(`${specName} MODIFIED failed for header "### Requirement: ${mod.name}" - not found`);
277
- }
278
- // Replace block with provided raw (ensure header line matches key)
279
- const modHeaderMatch = mod.raw.split('\n')[0].match(/^###\s*Requirement:\s*(.+)\s*$/);
280
- if (!modHeaderMatch || normalizeRequirementName(modHeaderMatch[1]) !== key) {
281
- throw new Error(
282
- `${specName} MODIFIED failed for header "### Requirement: ${mod.name}" - header mismatch in content`
283
- );
284
- }
285
- nameToBlock.set(key, mod);
286
- }
287
-
288
- // ADDED
289
- for (const add of plan.added) {
290
- const key = normalizeRequirementName(add.name);
291
- if (nameToBlock.has(key)) {
292
- throw new Error(`${specName} ADDED failed for header "### Requirement: ${add.name}" - already exists`);
293
- }
294
- nameToBlock.set(key, add);
295
- }
296
-
297
- // Duplicates within resulting map are implicitly prevented by key uniqueness.
298
-
299
- // Recompose requirements section preserving original ordering where possible
300
- const keptOrder: RequirementBlock[] = [];
301
- const seen = new Set<string>();
302
- for (const block of parts.bodyBlocks) {
303
- const key = normalizeRequirementName(block.name);
304
- const replacement = nameToBlock.get(key);
305
- if (replacement) {
306
- keptOrder.push(replacement);
307
- seen.add(key);
308
- }
309
- }
310
- // Append any newly added that were not in original order
311
- for (const [key, block] of nameToBlock.entries()) {
312
- if (!seen.has(key)) {
313
- keptOrder.push(block);
314
- }
315
- }
316
-
317
- const reqBody = [parts.preamble && parts.preamble.trim() ? parts.preamble.trimEnd() : '']
318
- .filter(Boolean)
319
- .concat(keptOrder.map((b) => b.raw))
320
- .join('\n\n')
321
- .trimEnd();
322
-
323
- const rebuilt = [parts.before.trimEnd(), parts.headerLine, reqBody, parts.after]
324
- .filter((s, idx) => !(idx === 0 && s === ''))
325
- .join('\n')
326
- .replace(/\n{3,}/g, '\n\n');
327
-
328
- return {
329
- rebuilt,
330
- counts: {
331
- added: plan.added.length,
332
- modified: plan.modified.length,
333
- removed: plan.removed.length,
334
- renamed: plan.renamed.length,
335
- },
336
- };
337
- }
338
-
339
- /**
340
- * Write an updated spec to disk.
341
- */
342
- export async function writeUpdatedSpec(
343
- update: SpecUpdate,
344
- rebuilt: string,
345
- counts: { added: number; modified: number; removed: number; renamed: number }
346
- ): Promise<void> {
347
- // Create target directory if needed
348
- const targetDir = path.dirname(update.target);
349
- await fs.mkdir(targetDir, { recursive: true });
350
- await fs.writeFile(update.target, rebuilt);
351
-
352
- const specName = path.basename(path.dirname(update.target));
353
- console.log(`Applying changes to prompter/specs/${specName}/spec.md:`);
354
- if (counts.added) console.log(` + ${counts.added} added`);
355
- if (counts.modified) console.log(` ~ ${counts.modified} modified`);
356
- if (counts.removed) console.log(` - ${counts.removed} removed`);
357
- if (counts.renamed) console.log(` → ${counts.renamed} renamed`);
358
- }
359
-
360
- /**
361
- * Build a skeleton spec for new capabilities.
362
- */
363
- export function buildSpecSkeleton(specFolderName: string, changeName: string): string {
364
- const titleBase = specFolderName;
365
- return `# ${titleBase} Specification\n\n## Purpose\nTBD - created by archiving change ${changeName}. Update Purpose after archive.\n\n## Requirements\n`;
366
- }
367
-
368
- /**
369
- * Apply all delta specs from a change to main specs.
370
- *
371
- * @param projectRoot - The project root directory
372
- * @param changeName - The name of the change to apply
373
- * @param options - Options for the operation
374
- * @returns Result of the operation with counts
375
- */
376
- export async function applySpecs(
377
- projectRoot: string,
378
- changeName: string,
379
- options: {
380
- dryRun?: boolean;
381
- skipValidation?: boolean;
382
- silent?: boolean;
383
- } = {}
384
- ): Promise<SpecsApplyOutput> {
385
- const changeDir = path.join(projectRoot, 'prompter', 'changes', changeName);
386
- const mainSpecsDir = path.join(projectRoot, 'prompter', 'specs');
387
-
388
- // Verify change exists
389
- try {
390
- const stat = await fs.stat(changeDir);
391
- if (!stat.isDirectory()) {
392
- throw new Error(`Change '${changeName}' not found.`);
393
- }
394
- } catch {
395
- throw new Error(`Change '${changeName}' not found.`);
396
- }
397
-
398
- // Find specs to update
399
- const specUpdates = await findSpecUpdates(changeDir, mainSpecsDir);
400
-
401
- if (specUpdates.length === 0) {
402
- return {
403
- changeName,
404
- capabilities: [],
405
- totals: { added: 0, modified: 0, removed: 0, renamed: 0 },
406
- noChanges: true,
407
- };
408
- }
409
-
410
- // Prepare all updates first (validation pass, no writes)
411
- const prepared: Array<{
412
- update: SpecUpdate;
413
- rebuilt: string;
414
- counts: { added: number; modified: number; removed: number; renamed: number };
415
- }> = [];
416
-
417
- for (const update of specUpdates) {
418
- const built = await buildUpdatedSpec(update, changeName);
419
- prepared.push({ update, rebuilt: built.rebuilt, counts: built.counts });
420
- }
421
-
422
- // Validate rebuilt specs unless validation is skipped
423
- if (!options.skipValidation) {
424
- const validator = new Validator();
425
- for (const p of prepared) {
426
- const specName = path.basename(path.dirname(p.update.target));
427
- const report = await validator.validateSpecContent(specName, p.rebuilt);
428
- if (!report.valid) {
429
- const errors = report.issues
430
- .filter((i) => i.level === 'ERROR')
431
- .map((i) => ` ✗ ${i.message}`)
432
- .join('\n');
433
- throw new Error(`Validation errors in rebuilt spec for ${specName}:\n${errors}`);
434
- }
435
- }
436
- }
437
-
438
- // Build results
439
- const capabilities: ApplyResult[] = [];
440
- const totals = { added: 0, modified: 0, removed: 0, renamed: 0 };
441
-
442
- for (const p of prepared) {
443
- const capability = path.basename(path.dirname(p.update.target));
444
-
445
- if (!options.dryRun) {
446
- // Write the updated spec
447
- const targetDir = path.dirname(p.update.target);
448
- await fs.mkdir(targetDir, { recursive: true });
449
- await fs.writeFile(p.update.target, p.rebuilt);
450
-
451
- if (!options.silent) {
452
- console.log(`Applying changes to prompter/specs/${capability}/spec.md:`);
453
- if (p.counts.added) console.log(` + ${p.counts.added} added`);
454
- if (p.counts.modified) console.log(` ~ ${p.counts.modified} modified`);
455
- if (p.counts.removed) console.log(` - ${p.counts.removed} removed`);
456
- if (p.counts.renamed) console.log(` → ${p.counts.renamed} renamed`);
457
- }
458
- } else if (!options.silent) {
459
- console.log(`Would apply changes to prompter/specs/${capability}/spec.md:`);
460
- if (p.counts.added) console.log(` + ${p.counts.added} added`);
461
- if (p.counts.modified) console.log(` ~ ${p.counts.modified} modified`);
462
- if (p.counts.removed) console.log(` - ${p.counts.removed} removed`);
463
- if (p.counts.renamed) console.log(` → ${p.counts.renamed} renamed`);
464
- }
465
-
466
- capabilities.push({
467
- capability,
468
- ...p.counts,
469
- });
470
-
471
- totals.added += p.counts.added;
472
- totals.modified += p.counts.modified;
473
- totals.removed += p.counts.removed;
474
- totals.renamed += p.counts.renamed;
475
- }
476
-
477
- return {
478
- changeName,
479
- capabilities,
480
- totals,
481
- noChanges: false,
482
- };
483
- }
@@ -1,8 +0,0 @@
1
- import chalk from 'chalk';
2
-
3
- export const PALETTE = {
4
- white: chalk.hex('#f4f4f4'),
5
- lightGray: chalk.hex('#c8c8c8'),
6
- midGray: chalk.hex('#8a8a8a'),
7
- darkGray: chalk.hex('#4a4a4a')
8
- };