@rayburst/cc 2.0.0 → 3.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.
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "rayburst",
3
- "version": "2.0.0",
4
- "description": "AI-native product management for Claude Code. Track features with acceptance criteria, validate with Playwright, and manage board cards through the full dev cycle.",
3
+ "version": "3.0.0",
4
+ "description": "Always-on product context for Claude Code. Automatically injects feature atlas and acceptance criteria into every coding session no slash commands needed.",
5
5
  "author": {
6
6
  "name": "Rayburst",
7
7
  "email": "hello@rayburst.app"
8
8
  },
9
9
  "homepage": "https://rayburst.app",
10
- "repository": "https://github.com/rayburst/rayburst-cc",
10
+ "repository": "https://github.com/Dubwoof/rayburst-cc",
11
11
  "license": "MIT",
12
12
  "keywords": [
13
13
  "product-management",
package/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # Rayburst
2
+
3
+ **Always-on product context for Claude Code.**
4
+
5
+ [![npm](https://img.shields.io/npm/v/@rayburst/cc?color=blue)](https://www.npmjs.com/package/@rayburst/cc) [![GitHub stars](https://img.shields.io/github/stars/Dubwoof/rayburst-cc?style=flat&color=yellow)](https://github.com/Dubwoof/rayburst-cc/stargazers) [![Last commit](https://img.shields.io/github/last-commit/Dubwoof/rayburst-cc?color=green)](https://github.com/Dubwoof/rayburst-cc/commits) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ ## The Problem
8
+
9
+ You're building a feature. Claude Code writes the code, but has no idea what the feature is supposed to do. It doesn't know the acceptance criteria. It doesn't know which board card it's working on. It doesn't know what QA expects. Every conversation starts from zero — you re-explain the requirements, the edge cases, the definition of done.
10
+
11
+ Rayburst solves this by giving Claude Code an **always-on product context layer**:
12
+
13
+ 1. **Session Context** — On every session start, Claude gets injected with your entire feature atlas: features, acceptance criteria, board cards, and their statuses. No commands needed.
14
+ 2. **Task Matching** — When you describe a task, Rayburst automatically matches it to existing features and injects the relevant Gherkin acceptance criteria. Claude knows what "done" looks like before writing a single line.
15
+ 3. **Coding Awareness** — When Claude writes or edits files, it gets reminded of the active criteria and warned if the file relates to other features that might be affected.
16
+ 4. **Atlas Protection** — Claude never creates, updates, or deletes features/criteria without asking you first.
17
+
18
+ **No slash commands. No manual lookups. Just code with product context.**
19
+
20
+ ## Install
21
+
22
+ **Prerequisites:** Claude Code v1.0.33+ and a [Rayburst](https://rayburst.app) account.
23
+
24
+ ```bash
25
+ claude plugin add Dubwoof/rayburst-cc
26
+ ```
27
+
28
+ Restart Claude Code, then run the one-time setup:
29
+
30
+ ```
31
+ /rb:setup
32
+ ```
33
+
34
+ This will prompt you for:
35
+ - Your **Rayburst API key** (get one at [rayburst.app/settings/api-keys](https://www.rayburst.app/settings/api-keys))
36
+ - Your **project URL** (e.g. `https://www.myapp.com` or `http://localhost:3000`)
37
+ - **Board selection** — pick from your org's boards
38
+ - **Project IDs** — frontend and backend project UUIDs
39
+ - **User credentials** — for browser-based validation
40
+
41
+ Config is saved to `.claude/rb-config.md` (add to `.gitignore` — contains your API key).
42
+
43
+ **That's it.** From now on, every Claude Code session in this project automatically has product context.
44
+
45
+ ## How It Works
46
+
47
+ Rayburst runs as a Claude Code plugin with **3 hooks** and an **MCP server** — all automatic, no commands needed.
48
+
49
+ ### Hook 1: SessionStart — Product Context Injection
50
+
51
+ On every session, Rayburst fetches your feature atlas and board state, then injects it as XML context that Claude follows automatically:
52
+
53
+ ```xml
54
+ <rayburst_product_context>
55
+ <rules>
56
+ <rule>Before implementing any task, check if a matching feature exists in the atlas.</rule>
57
+ <rule>If changes may affect OTHER features, flag this to the user.</rule>
58
+ <rule>NEVER modify the atlas without asking the user first.</rule>
59
+ <rule>When done, summarize which criteria were addressed.</rule>
60
+ </rules>
61
+
62
+ <atlas summary="45 features (12 draft, 28 active, 5 completed)">
63
+ <feature id="abc123" status="active" criteria="5">Dark Mode Toggle</feature>
64
+ <feature id="def456" status="draft" criteria="3">Monthly Billing</feature>
65
+ ...
66
+ </atlas>
67
+
68
+ <board summary="12 cards — 3 in-progress, 2 validation, 5 done">
69
+ <card id="card1" status="in-progress" features="abc123">Implement dark mode</card>
70
+ ...
71
+ </board>
72
+ </rayburst_product_context>
73
+ ```
74
+
75
+ Claude starts every conversation knowing every feature, its status, and the board state.
76
+
77
+ ### Hook 2: UserPromptSubmit — Automatic Feature Matching
78
+
79
+ When you type a message, Rayburst matches your words against feature titles and descriptions. If a match is found, it loads the full acceptance criteria and injects them:
80
+
81
+ ```xml
82
+ <rayburst_active_feature>
83
+ <feature id="abc123" title="Dark Mode Toggle" status="active">
84
+ <criterion id="c1" status="pending">
85
+ Given the user is on the settings page
86
+ When they toggle dark mode
87
+ Then all UI elements switch to dark theme
88
+ </criterion>
89
+ <criterion id="c2" status="pending">
90
+ Given dark mode is active
91
+ When the user refreshes the page
92
+ Then dark mode persists
93
+ </criterion>
94
+ </feature>
95
+ <guidance>Work against these acceptance criteria.</guidance>
96
+ </rayburst_active_feature>
97
+ ```
98
+
99
+ Claude immediately knows what it's building and what the acceptance criteria are — without you pasting requirements.
100
+
101
+ ### Hook 3: PreToolUse — Coding Reminders
102
+
103
+ When Claude writes or edits a file, Rayburst injects a reminder of the active criteria and flags if the file relates to other features:
104
+
105
+ ```xml
106
+ <rayburst_coding_reminder>
107
+ <active_feature>Dark Mode Toggle (abc123)</active_feature>
108
+ <criteria_checklist>
109
+ - [ ] Dark theme toggle on settings page
110
+ - [ ] Theme persists on refresh
111
+ - [ ] System theme detection fallback
112
+ </criteria_checklist>
113
+ <file>src/theme.ts</file>
114
+ <related_features>This file may also relate to: "Accessibility High Contrast".</related_features>
115
+ </rayburst_coding_reminder>
116
+ ```
117
+
118
+ ### MCP Server (25 tools)
119
+
120
+ Claude can call Rayburst tools directly when it needs to interact with the atlas:
121
+
122
+ | Domain | Tools |
123
+ |--------|-------|
124
+ | **Features** | `rb_list_features`, `rb_get_feature`, `rb_create_feature`, `rb_update_feature`, `rb_delete_feature` |
125
+ | **Criteria** | `rb_add_criterion`, `rb_update_criterion`, `rb_delete_criterion` |
126
+ | **Board** | `rb_list_boards`, `rb_list_cards`, `rb_get_card`, `rb_create_card`, `rb_update_card`, `rb_move_card` |
127
+ | **Card Extras** | `rb_list_card_features`, `rb_list_card_todos`, `rb_list_card_roles`, `rb_add_comment` |
128
+ | **Links** | `rb_link_feature_to_feature`, `rb_list_feature_links` |
129
+ | **Validation** | `rb_submit_validation` |
130
+ | **Tags** | `rb_list_tags`, `rb_create_tag` |
131
+ | **Health** | `rb_ping` |
132
+
133
+ Claude decides when to call these based on the injected rules — you never need to.
134
+
135
+ ## Example Session
136
+
137
+ ```
138
+ You: Help me implement the dark mode toggle
139
+
140
+ [SessionStart already injected atlas with 45 features]
141
+ [UserPromptSubmit matches "Dark Mode Toggle", injects 5 criteria]
142
+
143
+ Claude: I see "Dark Mode Toggle" in your feature atlas with 5 acceptance criteria:
144
+ 1. Settings page toggle switches to dark theme
145
+ 2. Theme persists on refresh
146
+ 3. System theme detection as default
147
+ 4. Dark mode applies to all components
148
+ 5. Toggle state syncs across tabs
149
+
150
+ Let me implement against these criteria.
151
+
152
+ [Claude edits src/theme.ts]
153
+ [PreToolUse injects criteria checklist + "also relates to Accessibility High Contrast"]
154
+
155
+ Claude: I've updated src/theme.ts to handle theme persistence (criterion #2).
156
+ Note: this file is also used by the "Accessibility High Contrast" feature —
157
+ my changes don't affect contrast ratio logic.
158
+
159
+ [Claude finishes implementation]
160
+
161
+ Claude: Implementation complete. Criteria addressed:
162
+ - [x] Settings page toggle (#1)
163
+ - [x] Theme persists on refresh (#2)
164
+ - [x] System theme detection (#3)
165
+ - [x] All components themed (#4)
166
+ - [ ] Cross-tab sync (#5) — requires SharedWorker, skipped for now
167
+
168
+ Would you like me to update the criteria statuses in the atlas?
169
+ ```
170
+
171
+ ## The Feature Atlas
172
+
173
+ The core data model is a **feature** with **acceptance criteria** in Gherkin format:
174
+
175
+ ```
176
+ Feature: "Card Label Management"
177
+ Status: active
178
+
179
+ Criteria:
180
+ 1. Given a card exists on the board
181
+ When the user clicks "Add Label"
182
+ Then a label picker appears with available colors
183
+
184
+ 2. Given a label is assigned to a card
185
+ When the user views the board
186
+ Then the label badge is visible on the card preview
187
+ ```
188
+
189
+ Features link to **board cards**, **projects**, **tags**, and **other features** (dependency graph with `depends_on`, `implements`, `extends`, `related_to` links).
190
+
191
+ ## Configuration
192
+
193
+ | Source | Priority | What |
194
+ |--------|----------|------|
195
+ | Environment variables | High | `RAYBURST_API_KEY`, `RAYBURST_API_URL`, `RAYBURST_AGENT_ID` |
196
+ | `.claude/rb-config.md` | Normal | API key, project URL, board ID, project IDs, user credentials |
197
+
198
+ Environment variables override config file values. Most users just run `/rb:setup` once.
199
+
200
+ ## API Key Security
201
+
202
+ - API keys are **scoped to one user + one organization** — cannot access other orgs
203
+ - Stored in `.claude/rb-config.md` — add to `.gitignore`
204
+ - Three types: `rb_user_` (humans), `rb_agent_` (AI agents), `rb_global_` (org admins)
205
+ - Create at [rayburst.app/settings/api-keys](https://www.rayburst.app/settings/api-keys)
206
+
207
+ ## Uninstall
208
+
209
+ ```bash
210
+ claude plugin remove rayburst
211
+ rm .claude/rb-config.md
212
+ ```
213
+
214
+ ## Contributing
215
+
216
+ ```bash
217
+ git clone https://github.com/Dubwoof/rayburst-cc.git
218
+ cd rayburst-cc && npm install && npm run build
219
+ ```
220
+
221
+ MCP server in `src/`, hooks in `hooks/`, setup skill in `skills/setup/`.
222
+
223
+ ```bash
224
+ claude plugin add /path/to/rayburst-cc
225
+ ```
226
+
227
+ ## License
228
+
229
+ MIT
package/hooks/hooks.json CHANGED
@@ -10,6 +10,37 @@
10
10
  }
11
11
  ]
12
12
  }
13
+ ],
14
+ "UserPromptSubmit": [
15
+ {
16
+ "matcher": "",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/userpromptsubmit.mjs"
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "PreToolUse": [
26
+ {
27
+ "matcher": "Write",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/pretooluse.mjs"
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ "matcher": "Edit",
37
+ "hooks": [
38
+ {
39
+ "type": "command",
40
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/pretooluse.mjs"
41
+ }
42
+ ]
43
+ }
13
44
  ]
14
45
  }
15
46
  }
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * PreToolUse hook for the Rayburst plugin.
5
+ *
6
+ * Triggered on Write and Edit tool calls. Reads the active feature
7
+ * from cache and injects a coding reminder with criteria checklist.
8
+ */
9
+
10
+ import { readCache } from "./rb-cache.mjs";
11
+ import { buildCodingReminderBlock } from "./product-context-block.mjs";
12
+
13
+ // Read stdin for the hook input
14
+ let input = "";
15
+ try {
16
+ const chunks = [];
17
+ for await (const chunk of process.stdin) {
18
+ chunks.push(chunk);
19
+ }
20
+ input = Buffer.concat(chunks).toString("utf-8");
21
+ } catch {
22
+ process.exit(0);
23
+ }
24
+
25
+ let hookInput;
26
+ try {
27
+ hookInput = JSON.parse(input);
28
+ } catch {
29
+ process.exit(0);
30
+ }
31
+
32
+ const toolName = hookInput?.input?.tool_name;
33
+ const toolInput = hookInput?.input?.tool_input;
34
+
35
+ // Only act on Write and Edit
36
+ if (toolName !== "Write" && toolName !== "Edit") {
37
+ process.exit(0);
38
+ }
39
+
40
+ // Read active feature from cache
41
+ const activeFeature = readCache("active-feature");
42
+ if (!activeFeature) {
43
+ process.exit(0);
44
+ }
45
+
46
+ // Get the file path being edited
47
+ const filePath = toolInput?.file_path || toolInput?.path || "";
48
+
49
+ // Check if other features mention this file path (basic matching)
50
+ const featureList = readCache("features") || [];
51
+ const relatedFeatures = [];
52
+
53
+ if (filePath) {
54
+ const fileBasename = filePath.split("/").pop() || "";
55
+ const fileDir = filePath.split("/").slice(-2, -1)[0] || "";
56
+
57
+ for (const f of featureList) {
58
+ if (f.id === activeFeature.id) continue;
59
+ const desc = (f.description || "").toLowerCase();
60
+ const title = (f.title || "").toLowerCase();
61
+ if (
62
+ desc.includes(fileBasename.toLowerCase()) ||
63
+ desc.includes(fileDir.toLowerCase()) ||
64
+ title.includes(fileBasename.toLowerCase().replace(/\.\w+$/, ""))
65
+ ) {
66
+ relatedFeatures.push(f);
67
+ }
68
+ }
69
+ }
70
+
71
+ // Build and inject the Coding Reminder Block
72
+ const contextBlock = buildCodingReminderBlock(
73
+ activeFeature,
74
+ filePath,
75
+ relatedFeatures.slice(0, 3)
76
+ );
77
+
78
+ if (contextBlock) {
79
+ console.log(
80
+ JSON.stringify({
81
+ hookSpecificOutput: {
82
+ hookEventName: "PreToolUse",
83
+ additionalContext: contextBlock,
84
+ },
85
+ })
86
+ );
87
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * XML block builders for Rayburst hook context injection.
3
+ */
4
+
5
+ /**
6
+ * Build the main Product Context Block injected at session start.
7
+ * Contains rules, feature atlas summary, and board summary.
8
+ */
9
+ export function buildProductContextBlock(features, cards) {
10
+ const featureList = Array.isArray(features) ? features : features?.data ?? [];
11
+ const cardList = Array.isArray(cards) ? cards : cards?.data ?? [];
12
+
13
+ // Count features by status
14
+ const counts = { draft: 0, active: 0, completed: 0, archived: 0 };
15
+ for (const f of featureList) {
16
+ counts[f.status || "draft"] = (counts[f.status || "draft"] || 0) + 1;
17
+ }
18
+ const total = featureList.length;
19
+
20
+ // Count cards by status
21
+ const cardCounts = {};
22
+ for (const c of cardList) {
23
+ cardCounts[c.status || "draft"] = (cardCounts[c.status || "draft"] || 0) + 1;
24
+ }
25
+
26
+ // Build feature lines (compact: one line each)
27
+ const featureLines = featureList
28
+ .slice(0, 60)
29
+ .map((f) => {
30
+ const critCount = f.criteriaCount ?? f.criteria?.length ?? "?";
31
+ return ` <feature id="${f.id}" status="${f.status}" criteria="${critCount}">${escapeXml(f.title)}</feature>`;
32
+ })
33
+ .join("\n");
34
+
35
+ const truncated =
36
+ featureList.length > 60
37
+ ? `\n <truncated shown="60" total="${featureList.length}"/>`
38
+ : "";
39
+
40
+ // Build card lines
41
+ const activeCards = cardList.filter(
42
+ (c) => c.status === "in-progress" || c.status === "validation" || c.status === "ready"
43
+ );
44
+ const cardLines = activeCards
45
+ .slice(0, 20)
46
+ .map((c) => {
47
+ const featureIds = (c.features || []).map((f) => f.id || f).join(",");
48
+ return ` <card id="${c.id}" status="${c.status}" features="${featureIds}">${escapeXml(c.title)}</card>`;
49
+ })
50
+ .join("\n");
51
+
52
+ return `<rayburst_product_context>
53
+ <rules>
54
+ <rule>You have access to a Rayburst feature atlas — a knowledge graph of product features with Gherkin acceptance criteria. Use it to understand what you're building.</rule>
55
+ <rule>Before implementing any task, check if a matching feature exists in the atlas below. If found, read its criteria with rb_get_feature and work against them.</rule>
56
+ <rule>If your changes may affect behaviors described in OTHER features' criteria, flag this to the user before proceeding.</rule>
57
+ <rule>NEVER create, update, or delete features or criteria in the atlas without explicitly asking the user for confirmation first. Show them what you want to change and wait for approval.</rule>
58
+ <rule>When implementation is complete, summarize which acceptance criteria were addressed and which remain pending.</rule>
59
+ <rule>Use mcp__plugin_rayburst_rayburst__rb_* MCP tools to interact with the atlas. Use rb_get_feature to load full criteria for a specific feature.</rule>
60
+ </rules>
61
+
62
+ <atlas summary="${total} features (${counts.draft} draft, ${counts.active} active, ${counts.completed} completed)">
63
+ ${featureLines}${truncated}
64
+ </atlas>
65
+
66
+ <board summary="${cardList.length} cards — ${cardCounts["in-progress"] || 0} in-progress, ${cardCounts["validation"] || 0} validation, ${cardCounts["done"] || 0} done">
67
+ ${cardLines}
68
+ </board>
69
+ </rayburst_product_context>`;
70
+ }
71
+
72
+ /**
73
+ * Build the Active Feature Block injected when a user prompt matches a feature.
74
+ * Contains full criteria for the matched feature(s).
75
+ */
76
+ export function buildActiveFeatureBlock(features) {
77
+ if (!features || features.length === 0) return "";
78
+
79
+ const featureBlocks = features.map((f) => {
80
+ const criteria = (f.criteria || [])
81
+ .map((c) => {
82
+ return ` <criterion id="${c.id}" status="${c.status}">${escapeXml(c.title || "")} — ${escapeXml(c.description || "")}</criterion>`;
83
+ })
84
+ .join("\n");
85
+
86
+ return ` <feature id="${f.id}" title="${escapeXml(f.title)}" status="${f.status}">
87
+ <description>${escapeXml(f.description || "")}</description>
88
+ ${criteria}
89
+ </feature>`;
90
+ });
91
+
92
+ return `<rayburst_active_feature>
93
+ ${featureBlocks.join("\n")}
94
+ <guidance>Work against these acceptance criteria. When done, summarize which criteria your changes address and which remain.</guidance>
95
+ </rayburst_active_feature>`;
96
+ }
97
+
98
+ /**
99
+ * Build the Coding Reminder Block injected before Write/Edit calls.
100
+ */
101
+ export function buildCodingReminderBlock(activeFeature, filePath, relatedFeatures) {
102
+ if (!activeFeature) return "";
103
+
104
+ const criteria = (activeFeature.criteria || [])
105
+ .map((c) => ` - [ ] ${escapeXml(c.title || c.description || "")}`)
106
+ .join("\n");
107
+
108
+ let relatedNote = "";
109
+ if (relatedFeatures && relatedFeatures.length > 0) {
110
+ const names = relatedFeatures
111
+ .map((f) => `"${escapeXml(f.title)}"`)
112
+ .join(", ");
113
+ relatedNote = `\n <related_features>This file may also relate to: ${names}. Check that your changes don't break their criteria.</related_features>`;
114
+ }
115
+
116
+ return `<rayburst_coding_reminder>
117
+ <active_feature>${escapeXml(activeFeature.title)} (${activeFeature.id})</active_feature>
118
+ <criteria_checklist>
119
+ ${criteria}
120
+ </criteria_checklist>
121
+ <file>${escapeXml(filePath || "unknown")}</file>${relatedNote}
122
+ </rayburst_coding_reminder>`;
123
+ }
124
+
125
+ function escapeXml(str) {
126
+ return (str || "")
127
+ .replace(/&/g, "&amp;")
128
+ .replace(/</g, "&lt;")
129
+ .replace(/>/g, "&gt;")
130
+ .replace(/"/g, "&quot;");
131
+ }