A Laravel package for synchronizing Markdown documentation with BookStack wiki. Perfect for keeping your project documentation in sync between your codebase and BookStack.
- Bidirectional Sync: Push local Markdown files to BookStack or pull BookStack content to local files
- Local SQLite Cache: Reduces API calls by caching wiki structure locally with change detection
- Bookmark Conversion: Automatically converts AI-generated bookmarks (Claude, Cursor, etc.) to BookStack's URL-encoded format
- Spanish Character Support: Full support for UTF-8 characters (á, é, í, ó, ú, ñ, ç, ü, etc.)
- Artisan Commands: Easy-to-use CLI commands for all operations
- Conflict Resolution: Multiple strategies for handling sync conflicts
- Dry Run Mode: Preview changes before making them
- PHP 8.2+
- Laravel 10, 11, or 12
- BookStack instance with API access
composer require aichadigital/bookstack-sync --devPublish the configuration file:
php artisan vendor:publish --tag="bookstack-sync-config"Add these variables to your .env file:
BOOKSTACK_URL=https://your-bookstack-instance.com/
BOOKSTACK_TOKEN_ID=your-token-id
BOOKSTACK_TOKEN_SECRET=your-token-secret
# Optional defaults
BOOKSTACK_BOOK_ID=123
BOOKSTACK_MARKDOWN_PATH=docsAlternative variable names are also supported for compatibility:
WIKI_URL=https://your-bookstack-instance.com/
WIKI_TOKEN_ID=your-token-id
WIKI_TOKEN=your-token-secretphp artisan bookstack:status
php artisan bookstack:status --books
php artisan bookstack:status --shelves# Push docs directory to book ID 5
php artisan bookstack:push docs --book=5
# Dry run (preview without changes)
php artisan bookstack:push docs --book=5 --dry-run
# Skip confirmation
php artisan bookstack:push docs --book=5 --force# Pull book ID 5 to local directory
php artisan bookstack:pull --book=5 --path=docs
# Dry run
php artisan bookstack:pull --book=5 --path=docs --dry-run# Export as Markdown
php artisan bookstack:export page 123
# Export book as PDF
php artisan bookstack:export book 5 --format=pdf --output=manual.pdf
# Available formats: markdown, html, pdf, plaintextphp artisan bookstack:search "configuration"
php artisan bookstack:search "instalación" --limit=50# Sync all wiki structure to local SQLite database
php artisan bookstack:sync
# Fresh sync (deletes existing database first)
php artisan bookstack:sync --fresh
# Sync only specific entities
php artisan bookstack:sync --no-shelves --no-chapters# Show database statistics
php artisan bookstack:db stats
# List cached entities
php artisan bookstack:db shelves
php artisan bookstack:db books
php artisan bookstack:db chapters --book=5
php artisan bookstack:db pages --book=5 --chapter=10
# Include deleted items
php artisan bookstack:db pages --deleted
# Show database path and size
php artisan bookstack:db path
# Delete local database
php artisan bookstack:db delete --forceuse AichaDigital\BookStackSync\Facades\BookStackSync;
// List all books
$books = BookStackSync::books();
// Get a specific page
$page = BookStackSync::page(123);
// Create a new page with Markdown content
$page = BookStackSync::createPage(
bookId: 5,
name: 'Configuración Inicial',
content: '# Introducción\n\nContenido aquí...',
chapterId: 10
);
// Search
$results = BookStackSync::search('instalación');
// Sync operations
$result = BookStackSync::pushToBook('/path/to/docs', 5);
$result = BookStackSync::pullFromBook(5, '/path/to/docs');The package automatically handles the conversion between AI-generated bookmark formats and BookStack's URL-encoded format:
use AichaDigital\BookStackSync\Facades\BookStackSync;
// Encode for BookStack
$encoded = BookStackSync::encodeBookmark('sección-principal');
// Returns: secci%C3%B3n-principal
// Decode from BookStack
$decoded = BookStackSync::decodeBookmark('secci%C3%B3n-principal');
// Returns: sección-principaluse AichaDigital\BookStackSync\Parsers\MarkdownParser;
$parser = new MarkdownParser();
// Convert content for BookStack
$content = '# Introducción\n\nSee [sección](#sección) for details.';
$converted = $parser->parseForBookStack($content);
// Anchors are now URL-encoded
// Extract headings
$headings = $parser->extractHeadings($content);
// Generate Table of Contents
$toc = $parser->generateTableOfContents($content);You can use YAML frontmatter in your Markdown files to specify metadata:
---
title: Mi Página
chapter: Introducción
---
# Content here...Supported frontmatter fields:
title/name: Page title (overrides filename)chapter: Chapter name (creates if doesn't exist)bookstack_id: Links to existing BookStack page for updates
Configure sync behavior in config/bookstack-sync.php:
'sync' => [
// Direction: 'push', 'pull', or 'bidirectional'
'direction' => env('BOOKSTACK_SYNC_DIRECTION', 'push'),
// Conflict resolution: 'local', 'remote', 'newest', 'manual'
'conflict_resolution' => env('BOOKSTACK_CONFLICT_RESOLUTION', 'manual'),
// Auto-create missing structure
'auto_create_structure' => true,
// Dry run mode
'dry_run' => false,
],The package includes a local SQLite cache to reduce API calls and enable change detection:
'database' => [
// Enable/disable local database caching
'enabled' => env('BOOKSTACK_LOCAL_DB', true),
// Database path (relative to storage/ or absolute path)
'path' => env('BOOKSTACK_DB_PATH', 'bookstack-sync.sqlite'),
],# Enable local SQLite cache (default: true)
BOOKSTACK_LOCAL_DB=true
# Custom database path (default: storage/bookstack-sync.sqlite)
BOOKSTACK_DB_PATH=bookstack-sync.sqlite- Run
php artisan bookstack:syncto populate the local cache - The cache stores all shelves, books, chapters, and pages with their metadata
- During push/pull operations, the package uses content hashes to skip unchanged files
- Deleted items in BookStack are marked with
is_deletedflag for tracking
| Character | URL Encoded |
|---|---|
| á | %C3%A1 |
| é | %C3%A9 |
| í | %C3%AD |
| ó | %C3%B3 |
| ú | %C3%BA |
| ñ | %C3%B1 |
| ü | %C3%BC |
| ç | %C3%A7 |
composer testTo run tests against your actual BookStack instance, copy .env.example to .env and configure your credentials:
cp .env.example .env
# Edit .env with your BookStack credentials
composer testThis package uses the BookStack REST API. Key endpoints used:
/api/shelves- Shelf operations/api/books- Book operations/api/chapters- Chapter operations/api/pages- Page operations/api/search- Search across content
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
Special thanks to:
- BookStack - The excellent open-source wiki platform that this package integrates with
- Dan Brown - Creator and maintainer of BookStack
Trademark Notice: BookStack® is a registered trademark of Daniel Brown. This package is not affiliated with, endorsed by, or sponsored by BookStack or Daniel Brown.
This package is licensed under the GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later). Please see License File for more information.