@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.
- package/LICENSE +9 -0
- package/README.md +150 -0
- package/dist/init/artifacts/beads/SKILL.md +16 -0
- package/dist/init/artifacts/beads/references/DEPENDENCIES.md +5 -0
- package/dist/init/artifacts/beads/references/ISSUE_CREATION.md +5 -0
- package/dist/init/artifacts/beads/references/RESUMABILITY.md +5 -0
- package/dist/init/manifest.json +54 -0
- package/package.json +82 -0
- package/src/init-cli.ts +172 -0
- package/src/init.ts +700 -0
- package/src/plugin-core.ts +290 -0
- package/src/plugin.ts +150 -0
- package/src/prime.ts +88 -0
- package/src/skill-collision-policy.ts +133 -0
- package/src/vendor-adaptations.ts +146 -0
- package/src/vendor-provenance.ts +252 -0
- package/src/vendor.ts +297 -0
- package/vendor/agents/task-agent.md +61 -0
- package/vendor/commands/audit.md +28 -0
- package/vendor/commands/blocked.md +15 -0
- package/vendor/commands/close.md +18 -0
- package/vendor/commands/comments.md +30 -0
- package/vendor/commands/compact.md +31 -0
- package/vendor/commands/create.md +19 -0
- package/vendor/commands/decision.md +111 -0
- package/vendor/commands/delete.md +32 -0
- package/vendor/commands/dep.md +101 -0
- package/vendor/commands/epic.md +27 -0
- package/vendor/commands/export.md +25 -0
- package/vendor/commands/import.md +18 -0
- package/vendor/commands/init.md +18 -0
- package/vendor/commands/label.md +33 -0
- package/vendor/commands/list.md +64 -0
- package/vendor/commands/prime.md +10 -0
- package/vendor/commands/quickstart.md +13 -0
- package/vendor/commands/ready.md +15 -0
- package/vendor/commands/rename-prefix.md +24 -0
- package/vendor/commands/reopen.md +22 -0
- package/vendor/commands/restore.md +27 -0
- package/vendor/commands/search.md +103 -0
- package/vendor/commands/show.md +17 -0
- package/vendor/commands/stats.md +17 -0
- package/vendor/commands/sync.md +17 -0
- package/vendor/commands/template.md +337 -0
- package/vendor/commands/update.md +24 -0
- package/vendor/commands/version.md +26 -0
- package/vendor/commands/workflow.md +58 -0
- 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!
|