feat: integrate beads
All checks were successful
Sync Beads to Gitea Issues / sync-beads (push) Successful in 7s
All checks were successful
Sync Beads to Gitea Issues / sync-beads (push) Successful in 7s
This commit is contained in:
parent
829ab46608
commit
f435bf0b09
44
.beads/.gitignore
vendored
Normal file
44
.beads/.gitignore
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# SQLite databases
|
||||||
|
*.db
|
||||||
|
*.db?*
|
||||||
|
*.db-journal
|
||||||
|
*.db-wal
|
||||||
|
*.db-shm
|
||||||
|
|
||||||
|
# Daemon runtime files
|
||||||
|
daemon.lock
|
||||||
|
daemon.log
|
||||||
|
daemon.pid
|
||||||
|
bd.sock
|
||||||
|
sync-state.json
|
||||||
|
last-touched
|
||||||
|
|
||||||
|
# Local version tracking (prevents upgrade notification spam after git ops)
|
||||||
|
.local_version
|
||||||
|
|
||||||
|
# Legacy database files
|
||||||
|
db.sqlite
|
||||||
|
bd.db
|
||||||
|
|
||||||
|
# Worktree redirect file (contains relative path to main repo's .beads/)
|
||||||
|
# Must not be committed as paths would be wrong in other clones
|
||||||
|
redirect
|
||||||
|
|
||||||
|
# Merge artifacts (temporary files from 3-way merge)
|
||||||
|
beads.base.jsonl
|
||||||
|
beads.base.meta.json
|
||||||
|
beads.left.jsonl
|
||||||
|
beads.left.meta.json
|
||||||
|
beads.right.jsonl
|
||||||
|
beads.right.meta.json
|
||||||
|
|
||||||
|
# Sync state (local-only, per-machine)
|
||||||
|
# These files are machine-specific and should not be shared across clones
|
||||||
|
.sync.lock
|
||||||
|
sync_base.jsonl
|
||||||
|
|
||||||
|
# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here.
|
||||||
|
# They would override fork protection in .git/info/exclude, allowing
|
||||||
|
# contributors to accidentally commit upstream issue databases.
|
||||||
|
# The JSONL files (issues.jsonl, interactions.jsonl) and config files
|
||||||
|
# are tracked by git by default since no pattern above ignores them.
|
||||||
81
.beads/README.md
Normal file
81
.beads/README.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# Beads - AI-Native Issue Tracking
|
||||||
|
|
||||||
|
Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
|
||||||
|
|
||||||
|
## What is Beads?
|
||||||
|
|
||||||
|
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
|
||||||
|
|
||||||
|
**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Essential Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create new issues
|
||||||
|
bd create "Add user authentication"
|
||||||
|
|
||||||
|
# View all issues
|
||||||
|
bd list
|
||||||
|
|
||||||
|
# View issue details
|
||||||
|
bd show <issue-id>
|
||||||
|
|
||||||
|
# Update issue status
|
||||||
|
bd update <issue-id> --status in_progress
|
||||||
|
bd update <issue-id> --status done
|
||||||
|
|
||||||
|
# Sync with git remote
|
||||||
|
bd sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with Issues
|
||||||
|
|
||||||
|
Issues in Beads are:
|
||||||
|
- **Git-native**: Stored in `.beads/issues.jsonl` and synced like code
|
||||||
|
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
|
||||||
|
- **Branch-aware**: Issues can follow your branch workflow
|
||||||
|
- **Always in sync**: Auto-syncs with your commits
|
||||||
|
|
||||||
|
## Why Beads?
|
||||||
|
|
||||||
|
✨ **AI-Native Design**
|
||||||
|
- Built specifically for AI-assisted development workflows
|
||||||
|
- CLI-first interface works seamlessly with AI coding agents
|
||||||
|
- No context switching to web UIs
|
||||||
|
|
||||||
|
🚀 **Developer Focused**
|
||||||
|
- Issues live in your repo, right next to your code
|
||||||
|
- Works offline, syncs when you push
|
||||||
|
- Fast, lightweight, and stays out of your way
|
||||||
|
|
||||||
|
🔧 **Git Integration**
|
||||||
|
- Automatic sync with git commits
|
||||||
|
- Branch-aware issue tracking
|
||||||
|
- Intelligent JSONL merge resolution
|
||||||
|
|
||||||
|
## Get Started with Beads
|
||||||
|
|
||||||
|
Try Beads in your own projects:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Beads
|
||||||
|
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
|
||||||
|
|
||||||
|
# Initialize in your repo
|
||||||
|
bd init
|
||||||
|
|
||||||
|
# Create your first issue
|
||||||
|
bd create "Try out Beads"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
|
||||||
|
- **Quick Start Guide**: Run `bd quickstart`
|
||||||
|
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Beads: Issue tracking that moves at the speed of thought* ⚡
|
||||||
62
.beads/config.yaml
Normal file
62
.beads/config.yaml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Beads Configuration File
|
||||||
|
# This file configures default behavior for all bd commands in this repository
|
||||||
|
# All settings can also be set via environment variables (BD_* prefix)
|
||||||
|
# or overridden with command-line flags
|
||||||
|
|
||||||
|
# Issue prefix for this repository (used by bd init)
|
||||||
|
# If not set, bd init will auto-detect from directory name
|
||||||
|
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
|
||||||
|
# issue-prefix: ""
|
||||||
|
|
||||||
|
# Use no-db mode: load from JSONL, no SQLite, write back after each command
|
||||||
|
# When true, bd will use .beads/issues.jsonl as the source of truth
|
||||||
|
# instead of SQLite database
|
||||||
|
# no-db: false
|
||||||
|
|
||||||
|
# Disable daemon for RPC communication (forces direct database access)
|
||||||
|
# no-daemon: false
|
||||||
|
|
||||||
|
# Disable auto-flush of database to JSONL after mutations
|
||||||
|
# no-auto-flush: false
|
||||||
|
|
||||||
|
# Disable auto-import from JSONL when it's newer than database
|
||||||
|
# no-auto-import: false
|
||||||
|
|
||||||
|
# Enable JSON output by default
|
||||||
|
# json: false
|
||||||
|
|
||||||
|
# Default actor for audit trails (overridden by BD_ACTOR or --actor)
|
||||||
|
# actor: ""
|
||||||
|
|
||||||
|
# Path to database (overridden by BEADS_DB or --db)
|
||||||
|
# db: ""
|
||||||
|
|
||||||
|
# Auto-start daemon if not running (can also use BEADS_AUTO_START_DAEMON)
|
||||||
|
# auto-start-daemon: true
|
||||||
|
|
||||||
|
# Debounce interval for auto-flush (can also use BEADS_FLUSH_DEBOUNCE)
|
||||||
|
# flush-debounce: "5s"
|
||||||
|
|
||||||
|
# Git branch for beads commits (bd sync will commit to this branch)
|
||||||
|
# IMPORTANT: Set this for team projects so all clones use the same sync branch.
|
||||||
|
# This setting persists across clones (unlike database config which is gitignored).
|
||||||
|
# Can also use BEADS_SYNC_BRANCH env var for local override.
|
||||||
|
# If not set, bd sync will require you to run 'bd config set sync.branch <branch>'.
|
||||||
|
# sync-branch: "beads-sync"
|
||||||
|
|
||||||
|
# Multi-repo configuration (experimental - bd-307)
|
||||||
|
# Allows hydrating from multiple repositories and routing writes to the correct JSONL
|
||||||
|
# repos:
|
||||||
|
# primary: "." # Primary repo (where this database lives)
|
||||||
|
# additional: # Additional repos to hydrate from (read-only)
|
||||||
|
# - ~/beads-planning # Personal planning repo
|
||||||
|
# - ~/work-planning # Work planning repo
|
||||||
|
|
||||||
|
# Integration settings (access with 'bd config get/set')
|
||||||
|
# These are stored in the database, not in this file:
|
||||||
|
# - jira.url
|
||||||
|
# - jira.project
|
||||||
|
# - linear.url
|
||||||
|
# - linear.api-key
|
||||||
|
# - github.org
|
||||||
|
# - github.repo
|
||||||
0
.beads/interactions.jsonl
Normal file
0
.beads/interactions.jsonl
Normal file
4
.beads/issues.jsonl
Normal file
4
.beads/issues.jsonl
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{"id":"gitea-welcome-0kp","title":"Reverse-sync Test","status":"tombstone","priority":2,"issue_type":"task","owner":"chris@rootiest.com","created_at":"2026-01-19T21:09:52.497127424-05:00","created_by":"rootiest","updated_at":"2026-01-19T21:22:16.221889237-05:00","comments":[{"id":2,"issue_id":"gitea-welcome-0kp","author":"rootiest","text":"It","created_at":"2026-01-20T02:11:02Z"}],"deleted_at":"2026-01-19T21:22:16.221889237-05:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
|
||||||
|
{"id":"gitea-welcome-24k","title":"Reverse-sync Test","status":"tombstone","priority":2,"issue_type":"task","owner":"chris@rootiest.com","created_at":"2026-01-19T21:13:44.940389131-05:00","created_by":"rootiest","updated_at":"2026-01-19T21:22:05.359383908-05:00","deleted_at":"2026-01-19T21:22:05.359383908-05:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
|
||||||
|
{"id":"gitea-welcome-5a4","title":"Test Issue","status":"closed","priority":2,"issue_type":"task","owner":"chris@rootiest.com","created_at":"2026-01-19T19:24:28.988421899-05:00","created_by":"rootiest","updated_at":"2026-01-19T20:08:24.331722606-05:00","closed_at":"2026-01-19T20:08:24.331722606-05:00","close_reason":"Closed","comments":[{"id":1,"issue_id":"gitea-welcome-5a4","author":"rootiest","text":"Completed!","created_at":"2026-01-20T01:07:50Z"},{"id":3,"issue_id":"gitea-welcome-5a4","author":"rootiest","text":"New","created_at":"2026-01-20T02:29:01Z"}]}
|
||||||
|
{"id":"gitea-welcome-d6r","title":"Reverse-sync Test #2","status":"tombstone","priority":2,"issue_type":"task","owner":"chris@rootiest.com","created_at":"2026-01-19T21:13:44.746027824-05:00","created_by":"rootiest","updated_at":"2026-01-19T21:22:10.935659182-05:00","deleted_at":"2026-01-19T21:22:10.935659182-05:00","deleted_by":"batch delete","delete_reason":"batch delete","original_type":"task"}
|
||||||
4
.beads/metadata.json
Normal file
4
.beads/metadata.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"database": "beads.db",
|
||||||
|
"jsonl_export": "issues.jsonl"
|
||||||
|
}
|
||||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
# Use bd merge for beads JSONL files
|
||||||
|
.beads/issues.jsonl merge=beads
|
||||||
110
.gitea/sync_beads.py
Normal file
110
.gitea/sync_beads.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Configuration from Environment
|
||||||
|
TOKEN = os.getenv("GITEA_TOKEN")
|
||||||
|
URL = os.getenv("GITEA_URL")
|
||||||
|
REPO = os.getenv("REPO_NAME")
|
||||||
|
HEADERS = {"Authorization": f"token {TOKEN}", "Content-Type": "application/json"}
|
||||||
|
|
||||||
|
def sync():
|
||||||
|
beads_path = ".beads/issues.jsonl"
|
||||||
|
|
||||||
|
if not os.path.exists(beads_path):
|
||||||
|
print(f"❌ ERROR: {beads_path} not found.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"🔍 Reading Beads from: {beads_path}")
|
||||||
|
|
||||||
|
# 1. Fetch existing issues from Gitea
|
||||||
|
try:
|
||||||
|
api_url = f"{URL}/api/v1/repos/{REPO}/issues?state=all"
|
||||||
|
resp = requests.get(api_url, headers=HEADERS)
|
||||||
|
resp.raise_for_status()
|
||||||
|
# Map by [ID] to identify existing ones
|
||||||
|
existing = {
|
||||||
|
i["title"].split("]")[0][1:]: i for i in resp.json() if "]" in i["title"]
|
||||||
|
}
|
||||||
|
print(f"📡 Found {len(existing)} existing issues in Gitea.")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Gitea API Connection Failed: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 2. Parse the Beads JSONL file
|
||||||
|
processed_count = 0
|
||||||
|
skipped_count = 0
|
||||||
|
deleted_count = 0
|
||||||
|
|
||||||
|
with open(beads_path, "r") as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(line)
|
||||||
|
bid = data.get("id")
|
||||||
|
status = data.get("status")
|
||||||
|
title = data.get("title")
|
||||||
|
itype = data.get("issue_type", "unknown")
|
||||||
|
|
||||||
|
if not bid:
|
||||||
|
print("⚠️ Skipping line: No ID found.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# --- DELETION / TOMBSTONE LOGIC ---
|
||||||
|
if status == "tombstone":
|
||||||
|
if bid in existing:
|
||||||
|
issue_num = existing[bid]['number']
|
||||||
|
print(f"🗑️ Deleting Gitea Issue #{issue_num} (Tombstone found for {bid})")
|
||||||
|
del_resp = requests.delete(f"{URL}/api/v1/repos/{REPO}/issues/{issue_num}", headers=HEADERS)
|
||||||
|
if del_resp.status_code == 204:
|
||||||
|
print(f"✅ Successfully deleted {bid}")
|
||||||
|
deleted_count += 1
|
||||||
|
else:
|
||||||
|
print(f"⚠️ Could not delete {bid}: {del_resp.status_code}")
|
||||||
|
else:
|
||||||
|
print(f"👻 Skipping tombstone: {bid} (Already gone from Gitea)")
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# --- STANDARD SYNC LOGIC ---
|
||||||
|
processed_count += 1
|
||||||
|
gitea_state = "closed" if status in ["closed", "done", "finished"] else "open"
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"title": f"[{bid}] {title}",
|
||||||
|
"body": f"{data.get('description', 'No description provided.')}\n\n---\n**Beads ID:** `{bid}`\n**Type:** `{itype}`",
|
||||||
|
"state": gitea_state,
|
||||||
|
}
|
||||||
|
|
||||||
|
if bid in existing:
|
||||||
|
print(f"✅ Updating {bid} (Issue #{existing[bid]['number']})")
|
||||||
|
requests.patch(
|
||||||
|
f"{URL}/api/v1/repos/{REPO}/issues/{existing[bid]['number']}",
|
||||||
|
headers=HEADERS,
|
||||||
|
json=payload,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(f"➕ Creating New Gitea Issue for {bid}")
|
||||||
|
r = requests.post(
|
||||||
|
f"{URL}/api/v1/repos/{REPO}/issues",
|
||||||
|
headers=HEADERS,
|
||||||
|
json=payload,
|
||||||
|
)
|
||||||
|
r.raise_for_status()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Error processing bead line: {e}")
|
||||||
|
|
||||||
|
print(f"🏁 Finished. Active: {processed_count}, Deleted: {deleted_count}, Skipped: {skipped_count}")
|
||||||
|
|
||||||
|
# Safety check to ensure we didn't process a completely empty file
|
||||||
|
if (processed_count + deleted_count + skipped_count) == 0:
|
||||||
|
print("❌ ERROR: File was found but it was EMPTY.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sync()
|
||||||
27
.gitea/workflows/beads-sync.yml
Normal file
27
.gitea/workflows/beads-sync.yml
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
name: Sync Beads to Gitea Issues
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main # Adjust if your default branch is different
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
sync-beads:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: "3.x"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: pip install requests
|
||||||
|
|
||||||
|
- name: Run Sync Script
|
||||||
|
env:
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||||
|
GITEA_URL: ${{ github.server_url }}
|
||||||
|
REPO_NAME: ${{ github.repository }}
|
||||||
|
run: python .gitea/sync_beads.py
|
||||||
40
AGENTS.md
Normal file
40
AGENTS.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Agent Instructions
|
||||||
|
|
||||||
|
This project uses **bd** (beads) for issue tracking. Run `bd onboard` to get started.
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bd ready # Find available work
|
||||||
|
bd show <id> # View issue details
|
||||||
|
bd update <id> --status in_progress # Claim work
|
||||||
|
bd close <id> # Complete work
|
||||||
|
bd sync # Sync with git
|
||||||
|
```
|
||||||
|
|
||||||
|
## Landing the Plane (Session Completion)
|
||||||
|
|
||||||
|
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
|
||||||
|
|
||||||
|
**MANDATORY WORKFLOW:**
|
||||||
|
|
||||||
|
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
||||||
|
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
||||||
|
3. **Update issue status** - Close finished work, update in-progress items
|
||||||
|
4. **PUSH TO REMOTE** - This is MANDATORY:
|
||||||
|
```bash
|
||||||
|
git pull --rebase
|
||||||
|
bd sync
|
||||||
|
git push
|
||||||
|
git status # MUST show "up to date with origin"
|
||||||
|
```
|
||||||
|
5. **Clean up** - Clear stashes, prune remote branches
|
||||||
|
6. **Verify** - All changes committed AND pushed
|
||||||
|
7. **Hand off** - Provide context for next session
|
||||||
|
|
||||||
|
**CRITICAL RULES:**
|
||||||
|
- Work is NOT complete until `git push` succeeds
|
||||||
|
- NEVER stop before pushing - that leaves work stranded locally
|
||||||
|
- NEVER say "ready to push when you are" - YOU must push
|
||||||
|
- If push fails, resolve and retry until it succeeds
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user