@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.
- package/CHANGELOG.md +70 -0
- package/README.md +105 -77
- package/dist/cli/index.js +25 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +1 -7
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +60 -299
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.d.ts +4 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +56 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +4 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +14 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +31 -41
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/whoami.d.ts +4 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +42 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/core/auth-store.d.ts +10 -0
- package/dist/core/auth-store.d.ts.map +1 -0
- package/dist/core/auth-store.js +39 -0
- package/dist/core/auth-store.js.map +1 -0
- package/dist/core/configurators/slash/antigravity.d.ts +2 -5
- package/dist/core/configurators/slash/antigravity.d.ts.map +1 -1
- package/dist/core/configurators/slash/antigravity.js +2 -57
- package/dist/core/configurators/slash/antigravity.js.map +1 -1
- package/dist/core/configurators/slash/base.d.ts +6 -18
- package/dist/core/configurators/slash/base.d.ts.map +1 -1
- package/dist/core/configurators/slash/base.js +8 -77
- package/dist/core/configurators/slash/base.js.map +1 -1
- package/dist/core/configurators/slash/claude.d.ts +2 -5
- package/dist/core/configurators/slash/claude.d.ts.map +1 -1
- package/dist/core/configurators/slash/claude.js +2 -57
- package/dist/core/configurators/slash/claude.js.map +1 -1
- package/dist/core/configurators/slash/codex.d.ts +2 -5
- package/dist/core/configurators/slash/codex.d.ts.map +1 -1
- package/dist/core/configurators/slash/codex.js +2 -57
- package/dist/core/configurators/slash/codex.js.map +1 -1
- package/dist/core/configurators/slash/droid.d.ts +2 -5
- package/dist/core/configurators/slash/droid.d.ts.map +1 -1
- package/dist/core/configurators/slash/droid.js +2 -32
- package/dist/core/configurators/slash/droid.js.map +1 -1
- package/dist/core/configurators/slash/forge.d.ts +2 -5
- package/dist/core/configurators/slash/forge.d.ts.map +1 -1
- package/dist/core/configurators/slash/forge.js +2 -32
- package/dist/core/configurators/slash/forge.js.map +1 -1
- package/dist/core/configurators/slash/github-copilot.d.ts +2 -7
- package/dist/core/configurators/slash/github-copilot.d.ts.map +1 -1
- package/dist/core/configurators/slash/github-copilot.js +2 -96
- package/dist/core/configurators/slash/github-copilot.js.map +1 -1
- package/dist/core/configurators/slash/index.d.ts +1 -1
- package/dist/core/configurators/slash/index.d.ts.map +1 -1
- package/dist/core/configurators/slash/index.js +1 -1
- package/dist/core/configurators/slash/index.js.map +1 -1
- package/dist/core/configurators/slash/kilocode.d.ts +2 -5
- package/dist/core/configurators/slash/kilocode.d.ts.map +1 -1
- package/dist/core/configurators/slash/kilocode.js +2 -57
- package/dist/core/configurators/slash/kilocode.js.map +1 -1
- package/dist/core/configurators/slash/opencode.d.ts +2 -5
- package/dist/core/configurators/slash/opencode.d.ts.map +1 -1
- package/dist/core/configurators/slash/opencode.js +2 -57
- package/dist/core/configurators/slash/opencode.js.map +1 -1
- package/dist/core/configurators/slash/registry.d.ts +4 -4
- package/dist/core/configurators/slash/registry.d.ts.map +1 -1
- package/dist/core/configurators/slash/registry.js.map +1 -1
- package/dist/core/registry.d.ts +18 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +94 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/templates/index.d.ts +0 -1
- package/dist/core/templates/index.d.ts.map +1 -1
- package/dist/core/templates/index.js +0 -1
- package/dist/core/templates/index.js.map +1 -1
- package/package.json +7 -1
- package/AGENTS.md +0 -123
- package/CLAUDE.md +0 -17
- package/build.js +0 -20
- package/convex-setup.md +0 -403
- package/dist/core/templates/slash-command-templates.d.ts +0 -7
- package/dist/core/templates/slash-command-templates.d.ts.map +0 -1
- package/dist/core/templates/slash-command-templates.js +0 -1041
- package/dist/core/templates/slash-command-templates.js.map +0 -1
- package/prompt/ai-humanizer.md +0 -45
- package/prompt/api-contract-generator.md +0 -234
- package/prompt/apply.md +0 -17
- package/prompt/archive.md +0 -21
- package/prompt/design-system.md +0 -210
- package/prompt/document-explainer.md +0 -149
- package/prompt/epic-generator.md +0 -198
- package/prompt/epic-single.md +0 -47
- package/prompt/erd-generator.md +0 -130
- package/prompt/fsd-generator.md +0 -157
- package/prompt/prd-agent-generator.md +0 -147
- package/prompt/prd-generator.md +0 -195
- package/prompt/product-brief.md +0 -289
- package/prompt/proposal.md +0 -22
- package/prompt/qa-test-scenario.md +0 -133
- package/prompt/skill-creator.md +0 -350
- package/prompt/story-generator.md +0 -278
- package/prompt/story-single.md +0 -70
- package/prompt/tdd-generator.md +0 -294
- package/prompt/tdd-lite-generator.md +0 -224
- package/prompt/wireframe-generator.md +0 -219
- package/skills/ai-context-generator/SKILL.md +0 -54
- package/skills/ai-context-generator/references/AGENTS.template.md +0 -83
- package/skills/ai-context-generator/references/CLAUDE.template.md +0 -39
- package/skills/ai-context-generator/references/behavioral-guidelines.md +0 -71
- package/skills/ai-context-generator/references/discovery-checklist.md +0 -40
- package/skills/ai-context-generator/references/examples/AGENTS.good.md +0 -103
- package/skills/ai-context-generator/references/extraction-checklist.md +0 -23
- package/skills/ai-context-generator/references/overlays/laravel.md +0 -44
- package/skills/cerebro/SKILL.md +0 -187
- package/skills/cerebro/references/agents.md +0 -213
- package/skills/code-review/SKILL.md +0 -373
- package/skills/code-review/assets/report-template-agent.md +0 -212
- package/skills/code-review/assets/report-template-compact.md +0 -81
- package/skills/code-review/assets/report-template-full.md +0 -264
- package/skills/code-review/assets/report-template-human.md +0 -168
- package/skills/code-review/references/universal-patterns.md +0 -495
- package/skills/design-md/README.md +0 -34
- package/skills/design-md/SKILL.md +0 -172
- package/skills/design-md/examples/DESIGN.md +0 -154
- package/skills/design-system-generator/SKILL.md +0 -324
- package/skills/design-system-generator/assets/design-system-template.md +0 -348
- package/skills/design-system-generator/references/extraction-patterns.md +0 -321
- package/skills/doc-builder/SKILL.md +0 -115
- package/skills/doc-builder/references/ui-patterns.md +0 -394
- package/skills/document-translator/SKILL.md +0 -58
- package/skills/enhance-prompt/README.md +0 -34
- package/skills/enhance-prompt/SKILL.md +0 -204
- package/skills/enhance-prompt/references/KEYWORDS.md +0 -114
- package/skills/feature-planner/SKILL.md +0 -305
- package/skills/feature-planner/assets/implementation-plan-template.md +0 -85
- package/skills/frontend-design/LICENSE.txt +0 -177
- package/skills/frontend-design/SKILL.md +0 -42
- package/skills/gamma-builder/SKILL.md +0 -134
- package/skills/laravel-code-review/SKILL.md +0 -383
- package/skills/laravel-code-review/assets/report-template-agent.md +0 -195
- package/skills/laravel-code-review/assets/report-template-compact.md +0 -79
- package/skills/laravel-code-review/assets/report-template-full.md +0 -253
- package/skills/laravel-code-review/assets/report-template-human.md +0 -159
- package/skills/laravel-code-review/references/laravel-patterns.md +0 -571
- package/skills/laravel-code-review/references/php84-features.md +0 -442
- package/skills/mcp-builder/LICENSE.txt +0 -202
- package/skills/mcp-builder/SKILL.md +0 -236
- package/skills/mcp-builder/reference/evaluation.md +0 -602
- package/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
- package/skills/mcp-builder/reference/node_mcp_server.md +0 -970
- package/skills/mcp-builder/reference/python_mcp_server.md +0 -719
- package/skills/mcp-builder/scripts/connections.py +0 -151
- package/skills/mcp-builder/scripts/evaluation.py +0 -373
- package/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
- package/skills/mcp-builder/scripts/requirements.txt +0 -2
- package/skills/meeting-notes/SKILL.md +0 -159
- package/skills/meeting-notes/evals/evals.json +0 -23
- package/skills/project-orchestrator/SKILL.md +0 -487
- package/skills/project-orchestrator/assets/caddy-vps-setup.md +0 -180
- package/skills/project-orchestrator/assets/plan-summary-template.md +0 -159
- package/skills/prompter-specs/SKILL.md +0 -115
- package/skills/prompter-workflow/SKILL.md +0 -166
- package/skills/prompter-workflow/evals/evals.json +0 -89
- package/skills/sph-generator/SKILL.md +0 -488
- package/skills/ui-ux-pro/SKILL.md +0 -199
- package/skills/ui-ux-pro/assets/design-spec-template.md +0 -173
- package/skills/ui-ux-pro/references/component-patterns.md +0 -255
- package/skills/ui-ux-pro/references/design-principles.md +0 -167
- package/src/cli/index.ts +0 -223
- package/src/commands/archive.ts +0 -302
- package/src/commands/change.ts +0 -292
- package/src/commands/config.ts +0 -233
- package/src/commands/guide.ts +0 -50
- package/src/commands/init.ts +0 -899
- package/src/commands/list.ts +0 -194
- package/src/commands/show.ts +0 -138
- package/src/commands/spec.ts +0 -251
- package/src/commands/update.ts +0 -156
- package/src/commands/upgrade.ts +0 -30
- package/src/commands/validate.ts +0 -326
- package/src/core/artifact-graph/graph.ts +0 -167
- package/src/core/artifact-graph/index.ts +0 -44
- package/src/core/artifact-graph/instruction-loader.ts +0 -302
- package/src/core/artifact-graph/resolver.ts +0 -226
- package/src/core/artifact-graph/schema.ts +0 -124
- package/src/core/artifact-graph/state.ts +0 -64
- package/src/core/artifact-graph/types.ts +0 -65
- package/src/core/completions/command-registry.ts +0 -382
- package/src/core/completions/completion-provider.ts +0 -128
- package/src/core/completions/generators/bash-generator.ts +0 -191
- package/src/core/completions/generators/fish-generator.ts +0 -188
- package/src/core/completions/generators/powershell-generator.ts +0 -223
- package/src/core/completions/generators/zsh-generator.ts +0 -281
- package/src/core/completions/templates/bash-templates.ts +0 -24
- package/src/core/completions/templates/fish-templates.ts +0 -40
- package/src/core/completions/templates/powershell-templates.ts +0 -25
- package/src/core/completions/templates/zsh-templates.ts +0 -36
- package/src/core/completions/types.ts +0 -90
- package/src/core/config-schema.ts +0 -230
- package/src/core/config.ts +0 -181
- package/src/core/configurators/slash/antigravity.ts +0 -70
- package/src/core/configurators/slash/base.ts +0 -203
- package/src/core/configurators/slash/claude.ts +0 -70
- package/src/core/configurators/slash/codex.ts +0 -70
- package/src/core/configurators/slash/droid.ts +0 -44
- package/src/core/configurators/slash/forge.ts +0 -44
- package/src/core/configurators/slash/github-copilot.ts +0 -114
- package/src/core/configurators/slash/index.ts +0 -10
- package/src/core/configurators/slash/kilocode.ts +0 -70
- package/src/core/configurators/slash/opencode.ts +0 -70
- package/src/core/configurators/slash/registry.ts +0 -51
- package/src/core/converters/json-converter.ts +0 -62
- package/src/core/global-config.ts +0 -136
- package/src/core/parsers/change-parser.ts +0 -234
- package/src/core/parsers/markdown-parser.ts +0 -237
- package/src/core/parsers/requirement-blocks.ts +0 -234
- package/src/core/prompt-templates.ts +0 -3504
- package/src/core/schemas/base.schema.ts +0 -20
- package/src/core/schemas/change.schema.ts +0 -42
- package/src/core/schemas/index.ts +0 -20
- package/src/core/schemas/spec.schema.ts +0 -17
- package/src/core/skill-discovery.ts +0 -68
- package/src/core/specs-apply.ts +0 -483
- package/src/core/styles/palette.ts +0 -8
- package/src/core/templates/agents-template.ts +0 -459
- package/src/core/templates/claude-template.ts +0 -2
- package/src/core/templates/index.ts +0 -4
- package/src/core/templates/project-template.ts +0 -32
- package/src/core/templates/slash-command-templates.ts +0 -1068
- package/src/core/validation/constants.ts +0 -48
- package/src/core/validation/types.ts +0 -19
- package/src/core/validation/validator.ts +0 -449
- package/src/core/view.ts +0 -219
- package/src/index.ts +0 -1
- package/src/utils/change-metadata.ts +0 -171
- package/src/utils/change-utils.ts +0 -131
- package/src/utils/file-system.ts +0 -252
- package/src/utils/index.ts +0 -12
- package/src/utils/interactive.ts +0 -29
- package/src/utils/item-discovery.ts +0 -66
- package/src/utils/match.ts +0 -26
- package/src/utils/shell-detection.ts +0 -62
- package/src/utils/task-progress.ts +0 -43
- package/tsconfig.json +0 -28
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
# Design Principles — Anti-AI Aesthetic & Authentic Design Craft
|
|
2
|
-
|
|
3
|
-
Reference for producing designs that feel human-crafted, intentional, and unique. Load this when implementing designs or reviewing design quality.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Anti-AI-Look Patterns
|
|
8
|
-
|
|
9
|
-
AI-generated designs share recognizable traits. Avoid these to produce authentic work.
|
|
10
|
-
|
|
11
|
-
### Layout Anti-Patterns
|
|
12
|
-
|
|
13
|
-
| AI Trap | Why It Feels Generic | Do Instead |
|
|
14
|
-
|---|---|---|
|
|
15
|
-
| Perfect 3-column symmetry for everything | Real content isn't symmetric | Vary column widths, use 2-col or asymmetric grids |
|
|
16
|
-
| Every section is full-width hero + centered text | Repetitive rhythm, no visual variety | Mix layout types: sidebar, offset, editorial, split |
|
|
17
|
-
| Giant hero with headline + subtitle + CTA on every page | Cookie-cutter template feel | Match the hero to content importance — some pages don't need one |
|
|
18
|
-
| Everything centered on the page | Monotonous visual flow | Use left-aligned text with intentional center/right accents |
|
|
19
|
-
| Equal spacing everywhere | Flat, robotic rhythm | Vary spacing to create grouping and breathing room |
|
|
20
|
-
| Card grids with identical card sizes | Catalog/stock feel | Vary card sizes, feature one, use masonry or editorial layouts |
|
|
21
|
-
|
|
22
|
-
### Color Anti-Patterns
|
|
23
|
-
|
|
24
|
-
| AI Trap | Why It Feels Generic | Do Instead |
|
|
25
|
-
|---|---|---|
|
|
26
|
-
| Blue-to-purple gradient as primary | Overused AI default | Choose a palette rooted in the brand or project's personality |
|
|
27
|
-
| Neon accent on dark background | "Tech startup template" look | Use muted, sophisticated accents — or bold ones with restraint |
|
|
28
|
-
| Using 6+ colors with equal prominence | Visual noise, no hierarchy | 1-2 primary colors, 1 accent, rest neutrals |
|
|
29
|
-
| Gradient backgrounds on every section | Decorative without purpose | Use solid colors; reserve gradients for specific emphasis |
|
|
30
|
-
| Pure black (#000) text on pure white (#fff) | Harsh contrast, screen glare | Use near-black (#1a1a1a–#2d2d2d) on off-white (#f8f8f8–#fafafa) |
|
|
31
|
-
| Colored shadows (purple/blue box-shadows) | Trendy but rarely appropriate | Neutral shadows unless the brand specifically calls for it |
|
|
32
|
-
|
|
33
|
-
### Typography Anti-Patterns
|
|
34
|
-
|
|
35
|
-
| AI Trap | Why It Feels Generic | Do Instead |
|
|
36
|
-
|---|---|---|
|
|
37
|
-
| Using Inter/Poppins/Montserrat for everything | Ubiquitous AI defaults | Consider the project's personality — serif for editorial, geometric sans for tech, humanist for approachable |
|
|
38
|
-
| Only 2 sizes: heading and body | Flat hierarchy | Use a proper type scale (e.g., 12/14/16/20/24/32/48) |
|
|
39
|
-
| All text in the same weight | No emphasis, everything competes | Use 2-3 weights (regular, medium, bold) with clear purpose |
|
|
40
|
-
| ALL CAPS EVERYWHERE | Aggressive, hard to read | Reserve caps for small labels, buttons, or overlines |
|
|
41
|
-
| Giant display text with no supporting content | Empty visual calories | Size should match content importance |
|
|
42
|
-
|
|
43
|
-
### Component Anti-Patterns
|
|
44
|
-
|
|
45
|
-
| AI Trap | Why It Feels Generic | Do Instead |
|
|
46
|
-
|---|---|---|
|
|
47
|
-
| border-radius: 9999px on everything | Bubbly, toy-like | Choose 1-2 radius values that match the design's tone (4-8px for sharp, 12-16px for soft) |
|
|
48
|
-
| Glassmorphism / frosted glass everywhere | Trend over function | Use glass effects sparingly where depth communication is needed |
|
|
49
|
-
| Drop shadows on everything | Visual heaviness, everything "floats" | Shadow only elements that conceptually need elevation (modals, dropdowns, cards on hover) |
|
|
50
|
-
| Outlined icon + outlined button + outlined card | Everything is a border, nothing has weight | Mix filled and outlined treatments for hierarchy |
|
|
51
|
-
| Decorative blob SVGs or wave dividers | Instant AI template identifier | Use geometric shapes with purpose, or no decoration at all |
|
|
52
|
-
| Stock illustration people with purple skin | Unmistakable AI/startup template | Use photography, custom illustration, or no illustration |
|
|
53
|
-
|
|
54
|
-
### Interaction Anti-Patterns
|
|
55
|
-
|
|
56
|
-
| AI Trap | Why It Feels Generic | Do Instead |
|
|
57
|
-
|---|---|---|
|
|
58
|
-
| Everything has a hover scale(1.05) | Bouncy, distracting | Reserve scale for interactive cards; use color/shadow changes for most hover states |
|
|
59
|
-
| 0.3s ease on everything | Sluggish, indiscriminate | Use faster transitions (150-200ms) for small elements, slower (300-400ms) for layout changes |
|
|
60
|
-
| Fade-in-up on scroll for every element | Scroll-jacking, animation fatigue | Animate only key content on first appearance; let secondary content be static |
|
|
61
|
-
| Loading spinners as the only loading state | No context about what's loading | Use skeleton screens, progressive content reveal, or inline loading indicators |
|
|
62
|
-
|
|
63
|
-
---
|
|
64
|
-
|
|
65
|
-
## Authentic Design Principles
|
|
66
|
-
|
|
67
|
-
### 1. Content-First Hierarchy
|
|
68
|
-
|
|
69
|
-
Design serves content, not the other way around.
|
|
70
|
-
|
|
71
|
-
- Read the actual content before choosing layout
|
|
72
|
-
- Size elements proportional to their importance
|
|
73
|
-
- Use whitespace to group related content and separate sections
|
|
74
|
-
- Ask: "What should the user see first, second, third?"
|
|
75
|
-
|
|
76
|
-
### 2. Intentional Contrast
|
|
77
|
-
|
|
78
|
-
Every visual difference should communicate something.
|
|
79
|
-
|
|
80
|
-
- **Size contrast** — Headings vs. body, featured vs. standard items
|
|
81
|
-
- **Weight contrast** — Bold labels vs. regular values, strong CTAs vs. subtle links
|
|
82
|
-
- **Color contrast** — Primary actions vs. secondary, active states vs. default
|
|
83
|
-
- **Space contrast** — Tight groups of related items, generous gaps between sections
|
|
84
|
-
- **Density contrast** — Dense data areas vs. breathing hero/summary areas
|
|
85
|
-
|
|
86
|
-
### 3. Systematic but Not Robotic
|
|
87
|
-
|
|
88
|
-
Use systems (type scale, spacing scale, color tokens) but break them intentionally.
|
|
89
|
-
|
|
90
|
-
- Establish a base grid (4px or 8px) but don't snap everything mechanically
|
|
91
|
-
- Use your type scale but occasionally break it for display/hero moments
|
|
92
|
-
- Maintain consistent component patterns but allow contextual variations
|
|
93
|
-
|
|
94
|
-
### 4. Typography Does the Heavy Lifting
|
|
95
|
-
|
|
96
|
-
Most visual hierarchy comes from type, not decoration.
|
|
97
|
-
|
|
98
|
-
- Establish a clear scale: body → subhead → section head → page title → display
|
|
99
|
-
- Use weight and size together: a 14px bold label and a 16px regular body text create clear hierarchy without needing different colors
|
|
100
|
-
- Line-height matters: tighter for headings (1.1-1.3), looser for body (1.5-1.7)
|
|
101
|
-
- Letter-spacing: slightly positive for small caps/labels, zero or negative for large display text
|
|
102
|
-
|
|
103
|
-
### 5. Color as Communication
|
|
104
|
-
|
|
105
|
-
Color should mean something, not just decorate.
|
|
106
|
-
|
|
107
|
-
- **Brand color** — Primary actions, key elements, brand identity
|
|
108
|
-
- **Semantic color** — Success (green), warning (amber), error (red), info (blue)
|
|
109
|
-
- **Neutral palette** — Background, text, borders, dividers (this is 80% of your palette)
|
|
110
|
-
- **Accent** — Sparingly, to draw attention to one thing
|
|
111
|
-
|
|
112
|
-
### 6. Space Creates Meaning
|
|
113
|
-
|
|
114
|
-
Proximity and spacing communicate relationships.
|
|
115
|
-
|
|
116
|
-
- Items close together = related
|
|
117
|
-
- Items far apart = separate concepts
|
|
118
|
-
- Consistent internal padding = belonging to the same container
|
|
119
|
-
- Asymmetric margins = visual rhythm and movement
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Design Quality Checklist
|
|
124
|
-
|
|
125
|
-
Use this to evaluate designs before presenting to the user:
|
|
126
|
-
|
|
127
|
-
### Visual Hierarchy
|
|
128
|
-
- [ ] The most important element is visually dominant
|
|
129
|
-
- [ ] There is a clear reading order (F-pattern or Z-pattern where appropriate)
|
|
130
|
-
- [ ] Headings form a logical scale (not random sizes)
|
|
131
|
-
- [ ] CTAs are visually distinct from surrounding content
|
|
132
|
-
|
|
133
|
-
### Typography
|
|
134
|
-
- [ ] Body text is 16px+ for readability
|
|
135
|
-
- [ ] Line length is 45-75 characters for body text
|
|
136
|
-
- [ ] Heading/body font pairing is intentional
|
|
137
|
-
- [ ] No more than 3 font weights in active use
|
|
138
|
-
|
|
139
|
-
### Color
|
|
140
|
-
- [ ] Palette has 2 or fewer primary/accent colors
|
|
141
|
-
- [ ] Text:background contrast meets WCAG AA (4.5:1 for body, 3:1 for large text)
|
|
142
|
-
- [ ] Color is not the only indicator of state (icons, text, or shape also signal)
|
|
143
|
-
- [ ] Neutral palette has enough range (light bg, medium borders, dark text)
|
|
144
|
-
|
|
145
|
-
### Spacing & Layout
|
|
146
|
-
- [ ] Spacing follows a consistent scale (4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px)
|
|
147
|
-
- [ ] Related elements are grouped with tighter spacing
|
|
148
|
-
- [ ] Sections have comfortable breathing room
|
|
149
|
-
- [ ] Layout works at mobile, tablet, and desktop widths
|
|
150
|
-
|
|
151
|
-
### Components
|
|
152
|
-
- [ ] Interactive elements have visible hover/focus states
|
|
153
|
-
- [ ] Buttons have clear hierarchy (primary, secondary, tertiary/ghost)
|
|
154
|
-
- [ ] Form inputs have labels, placeholders are not used as labels
|
|
155
|
-
- [ ] Empty states, loading states, and error states are designed
|
|
156
|
-
|
|
157
|
-
### Interaction
|
|
158
|
-
- [ ] Transitions are fast (150-200ms) and purposeful
|
|
159
|
-
- [ ] Hover states indicate interactivity without being distracting
|
|
160
|
-
- [ ] Focus indicators are visible for keyboard navigation
|
|
161
|
-
- [ ] Animations don't block user interaction
|
|
162
|
-
|
|
163
|
-
### Authenticity
|
|
164
|
-
- [ ] Design doesn't match common template/AI patterns listed above
|
|
165
|
-
- [ ] Layout has visual variety (not repeating the same section format)
|
|
166
|
-
- [ ] Decoration (if any) serves a purpose
|
|
167
|
-
- [ ] The design reflects the project's unique personality
|
package/src/cli/index.ts
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
import ora from 'ora';
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import { InitCommand } from '../commands/init.js';
|
|
4
|
-
import { UpdateCommand } from '../commands/update.js';
|
|
5
|
-
import { ListCommand } from '../commands/list.js';
|
|
6
|
-
import { GuideCommand } from '../commands/guide.js';
|
|
7
|
-
import { UpgradeCommand } from '../commands/upgrade.js';
|
|
8
|
-
import { ViewCommand } from '../core/view.js';
|
|
9
|
-
import { ChangeCommand } from '../commands/change.js';
|
|
10
|
-
import { registerSpecCommand } from '../commands/spec.js';
|
|
11
|
-
import { registerConfigCommand } from '../commands/config.js';
|
|
12
|
-
import { ValidateCommand } from '../commands/validate.js';
|
|
13
|
-
import { ShowCommand } from '../commands/show.js';
|
|
14
|
-
import { ArchiveCommand } from '../commands/archive.js';
|
|
15
|
-
|
|
16
|
-
const program = new Command();
|
|
17
|
-
|
|
18
|
-
program
|
|
19
|
-
.name('prompter')
|
|
20
|
-
.description('Enhance prompts directly in your AI coding workflow')
|
|
21
|
-
.version('0.8.23');
|
|
22
|
-
|
|
23
|
-
program
|
|
24
|
-
.command('init')
|
|
25
|
-
.description('Initialize Prompter in your project')
|
|
26
|
-
.option('--tools <tools...>', 'Specify AI tools to configure (antigravity, claude, codex, github-copilot, opencode, kilocode)')
|
|
27
|
-
.option('--prompts <prompts...>', 'Specify prompts to install (ai-humanizer, api-contract-generator, apply, archive, design-system, document-explainer, epic-single, epic-generator, erd-generator, fsd-generator, prd-agent-generator, prd-generator, product-brief, proposal, qa-test-scenario, skill-creator, story-single, story-generator, tdd-generator, tdd-lite-generator, wireframe-generator)')
|
|
28
|
-
.option('--skills <skills...>', 'Specify skills to install by name (e.g. laravel-code-review design-system-generator)')
|
|
29
|
-
.option('--no-interactive', 'Run without interactive prompts')
|
|
30
|
-
.action(async (options) => {
|
|
31
|
-
const initCommand = new InitCommand();
|
|
32
|
-
await initCommand.execute(options);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
program
|
|
36
|
-
.command('update')
|
|
37
|
-
.description('Update Prompter workflow files to latest version')
|
|
38
|
-
.action(async () => {
|
|
39
|
-
const updateCommand = new UpdateCommand();
|
|
40
|
-
await updateCommand.execute();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
program
|
|
44
|
-
.command('list')
|
|
45
|
-
.description('List items (changes by default). Use --specs to list specs.')
|
|
46
|
-
.option('--specs', 'List specs instead of changes')
|
|
47
|
-
.option('--changes', 'List changes explicitly (default)')
|
|
48
|
-
.option('--sort <order>', 'Sort order: "recent" (default) or "name"', 'recent')
|
|
49
|
-
.option('--json', 'Output as JSON (for programmatic use)')
|
|
50
|
-
.action(async (options?: { specs?: boolean; changes?: boolean; sort?: string; json?: boolean }) => {
|
|
51
|
-
try {
|
|
52
|
-
const listCommand = new ListCommand();
|
|
53
|
-
const mode: 'changes' | 'specs' = options?.specs ? 'specs' : 'changes';
|
|
54
|
-
const sort = options?.sort === 'name' ? 'name' : 'recent';
|
|
55
|
-
await listCommand.execute('.', mode, { sort, json: options?.json });
|
|
56
|
-
} catch (error) {
|
|
57
|
-
console.log(); // Empty line for spacing
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
program
|
|
63
|
-
.command('view')
|
|
64
|
-
.description('Display an interactive dashboard of specs and changes')
|
|
65
|
-
.action(async () => {
|
|
66
|
-
try {
|
|
67
|
-
const viewCommand = new ViewCommand();
|
|
68
|
-
await viewCommand.execute('.');
|
|
69
|
-
} catch (error) {
|
|
70
|
-
console.log(); // Empty line for spacing
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
program
|
|
76
|
-
.command('guide')
|
|
77
|
-
.description('Show setup guide for Prompter')
|
|
78
|
-
.action(async () => {
|
|
79
|
-
const guideCommand = new GuideCommand();
|
|
80
|
-
await guideCommand.execute();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
program
|
|
84
|
-
.command('upgrade')
|
|
85
|
-
.description('Upgrade Prompter to the latest version')
|
|
86
|
-
.action(async () => {
|
|
87
|
-
const upgradeCommand = new UpgradeCommand();
|
|
88
|
-
await upgradeCommand.execute();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
// Change command with subcommands
|
|
92
|
-
const changeCmd = program
|
|
93
|
-
.command('change')
|
|
94
|
-
.description('Manage Prompter change proposals');
|
|
95
|
-
|
|
96
|
-
// Deprecation notice for noun-based commands
|
|
97
|
-
changeCmd.hook('preAction', () => {
|
|
98
|
-
console.error('Warning: The "prompter change ..." commands are deprecated. Prefer verb-first commands (e.g., "prompter list", "prompter validate --changes").');
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
changeCmd
|
|
102
|
-
.command('show [change-name]')
|
|
103
|
-
.description('Show a change proposal in JSON or markdown format')
|
|
104
|
-
.option('--json', 'Output as JSON')
|
|
105
|
-
.option('--deltas-only', 'Show only deltas (JSON only)')
|
|
106
|
-
.option('--requirements-only', 'Alias for --deltas-only (deprecated)')
|
|
107
|
-
.option('--no-interactive', 'Disable interactive prompts')
|
|
108
|
-
.action(async (changeName?: string, options?: { json?: boolean; requirementsOnly?: boolean; deltasOnly?: boolean; noInteractive?: boolean }) => {
|
|
109
|
-
try {
|
|
110
|
-
const changeCommand = new ChangeCommand();
|
|
111
|
-
await changeCommand.show(changeName, options);
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error(`Error: ${(error as Error).message}`);
|
|
114
|
-
process.exitCode = 1;
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
changeCmd
|
|
119
|
-
.command('list')
|
|
120
|
-
.description('List all active changes (DEPRECATED: use "prompter list" instead)')
|
|
121
|
-
.option('--json', 'Output as JSON')
|
|
122
|
-
.option('--long', 'Show id and title with counts')
|
|
123
|
-
.action(async (options?: { json?: boolean; long?: boolean }) => {
|
|
124
|
-
try {
|
|
125
|
-
console.error('Warning: "prompter change list" is deprecated. Use "prompter list".');
|
|
126
|
-
const changeCommand = new ChangeCommand();
|
|
127
|
-
await changeCommand.list(options);
|
|
128
|
-
} catch (error) {
|
|
129
|
-
console.error(`Error: ${(error as Error).message}`);
|
|
130
|
-
process.exitCode = 1;
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
changeCmd
|
|
135
|
-
.command('validate [change-name]')
|
|
136
|
-
.description('Validate a change proposal')
|
|
137
|
-
.option('--strict', 'Enable strict validation mode')
|
|
138
|
-
.option('--json', 'Output validation report as JSON')
|
|
139
|
-
.option('--no-interactive', 'Disable interactive prompts')
|
|
140
|
-
.action(async (changeName?: string, options?: { strict?: boolean; json?: boolean; noInteractive?: boolean }) => {
|
|
141
|
-
try {
|
|
142
|
-
const changeCommand = new ChangeCommand();
|
|
143
|
-
await changeCommand.validate(changeName, options);
|
|
144
|
-
if (typeof process.exitCode === 'number' && process.exitCode !== 0) {
|
|
145
|
-
process.exit(process.exitCode);
|
|
146
|
-
}
|
|
147
|
-
} catch (error) {
|
|
148
|
-
console.error(`Error: ${(error as Error).message}`);
|
|
149
|
-
process.exitCode = 1;
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
program
|
|
154
|
-
.command('archive [change-name]')
|
|
155
|
-
.description('Archive a completed change and update main specs')
|
|
156
|
-
.option('-y, --yes', 'Skip confirmation prompts')
|
|
157
|
-
.option('--skip-specs', 'Skip spec update operations (useful for infrastructure, tooling, or doc-only changes)')
|
|
158
|
-
.option('--no-validate', 'Skip validation (not recommended, requires confirmation)')
|
|
159
|
-
.action(async (changeName?: string, options?: { yes?: boolean; skipSpecs?: boolean; noValidate?: boolean; validate?: boolean }) => {
|
|
160
|
-
try {
|
|
161
|
-
const archiveCommand = new ArchiveCommand();
|
|
162
|
-
await archiveCommand.execute(changeName, options);
|
|
163
|
-
} catch (error) {
|
|
164
|
-
console.log(); // Empty line for spacing
|
|
165
|
-
ora().fail(`Error: ${(error as Error).message}`);
|
|
166
|
-
process.exit(1);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
registerSpecCommand(program);
|
|
171
|
-
registerConfigCommand(program);
|
|
172
|
-
|
|
173
|
-
// Top-level validate command
|
|
174
|
-
program
|
|
175
|
-
.command('validate [item-name]')
|
|
176
|
-
.description('Validate changes and specs')
|
|
177
|
-
.option('--all', 'Validate all changes and specs')
|
|
178
|
-
.option('--changes', 'Validate all changes')
|
|
179
|
-
.option('--specs', 'Validate all specs')
|
|
180
|
-
.option('--type <type>', 'Specify item type when ambiguous: change|spec')
|
|
181
|
-
.option('--strict', 'Enable strict validation mode')
|
|
182
|
-
.option('--json', 'Output validation results as JSON')
|
|
183
|
-
.option('--concurrency <n>', 'Max concurrent validations (defaults to env PROMPTER_CONCURRENCY or 6)')
|
|
184
|
-
.option('--no-interactive', 'Disable interactive prompts')
|
|
185
|
-
.action(async (itemName?: string, options?: { all?: boolean; changes?: boolean; specs?: boolean; type?: string; strict?: boolean; json?: boolean; noInteractive?: boolean; concurrency?: string }) => {
|
|
186
|
-
try {
|
|
187
|
-
const validateCommand = new ValidateCommand();
|
|
188
|
-
await validateCommand.execute(itemName, options);
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.log();
|
|
191
|
-
process.exit(1);
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// Top-level show command
|
|
196
|
-
program
|
|
197
|
-
.command('show [item-name]')
|
|
198
|
-
.description('Show a change or spec')
|
|
199
|
-
.option('--json', 'Output as JSON')
|
|
200
|
-
.option('--type <type>', 'Specify item type when ambiguous: change|spec')
|
|
201
|
-
.option('--no-interactive', 'Disable interactive prompts')
|
|
202
|
-
// change-only flags
|
|
203
|
-
.option('--deltas-only', 'Show only deltas (JSON only, change)')
|
|
204
|
-
.option('--requirements-only', 'Alias for --deltas-only (deprecated, change)')
|
|
205
|
-
// spec-only flags
|
|
206
|
-
.option('--requirements', 'JSON only: Show only requirements (exclude scenarios)')
|
|
207
|
-
.option('--no-scenarios', 'JSON only: Exclude scenario content')
|
|
208
|
-
.option('-r, --requirement <id>', 'JSON only: Show specific requirement by ID (1-based)')
|
|
209
|
-
// allow unknown options to pass-through to underlying command implementation
|
|
210
|
-
.allowUnknownOption(true)
|
|
211
|
-
.action(async (itemName?: string, options?: { json?: boolean; type?: string; noInteractive?: boolean;[k: string]: any }) => {
|
|
212
|
-
try {
|
|
213
|
-
const showCommand = new ShowCommand();
|
|
214
|
-
await showCommand.execute(itemName, options ?? {});
|
|
215
|
-
} catch (error) {
|
|
216
|
-
console.log();
|
|
217
|
-
process.exit(1);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
program.parse();
|
package/src/commands/archive.ts
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { getTaskProgressForChange, formatTaskStatus } from '../utils/task-progress.js';
|
|
4
|
-
import { Validator } from '../core/validation/validator.js';
|
|
5
|
-
import chalk from 'chalk';
|
|
6
|
-
import {
|
|
7
|
-
findSpecUpdates,
|
|
8
|
-
buildUpdatedSpec,
|
|
9
|
-
writeUpdatedSpec,
|
|
10
|
-
type SpecUpdate,
|
|
11
|
-
} from '../core/specs-apply.js';
|
|
12
|
-
|
|
13
|
-
export class ArchiveCommand {
|
|
14
|
-
async execute(
|
|
15
|
-
changeName?: string,
|
|
16
|
-
options: { yes?: boolean; skipSpecs?: boolean; noValidate?: boolean; validate?: boolean } = {}
|
|
17
|
-
): Promise<void> {
|
|
18
|
-
const targetPath = '.';
|
|
19
|
-
const changesDir = path.join(targetPath, 'prompter', 'changes');
|
|
20
|
-
const archiveDir = path.join(changesDir, 'archive');
|
|
21
|
-
const mainSpecsDir = path.join(targetPath, 'prompter', 'specs');
|
|
22
|
-
|
|
23
|
-
// Check if changes directory exists
|
|
24
|
-
try {
|
|
25
|
-
await fs.access(changesDir);
|
|
26
|
-
} catch {
|
|
27
|
-
throw new Error("No Prompter changes directory found. Run 'prompter init' first.");
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Get change name interactively if not provided
|
|
31
|
-
if (!changeName) {
|
|
32
|
-
const selectedChange = await this.selectChange(changesDir);
|
|
33
|
-
if (!selectedChange) {
|
|
34
|
-
console.log('No change selected. Aborting.');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
changeName = selectedChange;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const changeDir = path.join(changesDir, changeName);
|
|
41
|
-
|
|
42
|
-
// Verify change exists
|
|
43
|
-
try {
|
|
44
|
-
const stat = await fs.stat(changeDir);
|
|
45
|
-
if (!stat.isDirectory()) {
|
|
46
|
-
throw new Error(`Change '${changeName}' not found.`);
|
|
47
|
-
}
|
|
48
|
-
} catch {
|
|
49
|
-
throw new Error(`Change '${changeName}' not found.`);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const skipValidation = options.validate === false || options.noValidate === true;
|
|
53
|
-
|
|
54
|
-
// Validate specs and change before archiving
|
|
55
|
-
if (!skipValidation) {
|
|
56
|
-
const validator = new Validator();
|
|
57
|
-
let hasValidationErrors = false;
|
|
58
|
-
|
|
59
|
-
// Validate proposal.md (non-blocking unless strict mode desired in future)
|
|
60
|
-
const changeFile = path.join(changeDir, 'proposal.md');
|
|
61
|
-
try {
|
|
62
|
-
await fs.access(changeFile);
|
|
63
|
-
const changeReport = await validator.validateChange(changeFile);
|
|
64
|
-
// Proposal validation is informative only (do not block archive)
|
|
65
|
-
if (!changeReport.valid) {
|
|
66
|
-
console.log(chalk.yellow(`\nProposal warnings in proposal.md (non-blocking):`));
|
|
67
|
-
for (const issue of changeReport.issues) {
|
|
68
|
-
const symbol = issue.level === 'ERROR' ? '⚠' : (issue.level === 'WARNING' ? '⚠' : 'ℹ');
|
|
69
|
-
console.log(chalk.yellow(` ${symbol} ${issue.message}`));
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
} catch {
|
|
73
|
-
// Change file doesn't exist, skip validation
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Validate delta-formatted spec files under the change directory if present
|
|
77
|
-
const changeSpecsDir = path.join(changeDir, 'specs');
|
|
78
|
-
let hasDeltaSpecs = false;
|
|
79
|
-
try {
|
|
80
|
-
const candidates = await fs.readdir(changeSpecsDir, { withFileTypes: true });
|
|
81
|
-
for (const c of candidates) {
|
|
82
|
-
if (c.isDirectory()) {
|
|
83
|
-
try {
|
|
84
|
-
const candidatePath = path.join(changeSpecsDir, c.name, 'spec.md');
|
|
85
|
-
await fs.access(candidatePath);
|
|
86
|
-
const content = await fs.readFile(candidatePath, 'utf-8');
|
|
87
|
-
if (/^##\s+(ADDED|MODIFIED|REMOVED|RENAMED)\s+Requirements/m.test(content)) {
|
|
88
|
-
hasDeltaSpecs = true;
|
|
89
|
-
break;
|
|
90
|
-
}
|
|
91
|
-
} catch {}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
} catch {}
|
|
95
|
-
if (hasDeltaSpecs) {
|
|
96
|
-
const deltaReport = await validator.validateChangeDeltaSpecs(changeDir);
|
|
97
|
-
if (!deltaReport.valid) {
|
|
98
|
-
hasValidationErrors = true;
|
|
99
|
-
console.log(chalk.red(`\nValidation errors in change delta specs:`));
|
|
100
|
-
for (const issue of deltaReport.issues) {
|
|
101
|
-
if (issue.level === 'ERROR') {
|
|
102
|
-
console.log(chalk.red(` ✗ ${issue.message}`));
|
|
103
|
-
} else if (issue.level === 'WARNING') {
|
|
104
|
-
console.log(chalk.yellow(` ⚠ ${issue.message}`));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (hasValidationErrors) {
|
|
111
|
-
console.log(chalk.red('\nValidation failed. Please fix the errors before archiving.'));
|
|
112
|
-
console.log(chalk.yellow('To skip validation (not recommended), use --no-validate flag.'));
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
} else {
|
|
116
|
-
// Log warning when validation is skipped
|
|
117
|
-
const timestamp = new Date().toISOString();
|
|
118
|
-
|
|
119
|
-
if (!options.yes) {
|
|
120
|
-
const { confirm } = await import('@inquirer/prompts');
|
|
121
|
-
const proceed = await confirm({
|
|
122
|
-
message: chalk.yellow('⚠️ WARNING: Skipping validation may archive invalid specs. Continue? (y/N)'),
|
|
123
|
-
default: false
|
|
124
|
-
});
|
|
125
|
-
if (!proceed) {
|
|
126
|
-
console.log('Archive cancelled.');
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
} else {
|
|
130
|
-
console.log(chalk.yellow(`\n⚠️ WARNING: Skipping validation may archive invalid specs.`));
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
console.log(chalk.yellow(`[${timestamp}] Validation skipped for change: ${changeName}`));
|
|
134
|
-
console.log(chalk.yellow(`Affected files: ${changeDir}`));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Show progress and check for incomplete tasks
|
|
138
|
-
const progress = await getTaskProgressForChange(changesDir, changeName);
|
|
139
|
-
const status = formatTaskStatus(progress);
|
|
140
|
-
console.log(`Task status: ${status}`);
|
|
141
|
-
|
|
142
|
-
const incompleteTasks = Math.max(progress.total - progress.completed, 0);
|
|
143
|
-
if (incompleteTasks > 0) {
|
|
144
|
-
if (!options.yes) {
|
|
145
|
-
const { confirm } = await import('@inquirer/prompts');
|
|
146
|
-
const proceed = await confirm({
|
|
147
|
-
message: `Warning: ${incompleteTasks} incomplete task(s) found. Continue?`,
|
|
148
|
-
default: false
|
|
149
|
-
});
|
|
150
|
-
if (!proceed) {
|
|
151
|
-
console.log('Archive cancelled.');
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
} else {
|
|
155
|
-
console.log(`Warning: ${incompleteTasks} incomplete task(s) found. Continuing due to --yes flag.`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Handle spec updates unless skipSpecs flag is set
|
|
160
|
-
if (options.skipSpecs) {
|
|
161
|
-
console.log('Skipping spec updates (--skip-specs flag provided).');
|
|
162
|
-
} else {
|
|
163
|
-
// Find specs to update
|
|
164
|
-
const specUpdates = await findSpecUpdates(changeDir, mainSpecsDir);
|
|
165
|
-
|
|
166
|
-
if (specUpdates.length > 0) {
|
|
167
|
-
console.log('\nSpecs to update:');
|
|
168
|
-
for (const update of specUpdates) {
|
|
169
|
-
const status = update.exists ? 'update' : 'create';
|
|
170
|
-
const capability = path.basename(path.dirname(update.target));
|
|
171
|
-
console.log(` ${capability}: ${status}`);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
let shouldUpdateSpecs = true;
|
|
175
|
-
if (!options.yes) {
|
|
176
|
-
const { confirm } = await import('@inquirer/prompts');
|
|
177
|
-
shouldUpdateSpecs = await confirm({
|
|
178
|
-
message: 'Proceed with spec updates?',
|
|
179
|
-
default: true
|
|
180
|
-
});
|
|
181
|
-
if (!shouldUpdateSpecs) {
|
|
182
|
-
console.log('Skipping spec updates. Proceeding with archive.');
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (shouldUpdateSpecs) {
|
|
187
|
-
// Prepare all updates first (validation pass, no writes)
|
|
188
|
-
const prepared: Array<{ update: SpecUpdate; rebuilt: string; counts: { added: number; modified: number; removed: number; renamed: number } }> = [];
|
|
189
|
-
try {
|
|
190
|
-
for (const update of specUpdates) {
|
|
191
|
-
const built = await buildUpdatedSpec(update, changeName!);
|
|
192
|
-
prepared.push({ update, rebuilt: built.rebuilt, counts: built.counts });
|
|
193
|
-
}
|
|
194
|
-
} catch (err: any) {
|
|
195
|
-
console.log(String(err.message || err));
|
|
196
|
-
console.log('Aborted. No files were changed.');
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// All validations passed; pre-validate rebuilt full spec and then write files and display counts
|
|
201
|
-
let totals = { added: 0, modified: 0, removed: 0, renamed: 0 };
|
|
202
|
-
for (const p of prepared) {
|
|
203
|
-
const specName = path.basename(path.dirname(p.update.target));
|
|
204
|
-
if (!skipValidation) {
|
|
205
|
-
const report = await new Validator().validateSpecContent(specName, p.rebuilt);
|
|
206
|
-
if (!report.valid) {
|
|
207
|
-
console.log(chalk.red(`\nValidation errors in rebuilt spec for ${specName} (will not write changes):`));
|
|
208
|
-
for (const issue of report.issues) {
|
|
209
|
-
if (issue.level === 'ERROR') console.log(chalk.red(` ✗ ${issue.message}`));
|
|
210
|
-
else if (issue.level === 'WARNING') console.log(chalk.yellow(` ⚠ ${issue.message}`));
|
|
211
|
-
}
|
|
212
|
-
console.log('Aborted. No files were changed.');
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
await writeUpdatedSpec(p.update, p.rebuilt, p.counts);
|
|
217
|
-
totals.added += p.counts.added;
|
|
218
|
-
totals.modified += p.counts.modified;
|
|
219
|
-
totals.removed += p.counts.removed;
|
|
220
|
-
totals.renamed += p.counts.renamed;
|
|
221
|
-
}
|
|
222
|
-
console.log(
|
|
223
|
-
`Totals: + ${totals.added}, ~ ${totals.modified}, - ${totals.removed}, → ${totals.renamed}`
|
|
224
|
-
);
|
|
225
|
-
console.log('Specs updated successfully.');
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Create archive directory with date prefix
|
|
231
|
-
const archiveName = `${this.getArchiveDate()}-${changeName}`;
|
|
232
|
-
const archivePath = path.join(archiveDir, archiveName);
|
|
233
|
-
|
|
234
|
-
// Check if archive already exists
|
|
235
|
-
try {
|
|
236
|
-
await fs.access(archivePath);
|
|
237
|
-
throw new Error(`Archive '${archiveName}' already exists.`);
|
|
238
|
-
} catch (error: any) {
|
|
239
|
-
if (error.code !== 'ENOENT') {
|
|
240
|
-
throw error;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Create archive directory if needed
|
|
245
|
-
await fs.mkdir(archiveDir, { recursive: true });
|
|
246
|
-
|
|
247
|
-
// Move change to archive
|
|
248
|
-
await fs.rename(changeDir, archivePath);
|
|
249
|
-
|
|
250
|
-
console.log(`Change '${changeName}' archived as '${archiveName}'.`);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
private async selectChange(changesDir: string): Promise<string | null> {
|
|
254
|
-
const { select } = await import('@inquirer/prompts');
|
|
255
|
-
// Get all directories in changes (excluding archive)
|
|
256
|
-
const entries = await fs.readdir(changesDir, { withFileTypes: true });
|
|
257
|
-
const changeDirs = entries
|
|
258
|
-
.filter(entry => entry.isDirectory() && entry.name !== 'archive')
|
|
259
|
-
.map(entry => entry.name)
|
|
260
|
-
.sort();
|
|
261
|
-
|
|
262
|
-
if (changeDirs.length === 0) {
|
|
263
|
-
console.log('No active changes found.');
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Build choices with progress inline to avoid duplicate lists
|
|
268
|
-
let choices: Array<{ name: string; value: string }> = changeDirs.map(name => ({ name, value: name }));
|
|
269
|
-
try {
|
|
270
|
-
const progressList: Array<{ id: string; status: string }> = [];
|
|
271
|
-
for (const id of changeDirs) {
|
|
272
|
-
const progress = await getTaskProgressForChange(changesDir, id);
|
|
273
|
-
const status = formatTaskStatus(progress);
|
|
274
|
-
progressList.push({ id, status });
|
|
275
|
-
}
|
|
276
|
-
const nameWidth = Math.max(...progressList.map(p => p.id.length));
|
|
277
|
-
choices = progressList.map(p => ({
|
|
278
|
-
name: `${p.id.padEnd(nameWidth)} ${p.status}`,
|
|
279
|
-
value: p.id
|
|
280
|
-
}));
|
|
281
|
-
} catch {
|
|
282
|
-
// If anything fails, fall back to simple names
|
|
283
|
-
choices = changeDirs.map(name => ({ name, value: name }));
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
try {
|
|
287
|
-
const answer = await select({
|
|
288
|
-
message: 'Select a change to archive',
|
|
289
|
-
choices
|
|
290
|
-
});
|
|
291
|
-
return answer;
|
|
292
|
-
} catch (error) {
|
|
293
|
-
// User cancelled (Ctrl+C)
|
|
294
|
-
return null;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
private getArchiveDate(): string {
|
|
299
|
-
// Returns date in YYYY-MM-DD format
|
|
300
|
-
return new Date().toISOString().split('T')[0];
|
|
301
|
-
}
|
|
302
|
-
}
|