prd-gen 0.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.
- package/README.md +52 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +827 -0
- package/package.json +27 -0
- package/prd.json +187 -0
- package/prd.md +101 -0
- package/progress.txt +118 -0
- package/scripts/ralph/CLAUDE.md +104 -0
- package/scripts/ralph/progress.txt +20 -0
- package/scripts/ralph/ralph.sh +113 -0
- package/src/index.ts +836 -0
- package/tsconfig.json +17 -0
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "prd-gen",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Single-story-at-a-time PRD review tool",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"prd-gen": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"dev": "tsx src/index.ts",
|
|
14
|
+
"typecheck": "tsc --noEmit"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"prd",
|
|
18
|
+
"cli",
|
|
19
|
+
"review"
|
|
20
|
+
],
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"typescript": "^5.3.3",
|
|
24
|
+
"@types/node": "^20.10.0",
|
|
25
|
+
"tsx": "^4.7.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/prd.json
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
{
|
|
2
|
+
"project": "prd-gen",
|
|
3
|
+
"branchName": "ralph/prd-gen-cli",
|
|
4
|
+
"description": "prd-gen CLI Tool - Single-story-at-a-time PRD review with priority assignment, editing, add/delete; changes saved to prd.json",
|
|
5
|
+
"userStories": [
|
|
6
|
+
{
|
|
7
|
+
"id": "US-001",
|
|
8
|
+
"title": "Project scaffold and npm package setup",
|
|
9
|
+
"description": "As a developer, I need a runnable npm package so that users can install and run prd-gen via npx.",
|
|
10
|
+
"acceptanceCriteria": [
|
|
11
|
+
"package.json with name 'prd-gen', bin entry pointing to cli entry point",
|
|
12
|
+
"Entry point file exists and is executable",
|
|
13
|
+
"Running `node index.js` (or equivalent) does not throw on startup",
|
|
14
|
+
"Typecheck passes"
|
|
15
|
+
],
|
|
16
|
+
"priority": 1,
|
|
17
|
+
"passes": true,
|
|
18
|
+
"notes": ""
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "US-002",
|
|
22
|
+
"title": "HTTP server that loads prd.json and serves frontend",
|
|
23
|
+
"description": "As a developer, I need a native http server that reads prd.json and serves a static HTML page so that the browser UI can display stories.",
|
|
24
|
+
"acceptanceCriteria": [
|
|
25
|
+
"Server starts on a local port using Node's native `http` module (no Express)",
|
|
26
|
+
"GET / serves an HTML page",
|
|
27
|
+
"GET /api/stories returns JSON array from ./prd.json (empty array if file missing)",
|
|
28
|
+
"POST /api/stories accepts JSON body and writes to ./prd.json",
|
|
29
|
+
"Browser is opened automatically on start using `open` package or child_process",
|
|
30
|
+
"Ctrl+C shuts down gracefully",
|
|
31
|
+
"Typecheck passes"
|
|
32
|
+
],
|
|
33
|
+
"priority": 2,
|
|
34
|
+
"passes": true,
|
|
35
|
+
"notes": ""
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "US-003",
|
|
39
|
+
"title": "Basic story card UI with title, description and story counter",
|
|
40
|
+
"description": "As a user, I want to see the current story's title and description in a centered card so that I can review it.",
|
|
41
|
+
"acceptanceCriteria": [
|
|
42
|
+
"Page shows a centered container (~800px wide) with a soft shadow on white (#FFFFFF) background",
|
|
43
|
+
"Current story title shown in ~48px bold black (#000)",
|
|
44
|
+
"Current story description shown in ~20px #333, multiline",
|
|
45
|
+
"Top-right corner shows 'Story X / Y' in grey small text",
|
|
46
|
+
"If prd.json is empty or missing, page shows 'No stories to review.' with an Add button",
|
|
47
|
+
"Typecheck passes",
|
|
48
|
+
"Verify in browser using dev-browser skill"
|
|
49
|
+
],
|
|
50
|
+
"priority": 3,
|
|
51
|
+
"passes": true,
|
|
52
|
+
"notes": ""
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "US-004",
|
|
56
|
+
"title": "Priority badge and 1–10 priority selector buttons",
|
|
57
|
+
"description": "As a user, I want to see the current priority and set a new one by clicking numbered buttons so that I can assign priority quickly.",
|
|
58
|
+
"acceptanceCriteria": [
|
|
59
|
+
"Priority badge (#FF2D55 circle) shown on the story card displaying current priority (1–10) or empty if unset",
|
|
60
|
+
"10 round buttons labelled 1–10 shown at the bottom of the card",
|
|
61
|
+
"Selected priority button is highlighted in #FF2D55; others are grey",
|
|
62
|
+
"Hovering a button turns it #FF2D55",
|
|
63
|
+
"Clicking a button sets that priority and marks the form as dirty",
|
|
64
|
+
"Typecheck passes",
|
|
65
|
+
"Verify in browser using dev-browser skill"
|
|
66
|
+
],
|
|
67
|
+
"priority": 4,
|
|
68
|
+
"passes": true,
|
|
69
|
+
"notes": ""
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "US-005",
|
|
73
|
+
"title": "Inline editing for title and description",
|
|
74
|
+
"description": "As a user, I want to click the title or description to edit them inline so that I can correct or improve story text.",
|
|
75
|
+
"acceptanceCriteria": [
|
|
76
|
+
"Clicking the title makes it an editable input/contenteditable field",
|
|
77
|
+
"Clicking the description makes it an editable textarea/contenteditable field",
|
|
78
|
+
"Editing either field marks the form as dirty",
|
|
79
|
+
"Pressing Esc while a field is focused blurs it without reverting changes",
|
|
80
|
+
"Typecheck passes",
|
|
81
|
+
"Verify in browser using dev-browser skill"
|
|
82
|
+
],
|
|
83
|
+
"priority": 5,
|
|
84
|
+
"passes": true,
|
|
85
|
+
"notes": ""
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"id": "US-006",
|
|
89
|
+
"title": "Save button and Next story navigation",
|
|
90
|
+
"description": "As a user, I want a Save button when changes are unsaved and a Next story button when clean so that I can progress through the review.",
|
|
91
|
+
"acceptanceCriteria": [
|
|
92
|
+
"When form is clean, bottom-center shows a grey 'Next story' button",
|
|
93
|
+
"When form is dirty, bottom-center shows a red (#FF2D55 with white text) 'Save' button",
|
|
94
|
+
"Clicking Save POSTs changes to /api/stories and writes prd.json; shows thin red spinner during write",
|
|
95
|
+
"After save, button returns to 'Next story'",
|
|
96
|
+
"Clicking 'Next story' advances to the next story",
|
|
97
|
+
"Typecheck passes",
|
|
98
|
+
"Verify in browser using dev-browser skill"
|
|
99
|
+
],
|
|
100
|
+
"priority": 6,
|
|
101
|
+
"passes": true,
|
|
102
|
+
"notes": ""
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"id": "US-007",
|
|
106
|
+
"title": "Fanned story stack behind current card",
|
|
107
|
+
"description": "As a user, I want to see the remaining stories stacked behind the current card so that I can get a sense of how many are left.",
|
|
108
|
+
"acceptanceCriteria": [
|
|
109
|
+
"Stories behind current card are shown as fanned cards",
|
|
110
|
+
"1–5 remaining: all cards visible in the fan",
|
|
111
|
+
"6–15 remaining: top 5 cards visible + '+N' label",
|
|
112
|
+
"16+ remaining: top 3 cards visible + '+N' label",
|
|
113
|
+
"Fan shrinks as Next is clicked (card count decreases)",
|
|
114
|
+
"Typecheck passes",
|
|
115
|
+
"Verify in browser using dev-browser skill"
|
|
116
|
+
],
|
|
117
|
+
"priority": 7,
|
|
118
|
+
"passes": true,
|
|
119
|
+
"notes": ""
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"id": "US-008",
|
|
123
|
+
"title": "Add new story button",
|
|
124
|
+
"description": "As a user, I want to add a new story before the current one so that I can capture ideas during review.",
|
|
125
|
+
"acceptanceCriteria": [
|
|
126
|
+
"A '+' icon appears at the top-left of the card on hover",
|
|
127
|
+
"Clicking '+' inserts a new blank story immediately before the current story with defaults: title 'New Item', description 'Describe here…', priority null",
|
|
128
|
+
"After save, view returns to the original story position",
|
|
129
|
+
"Typecheck passes",
|
|
130
|
+
"Verify in browser using dev-browser skill"
|
|
131
|
+
],
|
|
132
|
+
"priority": 8,
|
|
133
|
+
"passes": true,
|
|
134
|
+
"notes": ""
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"id": "US-009",
|
|
138
|
+
"title": "Delete story with confirmation",
|
|
139
|
+
"description": "As a user, I want to delete a story with a confirmation prompt so that I don't accidentally remove stories.",
|
|
140
|
+
"acceptanceCriteria": [
|
|
141
|
+
"A red trash icon appears at the top-right of the card on hover",
|
|
142
|
+
"Clicking trash shows a confirmation dialog with text 'Delete this story? No undo.'",
|
|
143
|
+
"Confirming deletes the story and saves prd.json immediately",
|
|
144
|
+
"Cancelling closes the dialog without changes",
|
|
145
|
+
"Typecheck passes",
|
|
146
|
+
"Verify in browser using dev-browser skill"
|
|
147
|
+
],
|
|
148
|
+
"priority": 9,
|
|
149
|
+
"passes": true,
|
|
150
|
+
"notes": ""
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"id": "US-010",
|
|
154
|
+
"title": "Keyboard shortcuts",
|
|
155
|
+
"description": "As a power user, I want keyboard shortcuts for all actions so that I can review stories without touching the mouse.",
|
|
156
|
+
"acceptanceCriteria": [
|
|
157
|
+
"Keys 1–9 set priority 1–9 globally (even when not editing text)",
|
|
158
|
+
"Key 0 sets priority 10 globally",
|
|
159
|
+
"Enter or Space triggers Save when dirty, or Next story when clean",
|
|
160
|
+
"Backspace or Delete opens the delete confirmation dialog",
|
|
161
|
+
"N inserts a new story (same as clicking '+')",
|
|
162
|
+
"Esc closes any open confirmation dialog, or blurs the currently focused edit field",
|
|
163
|
+
"Priority keys are ignored when a text input/textarea is focused",
|
|
164
|
+
"Typecheck passes",
|
|
165
|
+
"Verify in browser using dev-browser skill"
|
|
166
|
+
],
|
|
167
|
+
"priority": 10,
|
|
168
|
+
"passes": true,
|
|
169
|
+
"notes": ""
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"id": "US-011",
|
|
173
|
+
"title": "End-of-review summary screen",
|
|
174
|
+
"description": "As a user, I want to see a summary after reviewing all stories so that I know the session is complete.",
|
|
175
|
+
"acceptanceCriteria": [
|
|
176
|
+
"After the last story, 'Next story' button becomes a dimmed 'Finish' button",
|
|
177
|
+
"Clicking Finish shows a summary screen with: 'Good job! You've reviewed all stories.' and counts for Reviewed, Created (new), Deleted",
|
|
178
|
+
"An 'Add new story' button is still visible on the summary screen and appends to the end",
|
|
179
|
+
"Typecheck passes",
|
|
180
|
+
"Verify in browser using dev-browser skill"
|
|
181
|
+
],
|
|
182
|
+
"priority": 11,
|
|
183
|
+
"passes": true,
|
|
184
|
+
"notes": ""
|
|
185
|
+
}
|
|
186
|
+
]
|
|
187
|
+
}
|
package/prd.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
**prd-gen CLI Tool – Final Comprehensive MVP Specification**
|
|
2
|
+
|
|
3
|
+
**Name**
|
|
4
|
+
prd-gen
|
|
5
|
+
|
|
6
|
+
**Type**
|
|
7
|
+
Node.js CLI, published to npm
|
|
8
|
+
|
|
9
|
+
**Command**
|
|
10
|
+
`prd-gen` or `npx prd-gen` (run in directory containing `prd.json`)
|
|
11
|
+
|
|
12
|
+
**Purpose**
|
|
13
|
+
Single-story-at-a-time review, priority assignment, title/description editing, add/delete; changes saved to `./prd.json`
|
|
14
|
+
|
|
15
|
+
**Input file** (`prd.json`)
|
|
16
|
+
Array of objects:
|
|
17
|
+
```json
|
|
18
|
+
[
|
|
19
|
+
{
|
|
20
|
+
"id": "story-001",
|
|
21
|
+
"title": "User login flow",
|
|
22
|
+
"description": "Implement OAuth2 …",
|
|
23
|
+
"priority": 5
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Startup**
|
|
29
|
+
- Load `./prd.json`
|
|
30
|
+
- Start minimal HTTP server
|
|
31
|
+
- Open browser to localhost
|
|
32
|
+
- Empty/missing file → “No stories to review.” + Add button
|
|
33
|
+
|
|
34
|
+
**Visual style**
|
|
35
|
+
- #FFFFFF background
|
|
36
|
+
- #000 title, #333 body, #666 secondary
|
|
37
|
+
- Accent: #FF2D55 only (priority, Save, delete, hover/selected)
|
|
38
|
+
- Centered container (~800px), soft shadow
|
|
39
|
+
- Fanned story stack behind current:
|
|
40
|
+
- 1–5 → all visible
|
|
41
|
+
- 6–15 → top 5 + “+N”
|
|
42
|
+
- 16+ → top 3 + “+N”
|
|
43
|
+
- Stack shrinks on Next
|
|
44
|
+
- Top-right: “Story X / Y” (grey, small)
|
|
45
|
+
|
|
46
|
+
**Story view**
|
|
47
|
+
- Title (~48px bold, editable)
|
|
48
|
+
- Description (~20px #333, multiline editable)
|
|
49
|
+
- Priority badge (#FF2D55 circle, shows 1–10 or empty)
|
|
50
|
+
- Bottom: 10 round buttons 1–10 (grey → #FF2D55 hover/selected)
|
|
51
|
+
- Bottom center:
|
|
52
|
+
- “Next story” (grey) when clean
|
|
53
|
+
- “Save” (#FF2D55 white text) when dirty
|
|
54
|
+
- Top-left (hover): “+” → Add new story (inserts before current)
|
|
55
|
+
- Top-right (hover): red trash → confirm delete
|
|
56
|
+
|
|
57
|
+
**Blank story defaults**
|
|
58
|
+
title: "New Item"
|
|
59
|
+
description: "Describe here…"
|
|
60
|
+
priority: null
|
|
61
|
+
|
|
62
|
+
**Save**
|
|
63
|
+
Explicit “Save” click only
|
|
64
|
+
Thin red spinner during write
|
|
65
|
+
No auto-save, no undo
|
|
66
|
+
|
|
67
|
+
**Add new story**
|
|
68
|
+
Inserts immediately before current
|
|
69
|
+
After save → returns to original position
|
|
70
|
+
|
|
71
|
+
**Delete**
|
|
72
|
+
Confirm dialog “Delete this story? No undo.”
|
|
73
|
+
|
|
74
|
+
**End of review**
|
|
75
|
+
After last → dimmed “Finish”
|
|
76
|
+
Click → summary:
|
|
77
|
+
“Good job! You've reviewed all stories.
|
|
78
|
+
Reviewed: X
|
|
79
|
+
Created: Y new
|
|
80
|
+
Deleted: Z”
|
|
81
|
+
Add new story button still visible (adds to end)
|
|
82
|
+
|
|
83
|
+
**Keyboard shortcuts**
|
|
84
|
+
- 1–9 → priority 1–9
|
|
85
|
+
- 0 → priority 10
|
|
86
|
+
- Enter / Space → Next story or Save
|
|
87
|
+
- Backspace / Delete → open delete confirm
|
|
88
|
+
- N → Add new story
|
|
89
|
+
- Esc → close confirm or blur edit
|
|
90
|
+
Priority keys global; others ignored when editing text
|
|
91
|
+
|
|
92
|
+
**Tech**
|
|
93
|
+
- Native `http` module (no Express)
|
|
94
|
+
- Serve static HTML/CSS/JS + JSON API
|
|
95
|
+
- Manual body parsing
|
|
96
|
+
- `fs` write on save
|
|
97
|
+
- `open` package or `child_process` for browser
|
|
98
|
+
- Vanilla frontend (or Alpine/htmx)
|
|
99
|
+
|
|
100
|
+
**Exit**
|
|
101
|
+
Ctrl+C → graceful shutdown (save pending)
|
package/progress.txt
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
## 2026-02-27 - US-011
|
|
2
|
+
- What was implemented: End-of-review summary screen. Added `showSummary`, `createdCount`, `deletedCount` state variables. Last story renders a dimmed 'Finish' button (`.finish-btn`). Clicking Finish sets `showSummary = true` and renders a summary card showing "Good job! You've reviewed all stories." with Reviewed/Created/Deleted stat counters and an "Add new story" button. `addStoryAtEnd()` appends to end and exits summary view. `createdCount` incremented in all add functions; `deletedCount` in `deleteStory()`. Keyboard handler returns early when `showSummary` is true.
|
|
3
|
+
- Files changed: src/index.ts (CSS: `.finish-btn`, `.summary-state`, `.summary-check`, `.summary-heading`, `.summary-subheading`, `.summary-stats`, `.summary-stat-value`, `.summary-stat-label`; state: `showSummary`, `createdCount`, `deletedCount`; render(): summary branch before story rendering; actionHtml: isLastStory detection; actionBtn click: shows summary on last story; addStory/addStoryBefore: increment createdCount; new addStoryAtEnd(); deleteStory: increment deletedCount; handleKeyDown: guard against showSummary)
|
|
4
|
+
- **Learnings for future iterations:**
|
|
5
|
+
- Place the summary screen check early in render() (after empty-state, before story rendering) so it short-circuits cleanly
|
|
6
|
+
- `isLastStory` local var in render() drives both the Finish button style (`.finish-btn`) and the Finish click handler to set `showSummary = true`
|
|
7
|
+
- Summary uses same `.card-stack` + `.card` wrapper as normal view for consistent layout/centering
|
|
8
|
+
- `addStoryAtEnd()` must reset `showSummary = false` before calling `saveAndRender()` so the new story card renders instead of the summary
|
|
9
|
+
- Guard keyboard shortcuts with `|| showSummary` check so number keys don't act on a nonexistent `stories[currentIndex]` from summary view
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 2026-02-27 - US-010
|
|
13
|
+
- What was implemented: Global `keydown` event listener added once inside the IIFE (via `document.addEventListener`). `isEditingText()` helper checks `document.activeElement` tag/contenteditable to block shortcuts while editing. Shortcuts: 1–9 set priority; 0 sets priority 10; Enter/Space clicks actionBtn (save or next); Backspace/Delete opens delete confirm; N calls addStoryBefore(); Esc closes dialog or blurs active element. All shortcuts except Esc are no-ops when `isEditingText()` returns true or `saving` is true.
|
|
14
|
+
- Files changed: src/index.ts (added `isEditingText()` + `handleKeyDown()` functions and `document.addEventListener('keydown', handleKeyDown)` inside IIFE)
|
|
15
|
+
- **Learnings for future iterations:**
|
|
16
|
+
- Add global keyboard handler once with `document.addEventListener('keydown', fn)` inside the IIFE — closure gives access to all state variables without re-registering on each render
|
|
17
|
+
- `isEditingText()` checks `document.activeElement.tagName` (input/textarea) AND `getAttribute('contenteditable') === 'true'` to cover both cases
|
|
18
|
+
- Guard action shortcuts (Enter, Space, Backspace, N) with `!saving` to avoid double-firing during a fetch
|
|
19
|
+
- `e.preventDefault()` on Space/Enter prevents page scroll or form submission
|
|
20
|
+
- Esc is the only shortcut that works even when editing text — it closes the dialog first, then blurs the active field if no dialog is open
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2026-02-27 - US-009
|
|
24
|
+
- What was implemented: Red trash icon button at top-right corner of card (position: absolute; top: -14px; right: -14px; 32px circle), hidden by default, shown on `.card:hover`. Clicking shows a fixed-overlay confirmation dialog with 'Delete this story? No undo.' text and Delete/Cancel buttons. Confirming removes the story at `currentIndex`, adjusts index if at end, then saves via `saveAndRender()`. Cancelling closes dialog. `showDeleteConfirm` boolean state variable controls visibility.
|
|
25
|
+
- Files changed: src/index.ts (CSS: `.card-delete-btn`, `.card:hover .card-delete-btn`, `.card-delete-btn:hover`, `.confirm-overlay`, `.confirm-dialog`, `.confirm-dialog p`, `.confirm-actions`, `.confirm-delete-btn`, `.confirm-cancel-btn`; state: `showDeleteConfirm`; HTML: deleteStoryBtn button with SVG icon inside .card; conditional overlay HTML; JS: deleteStoryBtn/confirmDeleteBtn/confirmCancelBtn event listeners; `deleteStory()` function)
|
|
26
|
+
- **Learnings for future iterations:**
|
|
27
|
+
- Confirmation dialog is a fixed overlay (position: fixed; inset: 0; z-index: 100) — works regardless of DOM position
|
|
28
|
+
- Reset `showDeleteConfirm = false` BEFORE calling `deleteStory()` or `render()` to avoid the dialog re-appearing after re-render
|
|
29
|
+
- After splicing a story, clamp `currentIndex` to `stories.length - 1` if it's now out of bounds
|
|
30
|
+
- The trash button uses an inline SVG for the icon — style via `stroke="currentColor"` so CSS color applies
|
|
31
|
+
- Conditional overlay template: `\${showDeleteConfirm ? \`...\` : ''}` within the outer TypeScript template literal
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2026-02-27 - US-008
|
|
35
|
+
- What was implemented: '+' icon button at the top-left corner of the card (absolute top:-14px left:-14px, 32px circle), hidden by default, shown on `.card:hover`. Clicking it inserts a new blank story (title 'New Item', description 'Describe here…', priority null) immediately before the current story, then increments `currentIndex` so the view returns to the original story position after save. Empty-state `addStory()` left unchanged; new `addStoryBefore()` function handles card-level insertion.
|
|
36
|
+
- Files changed: src/index.ts (CSS: `.card-add-btn`, `.card:hover .card-add-btn`, `.card-add-btn:hover`; HTML: button#addStoryBtn inside .card; JS: addStoryBtn event listener in render(); addStoryBefore() function)
|
|
37
|
+
- **Learnings for future iterations:**
|
|
38
|
+
- To show an element only on parent hover: set `opacity: 0` on child and `opacity: 1` on `.parent:hover .child`
|
|
39
|
+
- Place the '+' button at `position: absolute; top: -14px; left: -14px; z-index: 10` so it overlaps the card corner without conflicting with the priority badge (top: 20px; left: 24px)
|
|
40
|
+
- Keep `addStory()` (empty state) and `addStoryBefore()` (card button) separate — the empty-state case must keep currentIndex at 0, while the card case increments currentIndex to return to original position
|
|
41
|
+
- `currentIndex++` must happen BEFORE `saveAndRender()` so the post-save render() shows the original story
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 2026-02-27 - US-007
|
|
45
|
+
- What was implemented: Fanned ghost card stack behind the current card. Remaining stories after current index are counted; 1–5 shows all as ghost cards, 6–15 shows 5 + overflow badge, 16+ shows 3 + overflow badge. Ghost cards rendered as absolutely-positioned divs inside a `.card-stack` wrapper with rotation transforms (transform-origin: center 90%) — furthest ghost is most rotated. Overflow badge shows "+N" on the furthest ghost card.
|
|
46
|
+
- Files changed: src/index.ts (CSS: `.card-stack`, `.card-ghost`, `.stack-overflow-badge`; removed `max-width` from `.card` and moved to `.card-stack`; render() builds ghostHtml loop and wraps main card in `.card-stack`)
|
|
47
|
+
- **Learnings for future iterations:**
|
|
48
|
+
- Ghost cards use `position: absolute; top:0; left:0; right:0; bottom:0` within `.card-stack` — they size automatically to match the main card
|
|
49
|
+
- `transform-origin: center 90%` makes cards rotate around a point near the bottom, causing upper portions to fan out from behind the main card
|
|
50
|
+
- Ghost cards rendered first in DOM (lower z-index by natural stacking) so main card (rendered last, no explicit z-index needed) appears on top
|
|
51
|
+
- The `rotationSets` object maps `visibleGhosts` count → array of per-card rotation degrees (largest first = furthest ghost)
|
|
52
|
+
- Moving `max-width: 800px` from `.card` to `.card-stack` ensures ghost cards use the same width constraint
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 2026-02-27 - US-006
|
|
56
|
+
- What was implemented: Save button (red #FF2D55) shown when dirty; grey 'Next story' button when clean. Clicking Save POSTs stories to /api/stories, shows a thin red spinner during write, then resets dirty and re-renders. Clicking Next story advances currentIndex by 1 (clamped at end). saveAndRender() also resets dirty.
|
|
57
|
+
- Files changed: src/index.ts (CSS for action-area/save-btn/next-btn/spinner-ring/@keyframes spin; `saving` state variable; actionHtml in render(); action button event listeners; saveAndRender updated)
|
|
58
|
+
- **Learnings for future iterations:**
|
|
59
|
+
- `saving` state variable added alongside `dirty` — set true before POST, false in .then/.catch
|
|
60
|
+
- Show spinner by rendering a `.spinner-ring` span in place of the button (CSS border-top animation)
|
|
61
|
+
- Action button is either save-btn or next-btn depending on `dirty`; attach appropriate handler after innerHTML is set
|
|
62
|
+
- saveAndRender() (used by addStory) should also reset dirty=false after successful save
|
|
63
|
+
- When dirty=false and Next is clicked, currentIndex advances; no need to reset dirty since it's already false
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 2026-02-27 - US-005
|
|
67
|
+
- What was implemented: Inline editing for title and description using `contenteditable="true"` on both divs. Title uses `textContent` for updates; description uses `innerText` to preserve newlines. Input events update `stories[currentIndex]` and set `dirty = true`. Esc key blurs the focused field. CSS adds cursor:text, hover/focus background highlights and a red outline on focus.
|
|
68
|
+
- Files changed: src/index.ts (CSS additions + contenteditable attrs + event listeners in render())
|
|
69
|
+
- **Learnings for future iterations:**
|
|
70
|
+
- Use `contenteditable="true"` on existing divs — no swap to input/textarea needed
|
|
71
|
+
- Use `element.textContent` for single-line fields (title); use `element.innerText` for multiline (description) to preserve newlines
|
|
72
|
+
- Add `spellcheck="false"` on title field to avoid red squiggles under story titles
|
|
73
|
+
- `input` event fires on every keystroke for contenteditable; `keydown` for Esc handling
|
|
74
|
+
- `escapeHtml()` still used for initial innerHTML render; safe because browser interprets entities as plain text in contenteditable
|
|
75
|
+
- When render() is called (e.g. after priority change), contenteditable fields reset from stories array — dirty text changes are safe because `input` event syncs to stories before any click event fires
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Codebase Patterns
|
|
79
|
+
- ESM project: `"type": "module"` in package.json — use `import`/`export`, not `require`/`module.exports`
|
|
80
|
+
- TypeScript with NodeNext module resolution — file extensions matter for imports
|
|
81
|
+
- Use `fileURLToPath(import.meta.url)` + `path.dirname` to get `__dirname` equivalent in ESM
|
|
82
|
+
- PRD data lives in `prd.json` at `process.cwd()` with shape `{ userStories: [...] }`
|
|
83
|
+
- No external runtime dependencies (no Express, no open package) — use Node native modules
|
|
84
|
+
|
|
85
|
+
- HTML UI is embedded as a template literal in `HTML_PAGE` const in src/index.ts — all frontend code lives inline there
|
|
86
|
+
- Use `escapeHtml()` in JS to prevent XSS when rendering story content into innerHTML
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 2026-02-27 - US-002
|
|
91
|
+
- What was implemented: Native HTTP server using Node's `http` module. Serves GET / (HTML page), GET /api/stories (reads prd.json), POST /api/stories (writes prd.json). Opens browser via child_process exec on start. Handles SIGINT for graceful shutdown.
|
|
92
|
+
- Files changed: src/index.ts (full rewrite)
|
|
93
|
+
- **Learnings for future iterations:**
|
|
94
|
+
- prd.json shape: `{ userStories: [...] }` — read/write via `data.userStories`, not root array
|
|
95
|
+
- Browser open: platform-specific commands via `process.platform` (darwin=open, win32=start, linux=xdg-open)
|
|
96
|
+
- POST body: collect chunks with `req.on('data')`, parse in `req.on('end')`
|
|
97
|
+
- TypeScript: type `chunk` as `Buffer` in data event to satisfy strict mode
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 2026-02-27 - US-003
|
|
101
|
+
- What was implemented: Full story card UI replacing the placeholder HTML. Centered 800px card with soft shadow, 48px bold title, 20px description, Story X/Y counter top-right. Empty state shows "No stories to review." with Add button.
|
|
102
|
+
- Files changed: src/index.ts (HTML_PAGE replaced with full SPA)
|
|
103
|
+
- **Learnings for future iterations:**
|
|
104
|
+
- All frontend code is inline in the `HTML_PAGE` template literal — use template literal escaping carefully (backtick → \\\`, dollar+brace → no conflict if inside JS string literals)
|
|
105
|
+
- The JS in HTML uses IIFE pattern and fetches `/api/stories` on load; `stories` array and `currentIndex` are module-level state
|
|
106
|
+
- `escapeHtml()` helper is defined in JS to prevent XSS
|
|
107
|
+
- The `addStory()` function inserts at `currentIndex` position and calls `saveAndRender()`
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 2026-02-27 - US-004
|
|
111
|
+
- What was implemented: Priority badge (#FF2D55 circle) on card top-left showing current priority or empty grey circle if unset. 10 round priority buttons (1–10) at card bottom; selected button highlighted in #FF2D55, hover also turns #FF2D55. Clicking a button updates `stories[currentIndex].priority` and sets `dirty = true`, then re-renders.
|
|
112
|
+
- Files changed: src/index.ts (CSS additions + render function update + dirty state variable)
|
|
113
|
+
- **Learnings for future iterations:**
|
|
114
|
+
- `dirty` state variable added at IIFE module level alongside `stories` and `currentIndex`
|
|
115
|
+
- After setting innerHTML, re-attach event listeners by querying the newly-created DOM elements
|
|
116
|
+
- Inner JS template literals inside `HTML_PAGE` TypeScript template literal: escape backticks as `\`` and template expressions as `\${}`
|
|
117
|
+
- Priority buttons use `data-priority` attribute; read with `btn.getAttribute('data-priority')`
|
|
118
|
+
---
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Ralph Agent Instructions
|
|
2
|
+
|
|
3
|
+
You are an autonomous coding agent working on a software project.
|
|
4
|
+
|
|
5
|
+
## Your Task
|
|
6
|
+
|
|
7
|
+
1. Read the PRD at `prd.json` (in the same directory as this file)
|
|
8
|
+
2. Read the progress log at `progress.txt` (check Codebase Patterns section first)
|
|
9
|
+
3. Check you're on the correct branch from PRD `branchName`. If not, check it out or create from main.
|
|
10
|
+
4. Pick the **highest priority** user story where `passes: false`
|
|
11
|
+
5. Implement that single user story
|
|
12
|
+
6. Run quality checks (e.g., typecheck, lint, test - use whatever your project requires)
|
|
13
|
+
7. Update CLAUDE.md files if you discover reusable patterns (see below)
|
|
14
|
+
8. If checks pass, commit ALL changes with message: `feat: [Story ID] - [Story Title]`
|
|
15
|
+
9. Update the PRD to set `passes: true` for the completed story
|
|
16
|
+
10. Append your progress to `progress.txt`
|
|
17
|
+
|
|
18
|
+
## Progress Report Format
|
|
19
|
+
|
|
20
|
+
APPEND to progress.txt (never replace, always append):
|
|
21
|
+
```
|
|
22
|
+
## [Date/Time] - [Story ID]
|
|
23
|
+
- What was implemented
|
|
24
|
+
- Files changed
|
|
25
|
+
- **Learnings for future iterations:**
|
|
26
|
+
- Patterns discovered (e.g., "this codebase uses X for Y")
|
|
27
|
+
- Gotchas encountered (e.g., "don't forget to update Z when changing W")
|
|
28
|
+
- Useful context (e.g., "the evaluation panel is in component X")
|
|
29
|
+
---
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The learnings section is critical - it helps future iterations avoid repeating mistakes and understand the codebase better.
|
|
33
|
+
|
|
34
|
+
## Consolidate Patterns
|
|
35
|
+
|
|
36
|
+
If you discover a **reusable pattern** that future iterations should know, add it to the `## Codebase Patterns` section at the TOP of progress.txt (create it if it doesn't exist). This section should consolidate the most important learnings:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
## Codebase Patterns
|
|
40
|
+
- Example: Use `sql<number>` template for aggregations
|
|
41
|
+
- Example: Always use `IF NOT EXISTS` for migrations
|
|
42
|
+
- Example: Export types from actions.ts for UI components
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Only add patterns that are **general and reusable**, not story-specific details.
|
|
46
|
+
|
|
47
|
+
## Update CLAUDE.md Files
|
|
48
|
+
|
|
49
|
+
Before committing, check if any edited files have learnings worth preserving in nearby CLAUDE.md files:
|
|
50
|
+
|
|
51
|
+
1. **Identify directories with edited files** - Look at which directories you modified
|
|
52
|
+
2. **Check for existing CLAUDE.md** - Look for CLAUDE.md in those directories or parent directories
|
|
53
|
+
3. **Add valuable learnings** - If you discovered something future developers/agents should know:
|
|
54
|
+
- API patterns or conventions specific to that module
|
|
55
|
+
- Gotchas or non-obvious requirements
|
|
56
|
+
- Dependencies between files
|
|
57
|
+
- Testing approaches for that area
|
|
58
|
+
- Configuration or environment requirements
|
|
59
|
+
|
|
60
|
+
**Examples of good CLAUDE.md additions:**
|
|
61
|
+
- "When modifying X, also update Y to keep them in sync"
|
|
62
|
+
- "This module uses pattern Z for all API calls"
|
|
63
|
+
- "Tests require the dev server running on PORT 3000"
|
|
64
|
+
- "Field names must match the template exactly"
|
|
65
|
+
|
|
66
|
+
**Do NOT add:**
|
|
67
|
+
- Story-specific implementation details
|
|
68
|
+
- Temporary debugging notes
|
|
69
|
+
- Information already in progress.txt
|
|
70
|
+
|
|
71
|
+
Only update CLAUDE.md if you have **genuinely reusable knowledge** that would help future work in that directory.
|
|
72
|
+
|
|
73
|
+
## Quality Requirements
|
|
74
|
+
|
|
75
|
+
- ALL commits must pass your project's quality checks (typecheck, lint, test)
|
|
76
|
+
- Do NOT commit broken code
|
|
77
|
+
- Keep changes focused and minimal
|
|
78
|
+
- Follow existing code patterns
|
|
79
|
+
|
|
80
|
+
## Browser Testing (If Available)
|
|
81
|
+
|
|
82
|
+
For any story that changes UI, verify it works in the browser if you have browser testing tools configured (e.g., via MCP):
|
|
83
|
+
|
|
84
|
+
1. Navigate to the relevant page
|
|
85
|
+
2. Verify the UI changes work as expected
|
|
86
|
+
3. Take a screenshot if helpful for the progress log
|
|
87
|
+
|
|
88
|
+
If no browser tools are available, note in your progress report that manual browser verification is needed.
|
|
89
|
+
|
|
90
|
+
## Stop Condition
|
|
91
|
+
|
|
92
|
+
After completing a user story, check if ALL stories have `passes: true`.
|
|
93
|
+
|
|
94
|
+
If ALL stories are complete and passing, reply with:
|
|
95
|
+
<promise>COMPLETE</promise>
|
|
96
|
+
|
|
97
|
+
If there are still stories with `passes: false`, end your response normally (another iteration will pick up the next story).
|
|
98
|
+
|
|
99
|
+
## Important
|
|
100
|
+
|
|
101
|
+
- Work on ONE story per iteration
|
|
102
|
+
- Commit frequently
|
|
103
|
+
- Keep CI green
|
|
104
|
+
- Read the Codebase Patterns section in progress.txt before starting
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
## Codebase Patterns
|
|
2
|
+
- Project is a Node.js/TypeScript CLI tool using ESM modules (`"type": "module"`)
|
|
3
|
+
- Build: `npm run build` (tsc), Typecheck: `npm run typecheck`, Dev: `npx tsx src/index.ts`
|
|
4
|
+
- Source files in `src/`, compiled output to `dist/`
|
|
5
|
+
- tsconfig uses `"module": "NodeNext"`, `"moduleResolution": "NodeNext"`
|
|
6
|
+
|
|
7
|
+
# Ralph Progress Log
|
|
8
|
+
Started: Fri Feb 27 21:15:29 CST 2026
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 2026-02-27 - US-001
|
|
12
|
+
- Implemented project scaffold: package.json with prd-gen bin entry, tsconfig.json, src/index.ts entry point
|
|
13
|
+
- Initialized git repo and created branch ralph/prd-gen-cli
|
|
14
|
+
- Files changed: package.json, tsconfig.json, src/index.ts, .gitignore, package-lock.json
|
|
15
|
+
- **Learnings for future iterations:**
|
|
16
|
+
- Project uses ESM (`"type": "module"`), so imports need `.js` extension in TypeScript source
|
|
17
|
+
- TypeScript module setting is `NodeNext` which requires explicit file extensions
|
|
18
|
+
- Dev runner is `tsx` (in devDependencies), use `npx tsx src/index.ts` for running during dev
|
|
19
|
+
- Typecheck with `npm run typecheck` (tsc --noEmit)
|
|
20
|
+
---
|