@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.
- package/.claude-plugin/plugin.json +3 -3
- package/README.md +229 -0
- package/hooks/hooks.json +31 -0
- package/hooks/pretooluse.mjs +87 -0
- package/hooks/product-context-block.mjs +131 -0
- package/hooks/rb-cache.mjs +247 -0
- package/hooks/sessionstart.mjs +27 -167
- package/hooks/userpromptsubmit.mjs +84 -0
- package/package.json +3 -3
- package/skills/{init → setup}/SKILL.md +3 -3
- package/skills/discover/SKILL.md +0 -127
- package/skills/implement/SKILL.md +0 -128
- package/skills/plan/SKILL.md +0 -146
- package/skills/status/SKILL.md +0 -125
- package/skills/sync/SKILL.md +0 -131
- package/skills/validate-card/SKILL.md +0 -147
- package/skills/validate-feature/SKILL.md +0 -143
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rayburst",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
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/
|
|
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
|
+
[](https://www.npmjs.com/package/@rayburst/cc) [](https://github.com/Dubwoof/rayburst-cc/stargazers) [](https://github.com/Dubwoof/rayburst-cc/commits) [](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, "&")
|
|
128
|
+
.replace(/</g, "<")
|
|
129
|
+
.replace(/>/g, ">")
|
|
130
|
+
.replace(/"/g, """);
|
|
131
|
+
}
|