@snarkipus/opencode-beads 0.8.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.
Files changed (48) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +150 -0
  3. package/dist/init/artifacts/beads/SKILL.md +16 -0
  4. package/dist/init/artifacts/beads/references/DEPENDENCIES.md +5 -0
  5. package/dist/init/artifacts/beads/references/ISSUE_CREATION.md +5 -0
  6. package/dist/init/artifacts/beads/references/RESUMABILITY.md +5 -0
  7. package/dist/init/manifest.json +54 -0
  8. package/package.json +82 -0
  9. package/src/init-cli.ts +172 -0
  10. package/src/init.ts +700 -0
  11. package/src/plugin-core.ts +290 -0
  12. package/src/plugin.ts +150 -0
  13. package/src/prime.ts +88 -0
  14. package/src/skill-collision-policy.ts +133 -0
  15. package/src/vendor-adaptations.ts +146 -0
  16. package/src/vendor-provenance.ts +252 -0
  17. package/src/vendor.ts +297 -0
  18. package/vendor/agents/task-agent.md +61 -0
  19. package/vendor/commands/audit.md +28 -0
  20. package/vendor/commands/blocked.md +15 -0
  21. package/vendor/commands/close.md +18 -0
  22. package/vendor/commands/comments.md +30 -0
  23. package/vendor/commands/compact.md +31 -0
  24. package/vendor/commands/create.md +19 -0
  25. package/vendor/commands/decision.md +111 -0
  26. package/vendor/commands/delete.md +32 -0
  27. package/vendor/commands/dep.md +101 -0
  28. package/vendor/commands/epic.md +27 -0
  29. package/vendor/commands/export.md +25 -0
  30. package/vendor/commands/import.md +18 -0
  31. package/vendor/commands/init.md +18 -0
  32. package/vendor/commands/label.md +33 -0
  33. package/vendor/commands/list.md +64 -0
  34. package/vendor/commands/prime.md +10 -0
  35. package/vendor/commands/quickstart.md +13 -0
  36. package/vendor/commands/ready.md +15 -0
  37. package/vendor/commands/rename-prefix.md +24 -0
  38. package/vendor/commands/reopen.md +22 -0
  39. package/vendor/commands/restore.md +27 -0
  40. package/vendor/commands/search.md +103 -0
  41. package/vendor/commands/show.md +17 -0
  42. package/vendor/commands/stats.md +17 -0
  43. package/vendor/commands/sync.md +17 -0
  44. package/vendor/commands/template.md +337 -0
  45. package/vendor/commands/update.md +24 -0
  46. package/vendor/commands/version.md +26 -0
  47. package/vendor/commands/workflow.md +58 -0
  48. package/vendor/manifest.json +168 -0
package/src/vendor.ts ADDED
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Vendor file loaders for beads plugin.
3
+ *
4
+ * The vendor directory contains beads command definitions and agent prompts
5
+ * synced from the upstream beads repository via scripts/sync-beads.sh.
6
+ */
7
+
8
+ import * as fs from "node:fs/promises";
9
+ import * as path from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ import type { AgentConfig, Config } from "@opencode-ai/sdk";
12
+ import { adaptVendorPrompt } from "./vendor-adaptations";
13
+
14
+ function getVendorDir(): string {
15
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
+ return path.join(__dirname, "..", "vendor");
17
+ }
18
+
19
+ async function getPackageIdentity(): Promise<string> {
20
+ const packagePath = path.join(getVendorDir(), "..", "package.json");
21
+ const value = JSON.parse(await fs.readFile(packagePath, "utf8")) as {
22
+ name?: unknown;
23
+ version?: unknown;
24
+ };
25
+ if (typeof value.name !== "string") {
26
+ throw new Error("Invalid package artifact package.json: name must be a string");
27
+ }
28
+ if (typeof value.version !== "string") {
29
+ throw new Error("Invalid package artifact package.json: version must be a string");
30
+ }
31
+ return `${value.name}@${value.version}`;
32
+ }
33
+
34
+ interface ParsedMarkdown {
35
+ frontmatter: Record<string, string>;
36
+ body: string;
37
+ }
38
+
39
+ const DOCUMENT_ONLY_COMMANDS = new Set(["commands/prime.md", "commands/template.md"]);
40
+ const COMMAND_FRONTMATTER_FIELDS = new Set([
41
+ "agent",
42
+ "argument-hint",
43
+ "description",
44
+ "model",
45
+ "subtask",
46
+ ]);
47
+ const AGENT_FRONTMATTER_FIELDS = new Set([
48
+ "color",
49
+ "description",
50
+ "disable",
51
+ "maxSteps",
52
+ "mode",
53
+ "model",
54
+ "temperature",
55
+ "top_p",
56
+ ]);
57
+
58
+ function vendorError(relativePath: string, reason: string): Error {
59
+ return new Error(`Invalid vendor artifact ${relativePath}: ${reason}`);
60
+ }
61
+
62
+ function parseMarkdownWithFrontmatter(content: string, relativePath: string): ParsedMarkdown {
63
+ const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
64
+ const match = content.match(frontmatterRegex);
65
+
66
+ if (!match) throw vendorError(relativePath, "missing or malformed frontmatter delimiters");
67
+
68
+ const frontmatterStr = match[1];
69
+ const body = match[2];
70
+
71
+ if (frontmatterStr === undefined || body === undefined) {
72
+ throw vendorError(relativePath, "missing frontmatter or body");
73
+ }
74
+
75
+ const frontmatter: Record<string, string> = {};
76
+
77
+ for (const line of frontmatterStr.split("\n")) {
78
+ const trimmed = line.trim();
79
+ if (!trimmed || trimmed.startsWith("#")) continue;
80
+
81
+ const colonIndex = trimmed.indexOf(":");
82
+ if (colonIndex <= 0) throw vendorError(relativePath, `malformed frontmatter line: ${trimmed}`);
83
+
84
+ const key = trimmed.slice(0, colonIndex).trim();
85
+ let value = trimmed.slice(colonIndex + 1).trim();
86
+ if (Object.hasOwn(frontmatter, key)) {
87
+ throw vendorError(relativePath, `duplicate frontmatter field: ${key}`);
88
+ }
89
+
90
+ // Handle quoted strings
91
+ if (
92
+ (value.startsWith('"') && value.endsWith('"')) ||
93
+ (value.startsWith("'") && value.endsWith("'"))
94
+ ) {
95
+ value = value.slice(1, -1);
96
+ } else if (value.startsWith('"') || value.startsWith("'")) {
97
+ throw vendorError(relativePath, `unterminated quoted value for ${key}`);
98
+ }
99
+
100
+ // Handle empty array syntax like []
101
+ if (value === "[]") {
102
+ value = "";
103
+ }
104
+
105
+ frontmatter[key] = value;
106
+ }
107
+
108
+ const trimmedBody = body.trim();
109
+ if (!trimmedBody) throw vendorError(relativePath, "body must not be empty");
110
+ return { frontmatter, body: trimmedBody };
111
+ }
112
+
113
+ async function readVendorFile(vendorDirectory: string, relativePath: string): Promise<string> {
114
+ try {
115
+ const fullPath = path.join(vendorDirectory, relativePath);
116
+ return await fs.readFile(fullPath, "utf-8");
117
+ } catch (error) {
118
+ const reason = error instanceof Error ? error.message : String(error);
119
+ throw vendorError(relativePath, `cannot read file: ${reason}`);
120
+ }
121
+ }
122
+
123
+ async function manifestPaths(vendorDirectory: string): Promise<string[]> {
124
+ const content = await readVendorFile(vendorDirectory, "manifest.json");
125
+ let manifest: unknown;
126
+ try {
127
+ manifest = JSON.parse(content);
128
+ } catch (error) {
129
+ const reason = error instanceof Error ? error.message : String(error);
130
+ throw vendorError("manifest.json", `invalid JSON: ${reason}`);
131
+ }
132
+
133
+ if (
134
+ typeof manifest !== "object" ||
135
+ manifest === null ||
136
+ !("files" in manifest) ||
137
+ !Array.isArray(manifest.files)
138
+ ) {
139
+ throw vendorError("manifest.json", "files must be an array");
140
+ }
141
+ const paths = manifest.files.map((file, index) => {
142
+ if (
143
+ typeof file !== "object" ||
144
+ file === null ||
145
+ !("path" in file) ||
146
+ typeof file.path !== "string"
147
+ ) {
148
+ throw vendorError("manifest.json", `files[${index}].path must be a string`);
149
+ }
150
+ return file.path;
151
+ });
152
+ if (new Set(paths).size !== paths.length) {
153
+ throw vendorError("manifest.json", "file paths must be unique");
154
+ }
155
+ return paths.sort();
156
+ }
157
+
158
+ function requiredString(
159
+ frontmatter: Record<string, string>,
160
+ field: string,
161
+ relativePath: string
162
+ ): string {
163
+ const value = frontmatter[field];
164
+ if (!value?.trim()) throw vendorError(relativePath, `${field} must be a non-empty string`);
165
+ return value;
166
+ }
167
+
168
+ function parseBoolean(value: string, field: string, relativePath: string): boolean {
169
+ if (value === "true") return true;
170
+ if (value === "false") return false;
171
+ throw vendorError(relativePath, `${field} must be true or false`);
172
+ }
173
+
174
+ function parseNumber(value: string, field: string, relativePath: string): number {
175
+ const parsed = Number(value);
176
+ if (!Number.isFinite(parsed)) throw vendorError(relativePath, `${field} must be a number`);
177
+ return parsed;
178
+ }
179
+
180
+ function rejectUnsupportedFields(
181
+ frontmatter: Record<string, string>,
182
+ supported: ReadonlySet<string>,
183
+ relativePath: string
184
+ ): void {
185
+ const unsupported = Object.keys(frontmatter).find((field) => !supported.has(field));
186
+ if (unsupported) throw vendorError(relativePath, `unsupported frontmatter field: ${unsupported}`);
187
+ }
188
+
189
+ const BEADS_HOST_GUIDANCE = `## CLI Safety
190
+
191
+ There is no native \`bd\` or Beads MCP tool in OpenCode. Run \`bd\` through \`bash\`.
192
+ Treat the injected \`bd prime\` output as the canonical workflow reference. Use \`bd --help\` to discover commands, \`bd <command> --help\` for current syntax, and \`--json\` when structured output improves reliability. Distinguish command failures from output parsing failures.`;
193
+
194
+ const BEADS_DELEGATION_GUIDANCE = `## Delegation
195
+
196
+ Delegate multi-command Beads work and issue-graph analysis to the \`beads-task-agent\`; perform only single atomic operations directly.`;
197
+
198
+ const BEADS_SUBAGENT_CONTEXT = `## Beads Task Agent
199
+
200
+ Full \`bd prime\` workflow context is injected at start and after compaction. Use \`bd\` via \`bash\`; rerun prime if context is missing or stale.
201
+
202
+ For status or graph analysis, never claim, update, close, or modify a bead. Return the analysis and stop.
203
+
204
+ For completion, process exactly one bead. Use a caller-supplied ID; otherwise choose one highest-priority ready bead. First run \`bd show <id> --json\`; confirm it is open, actionable, and appropriate; then claim with \`bd update <id> --claim --json\`. If an assigned bead is closed, blocked, unsuitable, missing, or otherwise not actionable, report and return without mutation or substitution.
205
+
206
+ Implement and validate it. Create discoveries with \`discovered-from:<current-id>\` and a parent only when structurally obvious. Never claim or implement work created this invocation; it may be eligible later. Close or block the current bead, report discoveries, and return without seeking more work. Return concise results, not raw JSON.`;
207
+
208
+ /** Build the single shared guidance layer for one injected audience. */
209
+ export function beadsGuidance(
210
+ audience: "primary" | "task-agent"
211
+ ): string {
212
+ const sections = [BEADS_HOST_GUIDANCE];
213
+ if (audience === "primary") sections.push(BEADS_DELEGATION_GUIDANCE);
214
+ return `<beads-guidance>\n${sections.join("\n\n")}\n</beads-guidance>`;
215
+ }
216
+
217
+ export async function loadAgent(vendorDirectory = getVendorDir()): Promise<Config["agent"]> {
218
+ const relativePath = "agents/task-agent.md";
219
+ const paths = await manifestPaths(vendorDirectory);
220
+ if (!paths.includes(relativePath)) {
221
+ throw vendorError("manifest.json", `missing required file record: ${relativePath}`);
222
+ }
223
+ const content = await readVendorFile(vendorDirectory, relativePath);
224
+ const parsed = parseMarkdownWithFrontmatter(content, relativePath);
225
+ const { frontmatter } = parsed;
226
+ rejectUnsupportedFields(frontmatter, AGENT_FRONTMATTER_FIELDS, relativePath);
227
+ if (frontmatter.mode !== undefined && frontmatter.mode !== "subagent") {
228
+ throw vendorError(relativePath, "mode must be subagent when provided");
229
+ }
230
+
231
+ const agent: AgentConfig = {
232
+ description: requiredString(frontmatter, "description", relativePath),
233
+ prompt: BEADS_SUBAGENT_CONTEXT,
234
+ mode: "subagent",
235
+ };
236
+ // Keep upstream adaptation validation as a publish-time compatibility gate.
237
+ adaptVendorPrompt(relativePath, parsed.body);
238
+ if (frontmatter.model) agent.model = frontmatter.model;
239
+ if (frontmatter.temperature) {
240
+ agent.temperature = parseNumber(frontmatter.temperature, "temperature", relativePath);
241
+ }
242
+ if (frontmatter.top_p) agent.top_p = parseNumber(frontmatter.top_p, "top_p", relativePath);
243
+ if (frontmatter.disable) {
244
+ agent.disable = parseBoolean(frontmatter.disable, "disable", relativePath);
245
+ }
246
+ if (frontmatter.color) agent.color = frontmatter.color;
247
+ if (frontmatter.maxSteps) {
248
+ agent.maxSteps = parseNumber(frontmatter.maxSteps, "maxSteps", relativePath);
249
+ }
250
+
251
+ return { "beads-task-agent": agent };
252
+ }
253
+
254
+ export async function loadCommands(vendorDirectory = getVendorDir()): Promise<Config["command"]> {
255
+ const paths = await manifestPaths(vendorDirectory);
256
+ const commandPaths = paths.filter((file) => file.startsWith("commands/") && file.endsWith(".md"));
257
+ if (commandPaths.length === 0) {
258
+ throw vendorError("manifest.json", "no command Markdown files are recorded");
259
+ }
260
+ const commands: Config["command"] = {};
261
+
262
+ for (const relativePath of commandPaths) {
263
+ const content = await readVendorFile(vendorDirectory, relativePath);
264
+ if (DOCUMENT_ONLY_COMMANDS.has(relativePath)) continue;
265
+ const parsed = parseMarkdownWithFrontmatter(content, relativePath);
266
+ const { frontmatter } = parsed;
267
+ rejectUnsupportedFields(frontmatter, COMMAND_FRONTMATTER_FIELDS, relativePath);
268
+ const file = path.basename(relativePath);
269
+ const name = `beads:${file.replace(".md", "")}`;
270
+
271
+ const argHint = frontmatter["argument-hint"];
272
+ const baseDescription = requiredString(frontmatter, "description", relativePath);
273
+ const description = argHint
274
+ ? `${baseDescription} (${argHint})`
275
+ : baseDescription;
276
+
277
+ const command: NonNullable<Config["command"]>[string] = {
278
+ description,
279
+ template: adaptVendorPrompt(relativePath, parsed.body),
280
+ };
281
+ if (frontmatter.agent) command.agent = frontmatter.agent;
282
+ if (frontmatter.model) command.model = frontmatter.model;
283
+ if (frontmatter.subtask) {
284
+ command.subtask = parseBoolean(frontmatter.subtask, "subtask", relativePath);
285
+ }
286
+ commands[name] = command;
287
+ }
288
+
289
+ const packageIdentity = await getPackageIdentity();
290
+ const cli = `bunx ${packageIdentity}`;
291
+ commands["beads:setup"] = {
292
+ description: "Install or manage the OpenCode Beads skill",
293
+ template: `The package CLI is canonical for the OpenCode Beads skill lifecycle. Use exact version-matched commands: \`${cli} init\`, \`${cli} init --global\`, \`${cli} check\`, \`${cli} update\`, and \`${cli} remove\`. Add \`--global\` to check, update, or remove for global scope. \`/beads:init\` is DB-only: it initializes a Beads database and does not install the skill.`,
294
+ };
295
+
296
+ return commands;
297
+ }
@@ -0,0 +1,61 @@
1
+ ---
2
+ description: Autonomous agent that finds and completes ready tasks
3
+ ---
4
+
5
+ You are a task-completion agent for beads. Your goal is to find ready work and complete it autonomously.
6
+
7
+ # Agent Workflow
8
+
9
+ 1. **Find Ready Work**
10
+ - Use the `ready` MCP tool to get unblocked tasks
11
+ - Prefer higher priority tasks (P0 > P1 > P2 > P3 > P4)
12
+ - If no ready tasks, report completion
13
+
14
+ 2. **Claim the Task**
15
+ - Use the `show` tool to get full task details
16
+ - Use the `claim` tool for atomic start-work semantics
17
+ - Report what you're working on
18
+
19
+ 3. **Execute the Task**
20
+ - Read the task description carefully
21
+ - Use available tools to complete the work
22
+ - Follow best practices from project documentation
23
+ - Run tests if applicable
24
+
25
+ 4. **Track Discoveries**
26
+ - If you find bugs, TODOs, or related work:
27
+ - Use `create` tool to file new issues
28
+ - Use `dep` tool with `discovered-from` to link them
29
+ - This maintains context for future work
30
+
31
+ 5. **Complete the Task**
32
+ - Verify the work is done correctly
33
+ - Use `close` tool with a clear completion message
34
+ - Report what was accomplished
35
+
36
+ 6. **Continue**
37
+ - Check for newly unblocked work with `ready`
38
+ - Repeat the cycle
39
+
40
+ # Important Guidelines
41
+
42
+ - Always claim before working (MCP: `claim`; CLI: `--claim`) and close when done
43
+ - Link discovered work with `discovered-from` dependencies
44
+ - Don't close issues unless work is actually complete
45
+ - If blocked, use `update` to set status to `blocked` and explain why
46
+ - Communicate clearly about progress and blockers
47
+
48
+ # Available Tools
49
+
50
+ Via beads MCP server:
51
+ - `ready` - Find unblocked tasks
52
+ - `show` - Get task details
53
+ - `claim` - Atomically claim task for work
54
+ - `update` - Update task status/fields
55
+ - `create` - Create new issues
56
+ - `dep` - Manage dependencies
57
+ - `close` - Complete tasks
58
+ - `blocked` - Check blocked issues
59
+ - `stats` - View project stats
60
+
61
+ You are autonomous but should communicate your progress clearly. Start by finding ready work!
@@ -0,0 +1,28 @@
1
+ ---
2
+ description: Log and label agent interactions (append-only JSONL)
3
+ argument-hint: record|label
4
+ ---
5
+
6
+ Append-only audit logging for agent interactions (prompts, responses, tool calls) in `.beads/interactions.jsonl`.
7
+
8
+ Each line is one event. Labeling is done by appending a new `"label"` event referencing a previous entry.
9
+
10
+ ## Usage
11
+
12
+ - **Record an interaction**:
13
+ - `bd audit record --kind llm_call --model "claude-3-5-haiku" --prompt "..." --response "..."`
14
+ - `bd audit record --kind tool_call --tool-name "go test" --exit-code 1 --error "..." --issue-id bd-42`
15
+
16
+ - **Pipe JSON via stdin**:
17
+ - `cat event.json | bd audit record`
18
+
19
+ - **Label an entry**:
20
+ - `bd audit label int-a1b2 --label good --reason "Worked perfectly"`
21
+ - `bd audit label int-a1b2 --label bad --reason "Hallucinated a file path"`
22
+
23
+ ## Notes
24
+
25
+ - Audit entries are **append-only** (no in-place edits).
26
+ - `bd dolt push` includes `.beads/interactions.jsonl` in the commit allowlist.
27
+
28
+
@@ -0,0 +1,15 @@
1
+ ---
2
+ description: Show blocked issues
3
+ argument-hint: []
4
+ ---
5
+
6
+ Show all issues that are blocked by dependencies.
7
+
8
+ Use `bd blocked` to see which issues have blockers preventing them from being worked on. This is the inverse of `bd ready` - it shows what's NOT ready.
9
+
10
+ Blocked issues have one or more dependencies with type "blocks" that are still open. Once all blocking dependencies are closed, the issue becomes ready and will appear in `bd ready`.
11
+
12
+ Useful for:
13
+ - Understanding why work is stuck
14
+ - Identifying critical path items
15
+ - Planning dependency resolution
@@ -0,0 +1,18 @@
1
+ ---
2
+ description: Close a completed issue
3
+ argument-hint: [issue-id] [reason]
4
+ ---
5
+
6
+ Close a beads issue that's been completed.
7
+
8
+ If arguments are provided:
9
+ - $1: Issue ID
10
+ - $2+: Completion reason (optional)
11
+
12
+ If the issue ID is missing, ask for it. Optionally ask for a reason describing what was done.
13
+
14
+ Use the beads MCP `close` tool to close the issue. Show confirmation with the issue details.
15
+
16
+ After closing, suggest checking for:
17
+ - Dependent issues that might now be unblocked (use `ready` tool)
18
+ - New work discovered during this task (use `create` tool with `discovered-from` link)
@@ -0,0 +1,30 @@
1
+ ---
2
+ description: View or manage comments on an issue
3
+ argument-hint: [issue-id]
4
+ ---
5
+
6
+ View or add comments to a beads issue.
7
+
8
+ Comments are separate from issue properties (title, description, etc.) because they serve a different purpose: they're a **discussion thread** rather than **singular editable fields**. Use `bd comments` for threaded conversations and `bd edit` for core issue metadata.
9
+
10
+ ## View Comments
11
+
12
+ To view all comments on an issue:
13
+ - $1: Issue ID (e.g., bd-123)
14
+
15
+ Use the beads CLI `bd comments <issue-id>` to list all comments. Show them to the user with timestamps and authors.
16
+
17
+ ## Add Comment
18
+
19
+ To add a comment:
20
+ - $1: "add"
21
+ - $2: Issue ID
22
+ - $3: Comment text (or use -f flag for file input)
23
+
24
+ Use `bd comments add <issue-id> "comment text"` to add a comment. Confirm the comment was added successfully.
25
+
26
+ Comments are useful for:
27
+ - Progress updates during work
28
+ - Design notes or technical decisions
29
+ - Links to related resources
30
+ - Questions or blockers
@@ -0,0 +1,31 @@
1
+ ---
2
+ description: Compact old closed issues using semantic summarization
3
+ argument-hint: [--all] [--id issue-id] [--dry-run]
4
+ ---
5
+
6
+ Reduce database size by summarizing closed issues no longer actively referenced.
7
+
8
+ ## Compaction Tiers
9
+
10
+ - **Tier 1**: Semantic compression (30+ days closed, ~70% size reduction)
11
+ - **Tier 2**: Ultra compression (90+ days closed) — planned, not yet implemented
12
+
13
+ ## Usage
14
+
15
+ - **Preview candidates**: `bd admin compact --dry-run`
16
+ - **Compact all eligible**: `bd admin compact --all`
17
+ - **Compact specific issue**: `bd admin compact --id bd-42`
18
+ - **Force compact**: `bd admin compact --id bd-42 --force` (bypass age checks)
19
+ - **View statistics**: `bd admin compact --stats`
20
+
21
+ ## Options
22
+
23
+ - **--tier**: Choose compaction tier (1 or 2, default: 1)
24
+ - **--workers**: Parallel workers (default: 5)
25
+ - **--batch-size**: Issues per batch (default: 10)
26
+
27
+ ## Important
28
+
29
+ This is **permanent graceful decay** - original content is discarded. Use `bd restore <id>` to view full history from git if needed.
30
+
31
+ Useful for long-running projects to keep database size manageable.
@@ -0,0 +1,19 @@
1
+ ---
2
+ description: Create a new issue interactively
3
+ argument-hint: [title] [type] [priority]
4
+ ---
5
+
6
+ Create a new beads issue. If arguments are provided:
7
+ - $1: Issue title
8
+ - $2: Issue type (bug, feature, task, epic, chore, decision)
9
+ - $3: Priority (0-4, where 0=critical, 4=backlog)
10
+
11
+ If arguments are missing, ask the user for:
12
+ 1. Issue title (required)
13
+ 2. Issue type (default: task)
14
+ 3. Priority (default: 2)
15
+ 4. Description (optional)
16
+
17
+ Use the beads MCP `create` tool to create the issue. Show the created issue ID and details to the user.
18
+
19
+ Optionally ask if this issue should be linked to another issue (discovered-from, blocks, parent-child, related).
@@ -0,0 +1,111 @@
1
+ ---
2
+ description: Record, list, and manage project decisions with rationale tracking
3
+ argument-hint: record|list|show|supersede
4
+ ---
5
+
6
+ Record and track project decisions as beads issues with structured rationale, alternatives considered, and links to affected work.
7
+
8
+ Decisions use `--type decision`. The description field holds the structured decision record.
9
+
10
+ ## Record a Decision
11
+
12
+ When the user wants to record a decision (or you invoke `bd decision record`):
13
+
14
+ 1. Gather the following (ask if not provided):
15
+ - **Title**: Short summary of what was decided (required)
16
+ - **Rationale**: Why this was chosen (required)
17
+ - **Alternatives**: What else was considered (optional but encouraged)
18
+ - **Affects**: Issue IDs this decision impacts (optional)
19
+ - **Priority**: How important (default P2)
20
+
21
+ 2. Create the issue with structured description:
22
+
23
+ ```bash
24
+ bd create "<title>" --type decision \
25
+ --description "$(cat <<'EOF'
26
+ ## Decision
27
+
28
+ <one-sentence summary of what was decided>
29
+
30
+ ## Rationale
31
+
32
+ <why this was chosen>
33
+
34
+ ## Alternatives Considered
35
+
36
+ - **<alt 1>**: <why rejected>
37
+ - **<alt 2>**: <why rejected>
38
+
39
+ ## Affects
40
+
41
+ - <issue IDs or area descriptions>
42
+ EOF
43
+ )"
44
+ ```
45
+
46
+ 3. If `--affects` issue IDs were provided, link them:
47
+ ```bash
48
+ bd dep add <decision-id> <affected-id> --type related
49
+ ```
50
+
51
+ 4. Show the created decision to the user.
52
+
53
+ ## List Decisions
54
+
55
+ ```bash
56
+ bd list --type decision
57
+ ```
58
+
59
+ To see all decisions including closed/superseded:
60
+ ```bash
61
+ bd list --type decision --all
62
+ ```
63
+
64
+ ## Show a Decision
65
+
66
+ ```bash
67
+ bd show <decision-id>
68
+ ```
69
+
70
+ Include comments for discussion history:
71
+ ```bash
72
+ bd comments <decision-id>
73
+ ```
74
+
75
+ ## Supersede a Decision
76
+
77
+ When a decision is replaced by a new one:
78
+
79
+ 1. Record the new decision (as above)
80
+ 2. Link the new decision to the old one:
81
+ ```bash
82
+ bd dep add <new-id> <old-id> --type related
83
+ ```
84
+ 3. Add a comment on the old decision:
85
+ ```bash
86
+ bd comments add <old-id> "Superseded by <new-id>: <brief reason>"
87
+ ```
88
+ 4. Close the old decision:
89
+ ```bash
90
+ bd close <old-id> --reason "Superseded by <new-id>"
91
+ ```
92
+
93
+ ## Add Context to an Existing Decision
94
+
95
+ Use comments to append discussion, implementation notes, or revisit rationale:
96
+ ```bash
97
+ bd comments add <decision-id> "Implementation note: ..."
98
+ ```
99
+
100
+ ## Search Decisions
101
+
102
+ ```bash
103
+ bd search "keyword" --type decision
104
+ ```
105
+
106
+ ## Conventions
107
+
108
+ - **Status**: `open` = active decision, `closed` = superseded or reversed
109
+ - **Description format**: Use the structured template above for consistency
110
+ - **Linking**: Use `related` dependency type to connect decisions to affected issues
111
+ - **Labels**: Use labels for categorizing decisions (e.g., `architecture`, `tooling`, `process`)
@@ -0,0 +1,32 @@
1
+ ---
2
+ description: Delete issues and clean up references
3
+ argument-hint: [issue-ids...] [--force]
4
+ ---
5
+
6
+ Delete one or more issues and clean up all references.
7
+
8
+ ## Safety Features
9
+
10
+ - **Preview mode**: Default shows what would be deleted
11
+ - **--force**: Required to actually delete
12
+ - **--dry-run**: Preview collision detection
13
+ - **Dependency checks**: Fails if issue has dependents (unless --cascade or --force)
14
+
15
+ ## Batch Deletion
16
+
17
+ - Delete multiple: `bd delete bd-1 bd-2 bd-3 --force`
18
+ - Delete from file: `bd delete --from-file deletions.txt --force`
19
+
20
+ ## Dependency Handling
21
+
22
+ - **Default**: Fails if issue has dependents not in deletion set
23
+ - **--cascade**: Recursively delete all dependent issues
24
+ - **--force**: Delete and orphan dependents
25
+
26
+ ## What Gets Deleted
27
+
28
+ 1. All dependency links (any type, both directions)
29
+ 2. Text references updated to "[deleted:ID]" in connected issues
30
+ 3. Issue removed from database
31
+
32
+ This operation cannot be undone. Use with caution!