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/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
+ ---