Merge branch 'steveyegge:main' into main

This commit is contained in:
Augustinas Malinauskas
2025-10-14 13:38:14 -07:00
committed by GitHub
8 changed files with 575 additions and 18 deletions

View File

@@ -0,0 +1,22 @@
---
description: Check beads and plugin versions
---
Check the installed versions of beads components and verify compatibility.
Use the beads MCP tools to:
1. Run `bd --version` via bash to get the CLI version
2. Check the plugin version from the environment
3. Compare versions and report any mismatches
Display:
- bd CLI version (from `bd --version`)
- Plugin version (0.9.0)
- MCP server status (from `stats` tool or connection test)
- Compatibility status (✓ compatible or ⚠️ update needed)
If versions are mismatched, provide instructions:
- Update bd CLI: `curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/install.sh | bash`
- Update plugin: `/plugin update beads`
Suggest checking for updates if the user is on an older version.

View File

@@ -16,6 +16,9 @@
"agent-memory", "agent-memory",
"mcp-server" "mcp-server"
], ],
"engines": {
"beads": ">=0.9.0"
},
"mcpServers": { "mcpServers": {
"beads": { "beads": {
"command": "uv", "command": "uv",

View File

@@ -4,11 +4,13 @@ This document explains our approach to `golangci-lint` warnings in this codebase
## Current Status ## Current Status
Running `golangci-lint run ./...` currently reports ~100 "issues". However, these are not actual code quality problems - they are false positives or intentional patterns that reflect idiomatic Go practice. Running `golangci-lint run ./...` currently reports ~200 "issues". However, these are not actual code quality problems - they are false positives or intentional patterns that reflect idiomatic Go practice.
**Note**: The count increased from ~100 to ~200 between Oct 12-14, 2025, due to significant test coverage additions for collision resolution (1100+ lines) and auto-flush features (300+ lines). All new warnings follow the same idiomatic patterns documented below.
## Issue Breakdown ## Issue Breakdown
### errcheck (73 issues) ### errcheck (159 issues)
**Pattern**: Unchecked errors from `defer` cleanup operations **Pattern**: Unchecked errors from `defer` cleanup operations
**Status**: Intentional and idiomatic **Status**: Intentional and idiomatic
@@ -27,9 +29,9 @@ defer os.RemoveAll(tmpDir) // in tests
Fixing these would add noise without improving code quality. The critical cleanup operations (where errors matter) are already checked explicitly. Fixing these would add noise without improving code quality. The critical cleanup operations (where errors matter) are already checked explicitly.
### revive (17 issues) ### revive (21 issues)
**Pattern 1**: Unused parameters in Cobra command handlers (15 issues) **Pattern 1**: Unused parameters in Cobra command handlers (18 issues)
**Status**: Required by interface **Status**: Required by interface
Examples: Examples:
@@ -41,13 +43,14 @@ Run: func(cmd *cobra.Command, args []string) {
**Rationale**: Cobra requires this exact function signature. Renaming to `_` would make the code less clear when parameters *are* used. **Rationale**: Cobra requires this exact function signature. Renaming to `_` would make the code less clear when parameters *are* used.
**Pattern 2**: Package naming (2 issues) **Pattern 2**: Package naming (3 issues)
- `package types` - Clear and appropriate for a types package - `package types` - Clear and appropriate for a types package
- `SQLiteStorage` - Intentional; `sqlite.Storage` would be confusing with the interface - `SQLiteStorage` - Intentional; `sqlite.Storage` would be confusing with the interface
- Blank import comment - Required for database driver registration
### gosec (7 issues) ### gosec (19 issues)
**Pattern 1**: G201 - SQL string formatting (4 issues) **Pattern 1**: G201 - SQL string formatting (6 issues)
**Status**: False positive - all SQL is validated **Status**: False positive - all SQL is validated
All dynamic SQL construction uses: All dynamic SQL construction uses:
@@ -55,25 +58,30 @@ All dynamic SQL construction uses:
- Parameterized queries for all values - Parameterized queries for all values
- Safe string building for clauses like ORDER BY and LIMIT - Safe string building for clauses like ORDER BY and LIMIT
**Pattern 2**: G304 - File inclusion via variable (2 issues) **Pattern 2**: G304 - File inclusion via variable (11 issues)
**Status**: Intended feature - user-specified file paths for import/export **Status**: Intended feature - user-specified file paths for import/export/test fixtures
**Pattern 3**: G301 - Directory permissions (1 issue) All file paths are either:
**Status**: Acceptable - 0755 is reasonable for a database directory - User-provided CLI arguments (expected for import/export commands)
- Test fixtures in controlled test environments
- Validated paths with security checks (e.g., markdown.go uses validateMarkdownPath)
### dupl (2 issues) **Pattern 3**: G301 - Directory permissions (2 issues)
**Status**: Acceptable - 0755 is reasonable for database directories
**Pattern**: Test code duplication ### gocyclo (1 issue)
**Pattern**: High cyclomatic complexity in `TestExportImport` (31)
**Status**: Acceptable **Status**: Acceptable
Test code duplication is often preferable to premature test abstraction. These tests are clear and maintainable as-is. This comprehensive integration test covers multiple scenarios (export, import, filters, updates). The complexity comes from thorough test coverage, not production code. Splitting would reduce readability.
### goconst (1 issue) ### goconst (2 issues)
**Pattern**: Repeated string constant in tests **Pattern**: Repeated string constants in tests
**Status**: Acceptable **Status**: Acceptable
The string `"test-user"` appears multiple times in test code. Extracting this to a constant would not improve test readability. Repeated test strings like `"test-user"` and file paths appear multiple times. Extracting these to constants would not improve test readability or maintainability.
## golangci-lint Configuration Challenges ## golangci-lint Configuration Challenges
@@ -89,7 +97,7 @@ This appears to be a known limitation of golangci-lint's configuration system.
**For contributors**: Don't be alarmed by the lint warnings. The code quality is high. **For contributors**: Don't be alarmed by the lint warnings. The code quality is high.
**For code review**: Focus on: **For code review**: Focus on:
- New issues introduced by changes (not the baseline 100) - New issues introduced by changes (not the baseline ~200)
- Actual logic errors - Actual logic errors
- Missing error checks on critical operations (file writes, database commits) - Missing error checks on critical operations (file writes, database commits)
- Security concerns beyond gosec's false positives - Security concerns beyond gosec's false positives

View File

@@ -73,6 +73,10 @@ After installation, restart Claude Code to activate the MCP server.
## Available Commands ## Available Commands
### Version Management
- **`/bd-version`** - Check bd CLI, plugin, and MCP server versions
### Core Workflow Commands ### Core Workflow Commands
- **`/bd-ready`** - Find tasks with no blockers, ready to work on - **`/bd-ready`** - Find tasks with no blockers, ready to work on
@@ -225,6 +229,58 @@ git pull
bd ready # Fresh data from git! bd ready # Fresh data from git!
``` ```
## Updating
The beads plugin has three components that may need updating:
### 1. Plugin Updates
Check for plugin updates:
```bash
/plugin update beads
```
Claude Code will pull the latest version from GitHub. After updating, **restart Claude Code** to apply MCP server changes.
### 2. bd CLI Updates
The plugin requires the `bd` CLI to be installed. Update it separately:
```bash
# Quick update
curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/install.sh | bash
# Or with go
go install github.com/steveyegge/beads/cmd/bd@latest
```
### 3. Version Compatibility
Check version compatibility:
```bash
/bd-version
```
This will show:
- bd CLI version
- Plugin version
- MCP server status
- Compatibility warnings if versions mismatch
**Recommended update workflow:**
1. Check versions: `/bd-version`
2. Update bd CLI if needed (see above)
3. Update plugin: `/plugin update beads`
4. Restart Claude Code
5. Verify: `/bd-version`
### Version Numbering
Beads follows semantic versioning. The plugin version tracks the bd CLI version:
- Plugin 0.9.x requires bd CLI 0.9.0+
- Major version bumps may introduce breaking changes
- Check CHANGELOG.md for release notes
## Troubleshooting ## Troubleshooting
### Plugin not appearing ### Plugin not appearing

View File

@@ -6,6 +6,7 @@ This directory contains examples of how to integrate bd with AI agents and workf
- **[python-agent/](python-agent/)** - Simple Python agent that discovers ready work and completes tasks - **[python-agent/](python-agent/)** - Simple Python agent that discovers ready work and completes tasks
- **[bash-agent/](bash-agent/)** - Bash script showing the full agent workflow - **[bash-agent/](bash-agent/)** - Bash script showing the full agent workflow
- **[markdown-to-jsonl/](markdown-to-jsonl/)** - Convert markdown planning docs to bd issues
- **[git-hooks/](git-hooks/)** - Pre-configured git hooks for automatic export/import - **[git-hooks/](git-hooks/)** - Pre-configured git hooks for automatic export/import
- **[branch-merge/](branch-merge/)** - Branch merge workflow with collision resolution - **[branch-merge/](branch-merge/)** - Branch merge workflow with collision resolution
- **[claude-desktop-mcp/](claude-desktop-mcp/)** - MCP server for Claude Desktop integration - **[claude-desktop-mcp/](claude-desktop-mcp/)** - MCP server for Claude Desktop integration

View File

@@ -0,0 +1,165 @@
# Markdown to JSONL Converter
Convert markdown planning documents into `bd` issues.
## Overview
This example shows how to bridge the gap between markdown planning docs and tracked issues, without adding complexity to the `bd` core tool.
The converter script (`md2jsonl.py`) parses markdown files and outputs JSONL that can be imported into `bd`.
## Features
-**YAML Frontmatter** - Extract metadata (priority, type, assignee)
-**Headings as Issues** - Each H1/H2 becomes an issue
-**Task Lists** - Markdown checklists become sub-issues
-**Dependency Parsing** - Extract "blocks: bd-10" references
-**Customizable** - Modify the script for your conventions
## Usage
### Basic conversion
```bash
python md2jsonl.py feature.md | bd import
```
### Save to file first
```bash
python md2jsonl.py feature.md > issues.jsonl
bd import -i issues.jsonl
```
### Preview before importing
```bash
python md2jsonl.py feature.md | jq .
```
## Markdown Format
### Frontmatter (Optional)
```markdown
---
priority: 1
type: feature
assignee: alice
---
```
### Headings
Each heading becomes an issue:
```markdown
# Main Feature
Description of the feature...
## Sub-task 1
Details about sub-task...
## Sub-task 2
More details...
```
### Task Lists
Task lists are converted to separate issues:
```markdown
## Setup Tasks
- [ ] Install dependencies
- [x] Configure database
- [ ] Set up CI/CD
```
Creates 3 issues (second one marked as closed).
### Dependencies
Reference other issues in the description:
```markdown
## Implement API
This task requires the database schema to be ready first.
Dependencies:
- blocks: bd-5
- related: bd-10, bd-15
```
The script extracts these and creates dependency records.
## Example
See `example-feature.md` for a complete example.
```bash
# Convert the example
python md2jsonl.py example-feature.md > example-issues.jsonl
# View the output
cat example-issues.jsonl | jq .
# Import into bd
bd import -i example-issues.jsonl
```
## Customization
The script is intentionally simple so you can customize it for your needs:
1. **Different heading levels** - Modify which headings become issues (H1 only? H1-H3?)
2. **Custom metadata** - Parse additional frontmatter fields
3. **Labels** - Extract hashtags or keywords as labels
4. **Epic detection** - Top-level headings become epics
5. **Issue templates** - Map different markdown structures to issue types
## Limitations
This is a simple example, not a production tool:
- Basic YAML parsing (no nested structures)
- Simple dependency extraction (regex-based)
- No validation of referenced issue IDs
- Doesn't handle all markdown edge cases
For production use, you might want to:
- Use a proper YAML parser (`pip install pyyaml`)
- Use a markdown parser (`pip install markdown` or `python-markdown2`)
- Add validation and error handling
- Support more dependency formats
## Philosophy
This example demonstrates the **lightweight extension pattern**:
- ✅ Keep `bd` core focused and minimal
- ✅ Let users customize for their workflows
- ✅ Use existing import infrastructure
- ✅ Easy to understand and modify
Rather than adding markdown support to `bd` core (800+ LOC + dependencies + maintenance), we provide a simple converter that users can adapt.
## Contributing
Have improvements? Found a bug? This is just an example, but contributions are welcome!
Consider:
- Better error messages
- More markdown patterns
- Integration with popular markdown formats
- Support for GFM (GitHub Flavored Markdown) extensions
## See Also
- [bd README](../../README.md) - Main documentation
- [Python Agent Example](../python-agent/) - Full agent workflow
- [JSONL Format](../../TEXT_FORMATS.md) - Understanding bd's JSONL structure

View File

@@ -0,0 +1,49 @@
---
priority: 1
type: feature
assignee: alice
---
# User Authentication System
Implement a complete user authentication system with login, signup, and password recovery.
This is a critical feature for the application. The authentication should be secure and follow best practices.
**Dependencies:**
- blocks: bd-5 (database schema must be ready first)
## Login Flow
Implement the login page with email/password authentication. Should support:
- Email validation
- Password hashing (bcrypt)
- Session management
- Remember me functionality
## Signup Flow
Create new user registration with validation:
- Email uniqueness check
- Password strength requirements
- Email verification
- Terms of service acceptance
## Password Recovery
Allow users to reset forgotten passwords:
- [ ] Send recovery email
- [ ] Generate secure reset tokens
- [x] Create reset password form
- [ ] Expire tokens after 24 hours
## Session Management
Handle user sessions securely:
- JWT tokens
- Refresh token rotation
- Session timeout after 30 days
- Logout functionality
Related to bd-10 (API endpoints) and discovered-from: bd-2 (security audit).

View File

@@ -0,0 +1,253 @@
#!/usr/bin/env python3
"""
Convert markdown files to bd JSONL format.
This is a simple example converter that demonstrates the pattern.
Users can customize this for their specific markdown conventions.
Supported markdown patterns:
1. YAML frontmatter for metadata
2. H1/H2 headings as issue titles
3. Task lists as sub-issues
4. Inline issue references (e.g., "blocks: bd-10")
Usage:
python md2jsonl.py feature.md | bd import
python md2jsonl.py feature.md > issues.jsonl
"""
import json
import re
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import List, Dict, Any, Optional
class MarkdownToIssues:
"""Convert markdown to bd JSONL format."""
def __init__(self, prefix: str = "bd"):
self.prefix = prefix
self.issue_counter = 1
self.issues: List[Dict[str, Any]] = []
def parse_frontmatter(self, content: str) -> tuple[Optional[Dict], str]:
"""Extract YAML frontmatter if present."""
# Simple frontmatter detection (--- ... ---)
if not content.startswith('---\n'):
return None, content
end = content.find('\n---\n', 4)
if end == -1:
return None, content
frontmatter_text = content[4:end]
body = content[end + 5:]
# Parse simple YAML (key: value)
metadata = {}
for line in frontmatter_text.split('\n'):
line = line.strip()
if ':' in line:
key, value = line.split(':', 1)
metadata[key.strip()] = value.strip()
return metadata, body
def extract_issue_from_heading(
self,
heading: str,
level: int,
content: str,
metadata: Optional[Dict] = None
) -> Dict[str, Any]:
"""Create an issue from a markdown heading and its content."""
# Generate ID
issue_id = f"{self.prefix}-{self.issue_counter}"
self.issue_counter += 1
# Extract title (remove markdown formatting)
title = heading.strip('#').strip()
# Parse metadata from frontmatter or defaults
if metadata is None:
metadata = {}
# Build issue
issue = {
"id": issue_id,
"title": title,
"description": content.strip(),
"status": metadata.get("status", "open"),
"priority": int(metadata.get("priority", 2)),
"issue_type": metadata.get("type", "task"),
"created_at": datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'),
"updated_at": datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'),
}
# Optional fields
if "assignee" in metadata:
issue["assignee"] = metadata["assignee"]
if "design" in metadata:
issue["design"] = metadata["design"]
# Extract dependencies from description
dependencies = self.extract_dependencies(content)
if dependencies:
issue["dependencies"] = dependencies
return issue
def extract_dependencies(self, text: str) -> List[Dict[str, str]]:
"""Extract dependency references from text."""
dependencies = []
# Pattern: "blocks: bd-10" or "depends-on: bd-5, bd-6"
# Pattern: "discovered-from: bd-20"
dep_pattern = r'(blocks|related|parent-child|discovered-from):\s*((?:bd-\d+(?:\s*,\s*)?)+)'
for match in re.finditer(dep_pattern, text, re.IGNORECASE):
dep_type = match.group(1).lower()
dep_ids = [id.strip() for id in match.group(2).split(',')]
for dep_id in dep_ids:
dependencies.append({
"issue_id": "", # Will be filled by import
"depends_on_id": dep_id.strip(),
"type": dep_type
})
return dependencies
def parse_task_list(self, content: str) -> List[Dict[str, Any]]:
"""Extract task list items as separate issues."""
issues = []
# Pattern: - [ ] Task or - [x] Task
task_pattern = r'^-\s+\[([ x])\]\s+(.+)$'
for line in content.split('\n'):
match = re.match(task_pattern, line.strip())
if match:
is_done = match.group(1) == 'x'
task_text = match.group(2)
issue_id = f"{self.prefix}-{self.issue_counter}"
self.issue_counter += 1
issue = {
"id": issue_id,
"title": task_text,
"description": "",
"status": "closed" if is_done else "open",
"priority": 2,
"issue_type": "task",
"created_at": datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'),
"updated_at": datetime.now(timezone.utc).isoformat().replace('+00:00', 'Z'),
}
issues.append(issue)
return issues
def parse_markdown(self, content: str, global_metadata: Optional[Dict] = None):
"""Parse markdown content into issues."""
# Extract frontmatter
frontmatter, body = self.parse_frontmatter(content)
# Merge metadata
metadata = global_metadata or {}
if frontmatter:
metadata.update(frontmatter)
# Split by headings
heading_pattern = r'^(#{1,6})\s+(.+)$'
lines = body.split('\n')
current_heading = None
current_level = 0
current_content = []
for line in lines:
match = re.match(heading_pattern, line)
if match:
# Save previous section
if current_heading:
content_text = '\n'.join(current_content)
# Check for task lists
task_issues = self.parse_task_list(content_text)
if task_issues:
self.issues.extend(task_issues)
else:
# Create issue from heading
issue = self.extract_issue_from_heading(
current_heading,
current_level,
content_text,
metadata
)
self.issues.append(issue)
# Start new section
current_level = len(match.group(1))
current_heading = match.group(2)
current_content = []
else:
current_content.append(line)
# Save final section
if current_heading:
content_text = '\n'.join(current_content)
task_issues = self.parse_task_list(content_text)
if task_issues:
self.issues.extend(task_issues)
else:
issue = self.extract_issue_from_heading(
current_heading,
current_level,
content_text,
metadata
)
self.issues.append(issue)
def to_jsonl(self) -> str:
"""Convert issues to JSONL format."""
lines = []
for issue in self.issues:
lines.append(json.dumps(issue, ensure_ascii=False))
return '\n'.join(lines)
def main():
"""Main entry point."""
if len(sys.argv) < 2:
print("Usage: python md2jsonl.py <markdown-file>", file=sys.stderr)
print("", file=sys.stderr)
print("Examples:", file=sys.stderr)
print(" python md2jsonl.py feature.md | bd import", file=sys.stderr)
print(" python md2jsonl.py feature.md > issues.jsonl", file=sys.stderr)
sys.exit(1)
markdown_file = Path(sys.argv[1])
if not markdown_file.exists():
print(f"Error: File not found: {markdown_file}", file=sys.stderr)
sys.exit(1)
# Read markdown
content = markdown_file.read_text()
# Convert to issues
converter = MarkdownToIssues(prefix="bd")
converter.parse_markdown(content)
# Output JSONL
print(converter.to_jsonl())
if __name__ == "__main__":
main()