Add project documentation and beads configuration
- Added CLAUDE.md with project overview and architecture details - Added PRD.md with product requirements - Added AGENTS.md for agent workflow documentation - Initialized beads issue tracking system - Added .gitattributes for Git configuration
This commit is contained in:
parent
62e299192d
commit
9f613b1901
12 changed files with 933 additions and 1 deletions
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.
|
||||||
89
.beads/PRIME.md
Normal file
89
.beads/PRIME.md
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
# Beads Workflow Context
|
||||||
|
|
||||||
|
> **Context Recovery**: Run `bd prime` after compaction, clear, or new session
|
||||||
|
> Hooks auto-call this in Claude Code when .beads/ detected
|
||||||
|
|
||||||
|
# 🚨 TWO-PHASE WORKFLOW 🚨
|
||||||
|
|
||||||
|
**This project uses a split workflow:**
|
||||||
|
|
||||||
|
| Phase | Command | Actions |
|
||||||
|
|-------|---------|---------|
|
||||||
|
| **1. Implement** | `/start-issue <id>` | Set status → Plan → Implement |
|
||||||
|
| **2. Finalize** | `/finish-issue` | Commit → Close → Push |
|
||||||
|
|
||||||
|
## What this means for you:
|
||||||
|
|
||||||
|
### ✅ ALLOWED during implementation:
|
||||||
|
- Set ticket to `in_progress`
|
||||||
|
- Read, analyze, plan
|
||||||
|
- Write and modify code
|
||||||
|
- Run tests, build, verify
|
||||||
|
|
||||||
|
### ❌ FORBIDDEN during implementation:
|
||||||
|
- `git add` / `git commit` / `git push`
|
||||||
|
- `bd close`
|
||||||
|
- `bd sync` (syncs commits)
|
||||||
|
|
||||||
|
### When implementation is complete:
|
||||||
|
|
||||||
|
**DO NOT** run the old "Session Close Protocol". Instead say:
|
||||||
|
|
||||||
|
> "✅ Implementation complete. Files changed: [list files]. Run `/finish-issue` when ready to commit and close."
|
||||||
|
|
||||||
|
Then **STOP** and wait for the user.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Rules
|
||||||
|
- Track strategic work in beads (multi-session, dependencies, discovered work)
|
||||||
|
- Use `bd create` for issues, TodoWrite for simple single-session execution
|
||||||
|
- When in doubt, prefer bd—persistence you don't need beats lost context
|
||||||
|
- Session management: check `bd ready` for available work
|
||||||
|
|
||||||
|
## Essential Commands
|
||||||
|
|
||||||
|
### Finding Work
|
||||||
|
- `bd ready` - Show issues ready to work (no blockers)
|
||||||
|
- `bd list --status=open` - All open issues
|
||||||
|
- `bd list --status=in_progress` - Your active work
|
||||||
|
- `bd show <id>` - Detailed issue view with dependencies
|
||||||
|
|
||||||
|
### Creating & Updating
|
||||||
|
- `bd create --title="..." --type=task|bug|feature --priority=2` - New issue
|
||||||
|
- Priority: 0-4 or P0-P4 (0=critical, 2=medium, 4=backlog). NOT "high"/"medium"/"low"
|
||||||
|
- `bd update <id> --status=in_progress` - Claim work
|
||||||
|
- `bd update <id> --assignee=username` - Assign to someone
|
||||||
|
- `bd update <id> --title/--description/--notes/--design` - Update fields inline
|
||||||
|
- `bd close <id>` - Mark complete (⚠️ only in /finish-issue!)
|
||||||
|
- **WARNING**: Do NOT use `bd edit` - it opens $EDITOR which blocks agents
|
||||||
|
|
||||||
|
### Dependencies & Blocking
|
||||||
|
- `bd dep add <issue> <depends-on>` - Add dependency
|
||||||
|
- `bd blocked` - Show all blocked issues
|
||||||
|
- `bd show <id>` - See what's blocking/blocked by this issue
|
||||||
|
|
||||||
|
### Project Health
|
||||||
|
- `bd stats` - Project statistics
|
||||||
|
- `bd doctor` - Check for issues
|
||||||
|
|
||||||
|
## Workflow Summary
|
||||||
|
|
||||||
|
```
|
||||||
|
/start-issue <id>
|
||||||
|
│
|
||||||
|
├── bd update <id> --status=in_progress
|
||||||
|
├── [Plan mode]
|
||||||
|
├── [Implement]
|
||||||
|
└── STOP → "Implementation complete"
|
||||||
|
|
||||||
|
... user reviews ...
|
||||||
|
|
||||||
|
/finish-issue
|
||||||
|
│
|
||||||
|
├── bd close <id>
|
||||||
|
├── git add -A
|
||||||
|
├── git commit -m "..."
|
||||||
|
├── bd sync
|
||||||
|
└── Done!
|
||||||
|
```
|
||||||
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
|
||||||
6
.beads/export-state/6feeba40f0df6736.json
Normal file
6
.beads/export-state/6feeba40f0df6736.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"worktree_root": "/home/oli/Dev/bookstack-api",
|
||||||
|
"last_export_commit": "a6adae98dd1697df76e68e562a4f84e7a38aa52d",
|
||||||
|
"last_export_time": "2026-01-30T09:36:58.353806809+01:00",
|
||||||
|
"jsonl_hash": "274bd67ea9399c10b88ec481bf082fe529a2d0cff39af1f6cbe89339a2f57f75"
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
{"id":"bookstack-api-8op","title":"Implement Client and Config structs","description":"Create the main Client struct and Config for client initialization with token-based authentication.\n\n## Requirements\nFrom PRD Section 5 (API \u0026 Interface-Spezifikation):\n\n```go\ntype Config struct {\n BaseURL string // e.g. \"https://docs.jakoubek.net\"\n TokenID string // API Token ID\n TokenSecret string // API Token Secret\n HTTPClient *http.Client // optional, for tests/mocking\n}\n\ntype Client struct {\n Books *BooksService\n Pages *PagesService\n Chapters *ChaptersService\n Shelves *ShelvesService\n Search *SearchService\n}\n\nfunc NewClient(cfg Config) *Client\n```\n\n## Authentication\nHeader format: `Authorization: Token \u003ctoken_id\u003e:\u003ctoken_secret\u003e`\n\n## Acceptance Criteria\n- [ ] Config struct with BaseURL, TokenID, TokenSecret, optional HTTPClient\n- [ ] Client struct with service fields (initially nil)\n- [ ] NewClient() constructor validates required fields\n- [ ] Default http.Client used when not provided\n- [ ] Unit tests for NewClient()","status":"open","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:06.037277135+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:06.037277135+01:00"}
|
{"id":"bookstack-api-8op","title":"Implement Client and Config structs","description":"Create the main Client struct and Config for client initialization with token-based authentication.\n\n## Requirements\nFrom PRD Section 5 (API \u0026 Interface-Spezifikation):\n\n```go\ntype Config struct {\n BaseURL string // e.g. \"https://docs.jakoubek.net\"\n TokenID string // API Token ID\n TokenSecret string // API Token Secret\n HTTPClient *http.Client // optional, for tests/mocking\n}\n\ntype Client struct {\n Books *BooksService\n Pages *PagesService\n Chapters *ChaptersService\n Shelves *ShelvesService\n Search *SearchService\n}\n\nfunc NewClient(cfg Config) *Client\n```\n\n## Authentication\nHeader format: `Authorization: Token \u003ctoken_id\u003e:\u003ctoken_secret\u003e`\n\n## Acceptance Criteria\n- [ ] Config struct with BaseURL, TokenID, TokenSecret, optional HTTPClient\n- [ ] Client struct with service fields (initially nil)\n- [ ] NewClient() constructor validates required fields\n- [ ] Default http.Client used when not provided\n- [ ] Unit tests for NewClient()","status":"open","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:06.037277135+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:06.037277135+01:00"}
|
||||||
{"id":"bookstack-api-9at","title":"Implement Pages Delete","description":"Implement delete operation for Pages.\n\n## Requirements\nFrom PRD Section 5:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| Delete | DELETE /api/pages/{id} | Delete page |\n\n## API Method\n```go\nfunc (s *PagesService) Delete(ctx context.Context, id int) error\n```\n\n## Technical Details\n- Returns no content on success\n- 404 if page not found\n- May require appropriate permissions\n\n## Acceptance Criteria\n- [ ] Delete() removes page by ID\n- [ ] Returns nil on success\n- [ ] Proper error handling (404 -\u003e ErrNotFound, 403 -\u003e ErrForbidden)\n- [ ] Unit tests with mock server","status":"open","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:53.980583894+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:53.980583894+01:00"}
|
{"id":"bookstack-api-9at","title":"Implement Pages Delete","description":"Implement delete operation for Pages.\n\n## Requirements\nFrom PRD Section 5:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| Delete | DELETE /api/pages/{id} | Delete page |\n\n## API Method\n```go\nfunc (s *PagesService) Delete(ctx context.Context, id int) error\n```\n\n## Technical Details\n- Returns no content on success\n- 404 if page not found\n- May require appropriate permissions\n\n## Acceptance Criteria\n- [ ] Delete() removes page by ID\n- [ ] Returns nil on success\n- [ ] Proper error handling (404 -\u003e ErrNotFound, 403 -\u003e ErrForbidden)\n- [ ] Unit tests with mock server","status":"open","priority":2,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:53.980583894+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:53.980583894+01:00"}
|
||||||
{"id":"bookstack-api-9xo","title":"Implement BooksService (List, Get)","description":"Implement the BooksService with List and Get operations.\n\n## Requirements\nFrom PRD Section 5:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| List | GET /api/books | All books |\n| Get | GET /api/books/{id} | Single book |\n\n## API Methods\n```go\ntype BooksService struct {\n client *Client\n}\n\nfunc (s *BooksService) List(ctx context.Context, opts *ListOptions) ([]*Book, error)\nfunc (s *BooksService) Get(ctx context.Context, id int) (*Book, error)\n```\n\n## ListOptions\nSupport pagination parameters:\n- count (max 500)\n- offset\n- sort (+name, -created_at, etc.)\n- filter[field]\n\n## Acceptance Criteria\n- [ ] BooksService struct created\n- [ ] List() returns paginated books\n- [ ] Get() returns single book by ID\n- [ ] Proper error handling (404 -\u003e ErrNotFound)\n- [ ] Unit tests with mock server","status":"open","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:30.949469353+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:30.949469353+01:00"}
|
{"id":"bookstack-api-9xo","title":"Implement BooksService (List, Get)","description":"Implement the BooksService with List and Get operations.\n\n## Requirements\nFrom PRD Section 5:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| List | GET /api/books | All books |\n| Get | GET /api/books/{id} | Single book |\n\n## API Methods\n```go\ntype BooksService struct {\n client *Client\n}\n\nfunc (s *BooksService) List(ctx context.Context, opts *ListOptions) ([]*Book, error)\nfunc (s *BooksService) Get(ctx context.Context, id int) (*Book, error)\n```\n\n## ListOptions\nSupport pagination parameters:\n- count (max 500)\n- offset\n- sort (+name, -created_at, etc.)\n- filter[field]\n\n## Acceptance Criteria\n- [ ] BooksService struct created\n- [ ] List() returns paginated books\n- [ ] Get() returns single book by ID\n- [ ] Proper error handling (404 -\u003e ErrNotFound)\n- [ ] Unit tests with mock server","status":"open","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:30.949469353+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:30.949469353+01:00"}
|
||||||
{"id":"bookstack-api-adp","title":"Set up Go module and project structure","description":"Initialize the Go module and create the basic project structure as defined in the PRD.\n\n## Requirements\n- Initialize go.mod with module name\n- Create placeholder files for the flat package structure:\n - bookstack.go (Client, Config, NewClient)\n - types.go (data structures)\n - errors.go (error types)\n - http.go (HTTP helpers)\n- Set up .gitignore for Go projects\n\n## Technical Details\n- Go 1.21+ required\n- Zero external dependencies (standard library only)\n- Module should be publishable as standalone Go module\n\n## Acceptance Criteria\n- [ ] go.mod exists with proper module name\n- [ ] Basic file structure created\n- [ ] `go build ./...` succeeds\n- [ ] .gitignore includes Go-specific patterns","status":"in_progress","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:05.818311718+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-30T09:33:48.689831698+01:00"}
|
{"id":"bookstack-api-adp","title":"Set up Go module and project structure","description":"Initialize the Go module and create the basic project structure as defined in the PRD.\n\n## Requirements\n- Initialize go.mod with module name\n- Create placeholder files for the flat package structure:\n - bookstack.go (Client, Config, NewClient)\n - types.go (data structures)\n - errors.go (error types)\n - http.go (HTTP helpers)\n- Set up .gitignore for Go projects\n\n## Technical Details\n- Go 1.21+ required\n- Zero external dependencies (standard library only)\n- Module should be publishable as standalone Go module\n\n## Acceptance Criteria\n- [ ] go.mod exists with proper module name\n- [ ] Basic file structure created\n- [ ] `go build ./...` succeeds\n- [ ] .gitignore includes Go-specific patterns","status":"closed","priority":0,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:05.818311718+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-30T09:38:24.407997076+01:00","closed_at":"2026-01-30T09:38:24.407997076+01:00","close_reason":"Closed"}
|
||||||
{"id":"bookstack-api-bu8","title":"Add GoDoc documentation for all public APIs","description":"Ensure all exported types and functions have proper GoDoc comments.\n\n## Requirements\nFrom PRD Section 8 (Definition of Done):\n- Alle public APIs dokumentiert (GoDoc)\n\n## Documentation Standards\n- Package-level doc comment in bookstack.go\n- All exported types documented\n- All exported functions documented\n- All exported constants/variables documented\n- Include usage examples in doc comments where helpful\n\n## Example\n```go\n// Client is the Bookstack API client. Use NewClient to create a new instance.\n// Client is safe for concurrent use.\ntype Client struct {\n // Books provides access to the Books API.\n Books *BooksService\n ...\n}\n\n// NewClient creates a new Bookstack API client with the provided configuration.\n// BaseURL, TokenID, and TokenSecret are required.\n//\n// Example:\n//\n// client := bookstack.NewClient(bookstack.Config{\n// BaseURL: \"https://docs.example.com\",\n// TokenID: \"token-id\",\n// TokenSecret: \"token-secret\",\n// })\nfunc NewClient(cfg Config) *Client\n```\n\n## Acceptance Criteria\n- [ ] Package documentation present\n- [ ] All exported types documented\n- [ ] All exported functions documented\n- [ ] Documentation renders correctly in pkg.go.dev style\n- [ ] No golint documentation warnings","status":"open","priority":1,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:40:24.752365504+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:40:24.752365504+01:00"}
|
{"id":"bookstack-api-bu8","title":"Add GoDoc documentation for all public APIs","description":"Ensure all exported types and functions have proper GoDoc comments.\n\n## Requirements\nFrom PRD Section 8 (Definition of Done):\n- Alle public APIs dokumentiert (GoDoc)\n\n## Documentation Standards\n- Package-level doc comment in bookstack.go\n- All exported types documented\n- All exported functions documented\n- All exported constants/variables documented\n- Include usage examples in doc comments where helpful\n\n## Example\n```go\n// Client is the Bookstack API client. Use NewClient to create a new instance.\n// Client is safe for concurrent use.\ntype Client struct {\n // Books provides access to the Books API.\n Books *BooksService\n ...\n}\n\n// NewClient creates a new Bookstack API client with the provided configuration.\n// BaseURL, TokenID, and TokenSecret are required.\n//\n// Example:\n//\n// client := bookstack.NewClient(bookstack.Config{\n// BaseURL: \"https://docs.example.com\",\n// TokenID: \"token-id\",\n// TokenSecret: \"token-secret\",\n// })\nfunc NewClient(cfg Config) *Client\n```\n\n## Acceptance Criteria\n- [ ] Package documentation present\n- [ ] All exported types documented\n- [ ] All exported functions documented\n- [ ] Documentation renders correctly in pkg.go.dev style\n- [ ] No golint documentation warnings","status":"open","priority":1,"issue_type":"task","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:40:24.752365504+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:40:24.752365504+01:00"}
|
||||||
{"id":"bookstack-api-cpg","title":"Implement PagesService (List, Get)","description":"Implement the PagesService with List and Get operations.\n\n## Requirements\nFrom PRD Section 5:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| List | GET /api/pages | All pages |\n| Get | GET /api/pages/{id} | Single page |\n\n## API Methods\n```go\ntype PagesService struct {\n client *Client\n}\n\nfunc (s *PagesService) List(ctx context.Context, opts *ListOptions) ([]*Page, error)\nfunc (s *PagesService) Get(ctx context.Context, id int) (*Page, error)\n```\n\n## Page Content\nThe Get response includes:\n- HTML content\n- RawHTML content \n- Markdown content (if available)\n\n## Acceptance Criteria\n- [ ] PagesService struct created\n- [ ] List() returns paginated pages\n- [ ] Get() returns single page by ID with content\n- [ ] Proper error handling (404 -\u003e ErrNotFound)\n- [ ] Unit tests with mock server","status":"open","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:31.188568742+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:31.188568742+01:00"}
|
{"id":"bookstack-api-cpg","title":"Implement PagesService (List, Get)","description":"Implement the PagesService with List and Get operations.\n\n## Requirements\nFrom PRD Section 5:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| List | GET /api/pages | All pages |\n| Get | GET /api/pages/{id} | Single page |\n\n## API Methods\n```go\ntype PagesService struct {\n client *Client\n}\n\nfunc (s *PagesService) List(ctx context.Context, opts *ListOptions) ([]*Page, error)\nfunc (s *PagesService) Get(ctx context.Context, id int) (*Page, error)\n```\n\n## Page Content\nThe Get response includes:\n- HTML content\n- RawHTML content \n- Markdown content (if available)\n\n## Acceptance Criteria\n- [ ] PagesService struct created\n- [ ] List() returns paginated pages\n- [ ] Get() returns single page by ID with content\n- [ ] Proper error handling (404 -\u003e ErrNotFound)\n- [ ] Unit tests with mock server","status":"open","priority":0,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:31.188568742+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:31.188568742+01:00"}
|
||||||
{"id":"bookstack-api-d2c","title":"Implement Pages Create and Update","description":"Implement write operations for Pages: Create and Update.\n\n## Requirements\nFrom PRD Section 5:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| Create | POST /api/pages | Create page |\n| Update | PUT /api/pages/{id} | Update page |\n\n## API Methods\n```go\ntype PageCreateRequest struct {\n BookID int `json:\"book_id\"`\n ChapterID int `json:\"chapter_id,omitempty\"`\n Name string `json:\"name\"`\n HTML string `json:\"html,omitempty\"`\n Markdown string `json:\"markdown,omitempty\"`\n}\n\ntype PageUpdateRequest struct {\n Name string `json:\"name,omitempty\"`\n HTML string `json:\"html,omitempty\"`\n Markdown string `json:\"markdown,omitempty\"`\n}\n\nfunc (s *PagesService) Create(ctx context.Context, req *PageCreateRequest) (*Page, error)\nfunc (s *PagesService) Update(ctx context.Context, id int, req *PageUpdateRequest) (*Page, error)\n```\n\n## Workflow (from PRD)\n1. Get page\n2. Edit content locally\n3. Update page\n\n## Acceptance Criteria\n- [ ] PageCreateRequest and PageUpdateRequest structs\n- [ ] Create() creates new page in book or chapter\n- [ ] Update() modifies existing page\n- [ ] Returns updated Page object\n- [ ] Proper error handling (400 -\u003e ErrBadRequest)\n- [ ] Unit tests with mock server","status":"open","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:53.734987403+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:53.734987403+01:00"}
|
{"id":"bookstack-api-d2c","title":"Implement Pages Create and Update","description":"Implement write operations for Pages: Create and Update.\n\n## Requirements\nFrom PRD Section 5:\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| Create | POST /api/pages | Create page |\n| Update | PUT /api/pages/{id} | Update page |\n\n## API Methods\n```go\ntype PageCreateRequest struct {\n BookID int `json:\"book_id\"`\n ChapterID int `json:\"chapter_id,omitempty\"`\n Name string `json:\"name\"`\n HTML string `json:\"html,omitempty\"`\n Markdown string `json:\"markdown,omitempty\"`\n}\n\ntype PageUpdateRequest struct {\n Name string `json:\"name,omitempty\"`\n HTML string `json:\"html,omitempty\"`\n Markdown string `json:\"markdown,omitempty\"`\n}\n\nfunc (s *PagesService) Create(ctx context.Context, req *PageCreateRequest) (*Page, error)\nfunc (s *PagesService) Update(ctx context.Context, id int, req *PageUpdateRequest) (*Page, error)\n```\n\n## Workflow (from PRD)\n1. Get page\n2. Edit content locally\n3. Update page\n\n## Acceptance Criteria\n- [ ] PageCreateRequest and PageUpdateRequest structs\n- [ ] Create() creates new page in book or chapter\n- [ ] Update() modifies existing page\n- [ ] Returns updated Page object\n- [ ] Proper error handling (400 -\u003e ErrBadRequest)\n- [ ] Unit tests with mock server","status":"open","priority":1,"issue_type":"feature","owner":"mail@oliverjakoubek.de","created_at":"2026-01-28T09:39:53.734987403+01:00","created_by":"Oliver Jakoubek","updated_at":"2026-01-28T09:39:53.734987403+01:00"}
|
||||||
|
|
|
||||||
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
|
||||||
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
|
||||||
|
|
||||||
102
CLAUDE.md
Normal file
102
CLAUDE.md
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
A Go library for the Bookstack REST API. Primary consumers are AI agents via CLI (with `--json` flag) and Go developers needing programmatic Bookstack access.
|
||||||
|
|
||||||
|
## Build & Test Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
go build ./...
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# Run single test
|
||||||
|
go test -run TestName ./...
|
||||||
|
|
||||||
|
# Run tests with coverage
|
||||||
|
go test -cover ./...
|
||||||
|
|
||||||
|
# Lint (if golangci-lint is installed)
|
||||||
|
golangci-lint run
|
||||||
|
|
||||||
|
# Format code
|
||||||
|
gofmt -w .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Flat Package Structure
|
||||||
|
|
||||||
|
All code lives in the root package `bookstack`. No subpackages.
|
||||||
|
|
||||||
|
```
|
||||||
|
bookstack.go # Client, Config, NewClient()
|
||||||
|
books.go # BooksService
|
||||||
|
pages.go # PagesService
|
||||||
|
chapters.go # ChaptersService
|
||||||
|
shelves.go # ShelvesService
|
||||||
|
search.go # SearchService
|
||||||
|
types.go # All data structures (Book, Page, Chapter, Shelf, SearchResult)
|
||||||
|
errors.go # Error types (ErrNotFound, ErrUnauthorized, APIError, etc.)
|
||||||
|
http.go # HTTP helpers, request building
|
||||||
|
iterator.go # Pagination iterator using Go 1.23+ iter.Seq
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client Pattern
|
||||||
|
|
||||||
|
Services are attached to the main `Client` struct:
|
||||||
|
|
||||||
|
```go
|
||||||
|
client := bookstack.NewClient(bookstack.Config{
|
||||||
|
BaseURL: "https://docs.example.com",
|
||||||
|
TokenID: os.Getenv("BOOKSTACK_TOKEN_ID"),
|
||||||
|
TokenSecret: os.Getenv("BOOKSTACK_TOKEN_SECRET"),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Access services via client
|
||||||
|
books, err := client.Books.List(ctx, nil)
|
||||||
|
page, err := client.Pages.Get(ctx, 123)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pagination via Iterators
|
||||||
|
|
||||||
|
Use Go 1.23+ iterator pattern for list operations:
|
||||||
|
|
||||||
|
```go
|
||||||
|
for book, err := range client.Books.ListAll(ctx) {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// process book
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
Use sentinel errors with `errors.Is()`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
if errors.Is(err, bookstack.ErrNotFound) {
|
||||||
|
// handle 404
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`APIError` provides detailed information when needed.
|
||||||
|
|
||||||
|
## Key Design Decisions
|
||||||
|
|
||||||
|
- **Zero external dependencies** - Only Go standard library
|
||||||
|
- **Go 1.21+** required
|
||||||
|
- **No caching/rate-limiting** - Caller responsibility
|
||||||
|
- **Token auth** via `Authorization: Token <id>:<secret>` header
|
||||||
|
- **Bookstack hierarchy**: Shelf → Book → Chapter → Page
|
||||||
|
|
||||||
|
## Bookstack API Reference
|
||||||
|
|
||||||
|
- Rate limit: 180 req/min (server configurable)
|
||||||
|
- Pagination: `count` (max 500), `offset`, `sort`, `filter[field]`
|
||||||
498
PRD.md
Normal file
498
PRD.md
Normal file
|
|
@ -0,0 +1,498 @@
|
||||||
|
# Product Requirements Document: bookstack-api
|
||||||
|
|
||||||
|
## 1. Projektübersicht
|
||||||
|
|
||||||
|
- **Ziel und Vision:**
|
||||||
|
Eine Go-Library für die Bookstack REST-API, die programmatischen Zugriff auf das Dokumentationssystem ermöglicht. Die Library wird in hqcli integriert und soll veröffentlichungsfähig sein.
|
||||||
|
|
||||||
|
- **Zielgruppe:**
|
||||||
|
- Primär: AI-Agenten (via hqcli mit `--json` Flag)
|
||||||
|
- Sekundär: Go-Entwickler, die Bookstack programmatisch nutzen wollen
|
||||||
|
|
||||||
|
- **Erfolgs-Metriken:**
|
||||||
|
- Vollständige Abdeckung der Kern-API (Books, Pages, Search)
|
||||||
|
- Nutzbar in hqcli für AI-gestützte Dokumentationssuche
|
||||||
|
- Veröffentlichung als eigenständiges Go-Modul
|
||||||
|
|
||||||
|
- **Projektscope:**
|
||||||
|
- **In Scope:** REST-API-Client für Bookstack, Iterator-basierte Pagination, Export-Funktionen
|
||||||
|
- **Out of Scope:** Webhooks, Caching, Admin-Funktionen (Users, Roles)
|
||||||
|
|
||||||
|
## 2. Funktionale Anforderungen
|
||||||
|
|
||||||
|
### Kern-Features
|
||||||
|
|
||||||
|
| Feature | Priorität | Version |
|
||||||
|
|---------|-----------|---------|
|
||||||
|
| Books: List, Get | P0 | v0.1 |
|
||||||
|
| Pages: List, Get | P0 | v0.1 |
|
||||||
|
| Search: All | P0 | v0.1 |
|
||||||
|
| Pages: Export (Markdown, PDF) | P1 | v0.2 |
|
||||||
|
| Chapters: List, Get | P1 | v0.2 |
|
||||||
|
| Shelves: List, Get | P1 | v0.2 |
|
||||||
|
| Pages: Create, Update | P1 | v0.3 |
|
||||||
|
| Pages: Delete | P2 | v0.4 |
|
||||||
|
| Attachments: CRUD | P2 | v0.4 |
|
||||||
|
| Comments: CRUD | P3 | v0.5 |
|
||||||
|
|
||||||
|
### User Stories
|
||||||
|
|
||||||
|
**US1: Dokumentation durchsuchen (AI-Agent)**
|
||||||
|
> Als AI-Agent möchte ich die Bookstack-Dokumentation durchsuchen können, um relevante Seiten für Benutzeranfragen zu finden.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hqcli docs search "deployment" --json
|
||||||
|
```
|
||||||
|
|
||||||
|
**US2: Seite abrufen**
|
||||||
|
> Als Benutzer möchte ich eine Dokumentationsseite anzeigen können, um deren Inhalt zu lesen.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hqcli docs page 123
|
||||||
|
hqcli docs page deployment-guide # via Slug
|
||||||
|
```
|
||||||
|
|
||||||
|
**US3: Bücher auflisten**
|
||||||
|
> Als Benutzer möchte ich alle verfügbaren Bücher sehen, um die Dokumentationsstruktur zu verstehen.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hqcli docs books --json
|
||||||
|
```
|
||||||
|
|
||||||
|
**US4: Seite exportieren**
|
||||||
|
> Als Benutzer möchte ich eine Seite als Markdown oder PDF exportieren können.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hqcli docs page 123 --export=md > page.md
|
||||||
|
hqcli docs page 123 --export=pdf > page.pdf
|
||||||
|
```
|
||||||
|
|
||||||
|
**US5: Seite im Browser öffnen**
|
||||||
|
> Als Benutzer möchte ich eine Seite schnell im Browser öffnen können.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hqcli docs open 123
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detaillierte Workflows
|
||||||
|
|
||||||
|
**Workflow: Suche und Anzeige**
|
||||||
|
```
|
||||||
|
1. Benutzer/Agent führt Suche aus
|
||||||
|
2. API gibt Liste von Treffern zurück (ID, Typ, Name, Preview)
|
||||||
|
3. Benutzer/Agent wählt Treffer aus
|
||||||
|
4. Seite wird abgerufen und angezeigt (Markdown oder JSON)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Workflow: Seite bearbeiten (v0.3)**
|
||||||
|
```
|
||||||
|
1. Seite abrufen (Get)
|
||||||
|
2. Inhalt lokal bearbeiten
|
||||||
|
3. Seite aktualisieren (Update)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Feature-Prioritäten
|
||||||
|
|
||||||
|
- **Must-have (v1):** List, Get, Search für Books/Pages
|
||||||
|
- **Should-have (v1):** Export Markdown/PDF, Chapters, Shelves
|
||||||
|
- **Nice-to-have (v2):** Create, Update, Delete
|
||||||
|
- **Future:** Attachments, Comments, Image Gallery
|
||||||
|
|
||||||
|
## 3. Technische Anforderungen
|
||||||
|
|
||||||
|
- **Performance-Ziele:**
|
||||||
|
- API-Calls < 500ms (abhängig von Netzwerk)
|
||||||
|
- Iterator verarbeitet 10.000+ Einträge ohne Memory-Probleme
|
||||||
|
|
||||||
|
- **Concurrent User-Kapazität:**
|
||||||
|
Nicht zutreffend (Library, kein Server)
|
||||||
|
|
||||||
|
- **Real-time Features:**
|
||||||
|
Nicht zutreffend
|
||||||
|
|
||||||
|
- **Sicherheitsstandards:**
|
||||||
|
- Token-basierte Authentifizierung (Token ID + Secret)
|
||||||
|
- Keine Speicherung von Credentials (Aufrufer-Verantwortung)
|
||||||
|
|
||||||
|
- **Compliance-Vorgaben:**
|
||||||
|
Keine speziellen
|
||||||
|
|
||||||
|
- **Plattform-Support:**
|
||||||
|
- Go 1.21+
|
||||||
|
- Linux, macOS, Windows
|
||||||
|
|
||||||
|
## 4. Datenarchitektur
|
||||||
|
|
||||||
|
*Nicht zutreffend – keine eigene Datenhaltung*
|
||||||
|
|
||||||
|
### Bookstack-Hierarchie (extern)
|
||||||
|
|
||||||
|
```
|
||||||
|
Shelf (Regal)
|
||||||
|
└── Book (Buch)
|
||||||
|
├── Chapter (Kapitel)
|
||||||
|
│ └── Page (Seite)
|
||||||
|
└── Page (Seite)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Datenstrukturen
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Book repräsentiert ein Bookstack-Buch
|
||||||
|
type Book struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
CreatedBy int `json:"created_by"`
|
||||||
|
UpdatedBy int `json:"updated_by"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page repräsentiert eine Bookstack-Seite
|
||||||
|
type Page struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
BookID int `json:"book_id"`
|
||||||
|
ChapterID int `json:"chapter_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
HTML string `json:"html"`
|
||||||
|
RawHTML string `json:"raw_html"`
|
||||||
|
Markdown string `json:"markdown"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chapter repräsentiert ein Bookstack-Kapitel
|
||||||
|
type Chapter struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
BookID int `json:"book_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shelf repräsentiert ein Bookstack-Regal
|
||||||
|
type Shelf struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchResult repräsentiert ein Suchergebnis
|
||||||
|
type SearchResult struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Type string `json:"type"` // page, chapter, book, bookshelf
|
||||||
|
URL string `json:"url"`
|
||||||
|
Preview string `json:"preview"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. API & Interface-Spezifikation
|
||||||
|
|
||||||
|
### Client-Initialisierung
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Config struct {
|
||||||
|
BaseURL string // z.B. "https://docs.jakoubek.net"
|
||||||
|
TokenID string // API Token ID
|
||||||
|
TokenSecret string // API Token Secret
|
||||||
|
HTTPClient *http.Client // optional, für Tests/Mocking
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(cfg Config) *Client
|
||||||
|
```
|
||||||
|
|
||||||
|
**Beispiel:**
|
||||||
|
```go
|
||||||
|
client := bookstack.NewClient(bookstack.Config{
|
||||||
|
BaseURL: "https://docs.jakoubek.net",
|
||||||
|
TokenID: os.Getenv("BOOKSTACK_TOKEN_ID"),
|
||||||
|
TokenSecret: os.Getenv("BOOKSTACK_TOKEN_SECRET"),
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Service-Struktur
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Client struct {
|
||||||
|
Books *BooksService
|
||||||
|
Pages *PagesService
|
||||||
|
Chapters *ChaptersService
|
||||||
|
Shelves *ShelvesService
|
||||||
|
Search *SearchService
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### REST-Endpoints (Bookstack API)
|
||||||
|
|
||||||
|
| Service | Methode | Endpoint | Beschreibung |
|
||||||
|
|---------|---------|----------|--------------|
|
||||||
|
| Books | List | GET /api/books | Alle Bücher |
|
||||||
|
| Books | Get | GET /api/books/{id} | Einzelnes Buch |
|
||||||
|
| Pages | List | GET /api/pages | Alle Seiten |
|
||||||
|
| Pages | Get | GET /api/pages/{id} | Einzelne Seite |
|
||||||
|
| Pages | Create | POST /api/pages | Seite erstellen |
|
||||||
|
| Pages | Update | PUT /api/pages/{id} | Seite aktualisieren |
|
||||||
|
| Pages | Delete | DELETE /api/pages/{id} | Seite löschen |
|
||||||
|
| Pages | ExportMD | GET /api/pages/{id}/export/markdown | Markdown-Export |
|
||||||
|
| Pages | ExportPDF | GET /api/pages/{id}/export/pdf | PDF-Export |
|
||||||
|
| Chapters | List | GET /api/chapters | Alle Kapitel |
|
||||||
|
| Chapters | Get | GET /api/chapters/{id} | Einzelnes Kapitel |
|
||||||
|
| Shelves | List | GET /api/shelves | Alle Regale |
|
||||||
|
| Shelves | Get | GET /api/shelves/{id} | Einzelnes Regal |
|
||||||
|
| Search | All | GET /api/search?query=... | Volltextsuche |
|
||||||
|
|
||||||
|
### Authentifizierung
|
||||||
|
|
||||||
|
```
|
||||||
|
Authorization: Token <token_id>:<token_secret>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pagination (Iterator-Pattern)
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ListAll gibt einen Iterator über alle Einträge zurück
|
||||||
|
// Nutzt Go 1.23+ iter.Seq oder eigene Implementation
|
||||||
|
func (s *BooksService) ListAll(ctx context.Context) iter.Seq2[*Book, error]
|
||||||
|
|
||||||
|
// Nutzung:
|
||||||
|
for book, err := range client.Books.ListAll(ctx) {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(book.Name)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Begründung:** Iterator-Pattern ist Go-idiomatisch (ab 1.23), Memory-effizient und ermöglicht frühen Abbruch.
|
||||||
|
|
||||||
|
### Rate Limiting
|
||||||
|
|
||||||
|
- Bookstack: 180 Requests/Minute (default)
|
||||||
|
- **Keine Library-interne Behandlung** – Aufrufer muss Rate-Limiting selbst handhaben
|
||||||
|
- Bei 429-Response: `ErrRateLimited` zurückgeben
|
||||||
|
|
||||||
|
## 6. Benutzeroberfläche
|
||||||
|
|
||||||
|
*Nicht zutreffend – Library ohne UI*
|
||||||
|
|
||||||
|
### hqcli-Integration (separates Projekt)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Bücher auflisten
|
||||||
|
hqcli docs books
|
||||||
|
hqcli docs books --json
|
||||||
|
|
||||||
|
# Seiten eines Buchs
|
||||||
|
hqcli docs pages --book=<id|slug>
|
||||||
|
|
||||||
|
# Seite anzeigen
|
||||||
|
hqcli docs page <id|slug>
|
||||||
|
hqcli docs page <id> --json
|
||||||
|
|
||||||
|
# Seite exportieren
|
||||||
|
hqcli docs page <id> --export=md
|
||||||
|
hqcli docs page <id> --export=pdf
|
||||||
|
|
||||||
|
# Suche
|
||||||
|
hqcli docs search "query"
|
||||||
|
hqcli docs search "query" --json
|
||||||
|
|
||||||
|
# Im Browser öffnen
|
||||||
|
hqcli docs open <id|slug>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Output-Priorität:** `--json` für AI-Agenten ist Hauptanwendungsfall
|
||||||
|
|
||||||
|
## 7. Nicht-funktionale Anforderungen
|
||||||
|
|
||||||
|
- **Verfügbarkeit:**
|
||||||
|
Nicht zutreffend (Library)
|
||||||
|
|
||||||
|
- **Dependencies:**
|
||||||
|
Nur Go-Standardbibliothek (net/http, encoding/json, etc.)
|
||||||
|
|
||||||
|
- **Backward Compatibility:**
|
||||||
|
Semantic Versioning (v0.x während Entwicklung, v1.x nach Stabilisierung)
|
||||||
|
|
||||||
|
- **Logging-Strategie:**
|
||||||
|
Keine eigene Logging – Fehler werden als `error` zurückgegeben
|
||||||
|
|
||||||
|
- **Konfiguration:**
|
||||||
|
Via `Config`-Struct bei Client-Erstellung
|
||||||
|
|
||||||
|
## 8. Qualitätssicherung
|
||||||
|
|
||||||
|
### Definition of Done
|
||||||
|
|
||||||
|
- [ ] Alle public APIs dokumentiert (GoDoc)
|
||||||
|
- [ ] Unit-Tests für alle Services (≥80% Coverage)
|
||||||
|
- [ ] Integration-Tests gegen Mock-Server
|
||||||
|
- [ ] Beispiel-Code in examples/
|
||||||
|
- [ ] README mit Quick-Start
|
||||||
|
|
||||||
|
### Test-Anforderungen
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Unit-Tests mit Mock-HTTP-Client
|
||||||
|
func TestBooksService_List(t *testing.T) {
|
||||||
|
server := httptest.NewServer(...)
|
||||||
|
client := bookstack.NewClient(bookstack.Config{
|
||||||
|
BaseURL: server.URL,
|
||||||
|
TokenID: "test",
|
||||||
|
TokenSecret: "test",
|
||||||
|
})
|
||||||
|
|
||||||
|
books, err := client.Books.List(ctx, nil)
|
||||||
|
// assertions...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Launch-Kriterien v1.0
|
||||||
|
|
||||||
|
- [ ] Books, Pages, Search vollständig implementiert
|
||||||
|
- [ ] Export (Markdown, PDF) funktioniert
|
||||||
|
- [ ] Dokumentation vollständig
|
||||||
|
- [ ] Keine bekannten Bugs
|
||||||
|
- [ ] hqcli-Integration getestet
|
||||||
|
|
||||||
|
## 9. Technische Implementierungshinweise
|
||||||
|
|
||||||
|
### Go-Projektstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
bookstack-api/
|
||||||
|
├── bookstack.go # Client, Config, NewClient()
|
||||||
|
├── books.go # BooksService
|
||||||
|
├── pages.go # PagesService
|
||||||
|
├── chapters.go # ChaptersService
|
||||||
|
├── shelves.go # ShelvesService
|
||||||
|
├── search.go # SearchService
|
||||||
|
├── types.go # Alle Datenstrukturen
|
||||||
|
├── errors.go # Error-Typen
|
||||||
|
├── http.go # HTTP-Helfer, Request-Building
|
||||||
|
├── iterator.go # Pagination-Iterator
|
||||||
|
├── bookstack_test.go # Tests
|
||||||
|
├── README.md
|
||||||
|
├── go.mod
|
||||||
|
├── go.sum
|
||||||
|
└── examples/
|
||||||
|
└── basic/
|
||||||
|
└── main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error-Handling-Strategie
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Definierte Error-Typen für häufige Fälle
|
||||||
|
var (
|
||||||
|
ErrNotFound = errors.New("bookstack: resource not found")
|
||||||
|
ErrUnauthorized = errors.New("bookstack: unauthorized")
|
||||||
|
ErrForbidden = errors.New("bookstack: forbidden")
|
||||||
|
ErrRateLimited = errors.New("bookstack: rate limited")
|
||||||
|
ErrBadRequest = errors.New("bookstack: bad request")
|
||||||
|
)
|
||||||
|
|
||||||
|
// APIError für detaillierte Fehlerinformationen
|
||||||
|
type APIError struct {
|
||||||
|
StatusCode int
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Body []byte // Original Response Body
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *APIError) Error() string {
|
||||||
|
return fmt.Sprintf("bookstack: API error %d: %s", e.StatusCode, e.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *APIError) Is(target error) bool {
|
||||||
|
switch target {
|
||||||
|
case ErrNotFound:
|
||||||
|
return e.StatusCode == 404
|
||||||
|
case ErrUnauthorized:
|
||||||
|
return e.StatusCode == 401
|
||||||
|
case ErrForbidden:
|
||||||
|
return e.StatusCode == 403
|
||||||
|
case ErrRateLimited:
|
||||||
|
return e.StatusCode == 429
|
||||||
|
case ErrBadRequest:
|
||||||
|
return e.StatusCode == 400
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Begründung:** `errors.Is()` ermöglicht einfache Fehlerprüfung, `APIError` bietet Details wenn nötig.
|
||||||
|
|
||||||
|
### HTTP-Wrapper
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Interner HTTP-Helfer
|
||||||
|
func (c *Client) do(ctx context.Context, method, path string, body, result any) error {
|
||||||
|
// 1. Request bauen
|
||||||
|
// 2. Auth-Header setzen
|
||||||
|
// 3. Request ausführen
|
||||||
|
// 4. Response prüfen
|
||||||
|
// 5. Bei Fehler: APIError zurückgeben
|
||||||
|
// 6. Bei Erfolg: JSON in result unmarshalen
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Entwicklungs-Prioritäten
|
||||||
|
|
||||||
|
1. **Phase 1 (v0.1):** Foundation
|
||||||
|
- Client-Setup, Auth, HTTP-Wrapper
|
||||||
|
- Books.List, Books.Get
|
||||||
|
- Pages.List, Pages.Get
|
||||||
|
- Error-Handling
|
||||||
|
|
||||||
|
2. **Phase 2 (v0.2):** Core Features
|
||||||
|
- Search.All
|
||||||
|
- Pages.ExportMarkdown, Pages.ExportPDF
|
||||||
|
- Iterator für Pagination
|
||||||
|
- Chapters, Shelves
|
||||||
|
|
||||||
|
3. **Phase 3 (v0.3):** Write Operations
|
||||||
|
- Pages.Create
|
||||||
|
- Pages.Update
|
||||||
|
|
||||||
|
4. **Phase 4 (v1.0):** Release
|
||||||
|
- Dokumentation
|
||||||
|
- Beispiele
|
||||||
|
- CI/CD
|
||||||
|
- Veröffentlichung
|
||||||
|
|
||||||
|
### Potenzielle Risiken
|
||||||
|
|
||||||
|
| Risiko | Wahrscheinlichkeit | Mitigation |
|
||||||
|
|--------|-------------------|------------|
|
||||||
|
| API-Änderungen in Bookstack | Niedrig | Semantic Versioning, Tests |
|
||||||
|
| Rate-Limiting-Probleme | Mittel | Dokumentation für Aufrufer |
|
||||||
|
| Große PDF-Exports | Mittel | Streaming statt Buffer |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Anhang: Bookstack API-Referenz
|
||||||
|
|
||||||
|
- **Basis-URL:** https://docs.jakoubek.net/api
|
||||||
|
- **Dokumentation:** https://docs.jakoubek.net/api/docs
|
||||||
|
- **Beispiele:** https://codeberg.org/bookstack/api-scripts
|
||||||
|
- **Rate Limit:** 180 req/min (konfigurierbar serverseitig)
|
||||||
|
|
||||||
|
### Pagination-Parameter
|
||||||
|
|
||||||
|
| Parameter | Beschreibung | Default |
|
||||||
|
|-----------|--------------|---------|
|
||||||
|
| count | Anzahl Ergebnisse | 100 (max 500) |
|
||||||
|
| offset | Start-Position | 0 |
|
||||||
|
| sort | Sortierung (+name, -created_at) | - |
|
||||||
|
| filter[field] | Filter (eq, ne, gt, lt, like) | - |
|
||||||
3
go.mod
Normal file
3
go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
module code.beautifulmachines.dev/jakoubek/bookstack-api
|
||||||
|
|
||||||
|
go 1.25.6
|
||||||
Loading…
Add table
Add a link
Reference in a new issue