docs: add human-friendly documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-03-11 13:02:39 +00:00
parent 3f578bbaf1
commit 481344a066
5 changed files with 870 additions and 0 deletions

182
docs/build-system.md Normal file
View file

@ -0,0 +1,182 @@
---
title: Build System
description: Project detection, cross-compilation, code signing, and .core/build.yaml configuration.
---
# Build System
The `build/` package provides automatic project type detection, cross-compilation for multiple OS/architecture targets, artefact archiving with checksums, and code signing. Configuration lives in `.core/build.yaml` at the project root.
## How it works
1. **Detect** — Probes the project directory for marker files to determine the project type.
2. **Build** — Runs the appropriate builder plugin for each configured target (OS/arch pair).
3. **Archive** — Packages build outputs into compressed archives with SHA-256 checksums.
4. **Sign** — Optionally signs binaries before archiving (macOS codesign, GPG, or Windows signtool).
Run with:
```bash
core build # Build for configured targets
core build --ci # All targets, JSON output for CI pipelines
```
## Project detection
`discovery.go` probes marker files in priority order. The first match wins:
| Marker file | Detected type | Builder |
|-------------|--------------|---------|
| `wails.json` | `wails` | Wails v3 desktop app |
| `go.mod` | `go` | Standard Go binary |
| `package.json` | `node` | Node.js (stub) |
| `composer.json` | `php` | PHP (stub) |
| `CMakeLists.txt` | `cpp` | CMake C++ |
| `Dockerfile` | `docker` | Docker buildx |
| `*.yml` (LinuxKit pattern) | `linuxkit` | LinuxKit VM image |
| `Taskfile.yml` | `taskfile` | Task CLI delegation |
## Builder interface
Each builder implements three methods:
```go
type Builder interface {
Name() string
Detect(fs io.Medium, dir string) (bool, error)
Build(ctx context.Context, cfg *Config, targets []Target) ([]Artifact, error)
}
```
- `Detect` checks whether the builder should handle a given directory.
- `Build` produces `[]Artifact`, where each artefact has a file `Path`, `OS`, `Arch`, and a SHA-256 `Checksum`.
## Builders
### Go builder (`go.go`)
Runs `go build` with ldflags injection and cross-compilation via `GOOS`/`GOARCH` environment variables. Reads the entry point from `build.yaml`'s `project.main` field.
### Wails builder (`wails.go`)
Builds Wails v3 desktop applications with platform-specific packaging. Uses `wails.json` for application metadata.
### Docker builder (`docker.go`)
Runs `docker buildx build` with optional multi-platform support and registry push.
### C++ builder (`cpp.go`)
Runs CMake configure and build in a temporary directory. Requires `CMakeLists.txt`.
### LinuxKit builder (`linuxkit.go`)
Produces multi-format VM images from LinuxKit YAML configurations. Output formats: ISO, qcow2, raw, VMDK.
### Taskfile builder (`taskfile.go`)
Delegates to the `task` CLI, mapping build targets to Taskfile tasks. Used for projects that have not yet migrated to native builders.
### Node and PHP stubs
Stub builders for Node.js and PHP projects. Node runs `npm run build`; PHP wraps Docker builds. Both are minimal and intended for extension.
## Artefacts and checksums
Each builder produces `[]Artifact`:
```go
type Artifact struct {
Path string // File path of the built artefact
OS string // Target operating system
Arch string // Target architecture
Checksum string // SHA-256 hash
}
```
Checksums are computed automatically and written to `dist/*.sha256` files.
## Archive formats
`archive.go` packages build outputs into compressed archives. Supported formats:
- `tar.gz` — default for Linux/macOS
- `tar.xz` — smaller archives (uses Borg's xz compression)
- `zip` — default for Windows
## Code signing
The `signing/` sub-package implements a `Signer` interface with three backends:
| Signer | Platform | Tool | Key source |
|--------|----------|------|-----------|
| macOS | darwin | `codesign` | Keychain identity |
| GPG | any | `gpg --detach-sign` | GPG key ID |
| Windows | windows | `signtool` | Certificate store |
Each signer checks `Available()` at runtime to verify the signing tool exists. Signing runs after compilation, before archiving.
## .core/build.yaml reference
```yaml
version: 1
project:
name: my-app # Project name (used in archive filenames)
description: My application
main: ./cmd/my-app # Go entry point (go/wails only)
binary: my-app # Output binary name
build:
cgo: false # Enable CGO (default: false)
flags:
- -trimpath # Go build flags
ldflags:
- -s # Strip debug info
- -w # Strip DWARF
targets: # Cross-compilation targets
- os: linux
arch: amd64
- os: linux
arch: arm64
- os: darwin
arch: arm64
- os: windows
arch: amd64
```
### Field reference
| Field | Type | Description |
|-------|------|-------------|
| `version` | `int` | Config schema version (always `1`) |
| `project.name` | `string` | Project name, used in archive filenames |
| `project.description` | `string` | Human-readable description |
| `project.main` | `string` | Go entry point path (e.g. `./cmd/myapp`) |
| `project.binary` | `string` | Output binary filename |
| `build.cgo` | `bool` | Whether to enable CGO (default `false`) |
| `build.flags` | `[]string` | Go build flags (e.g. `-trimpath`) |
| `build.ldflags` | `[]string` | Linker flags (e.g. `-s -w`) |
| `targets` | `[]Target` | List of OS/arch pairs to compile for |
| `targets[].os` | `string` | Target OS (`linux`, `darwin`, `windows`) |
| `targets[].arch` | `string` | Target architecture (`amd64`, `arm64`) |
### Generated configuration
Running `core setup repo` in a git repository auto-generates `.core/build.yaml` based on detected project type:
```bash
core setup repo # Generates .core/build.yaml, release.yaml, test.yaml
core setup repo --dry-run # Preview without writing files
```
## Adding a new builder
1. Create `build/builders/mylang.go`.
2. Implement the `Builder` interface:
- `Name()` returns a human-readable name.
- `Detect(fs, dir)` checks for a marker file (e.g. `myconfig.toml`).
- `Build(ctx, cfg, targets)` produces artefacts.
3. Register the builder in `build/buildcmd/`.
4. Write tests verifying `Detect` (marker present/absent) and `Build` (at minimum with a mock `io.Medium`).

146
docs/index.md Normal file
View file

@ -0,0 +1,146 @@
---
title: go-devops
description: Build system, release publishers, infrastructure management, and DevOps tooling for the Lethean ecosystem.
---
# go-devops
`forge.lthn.ai/core/go-devops` is the build, release, and infrastructure automation library for the Lethean ecosystem. It replaces goreleaser with a native Go pipeline that auto-detects project types, cross-compiles, signs artefacts, generates changelogs, and publishes to eight distribution targets.
**Module**: `forge.lthn.ai/core/go-devops`
**Go**: 1.26
**Licence**: EUPL-1.2
## What it does
| Area | Summary |
|------|---------|
| **Build system** | Auto-detect project type from marker files, cross-compile for multiple OS/arch targets, archive and checksum artefacts |
| **Code signing** | macOS `codesign`, GPG detached signatures, Windows `signtool` |
| **Release publishers** | GitHub Releases, Docker, Homebrew, npm, AUR, Scoop, Chocolatey, LinuxKit |
| **SDK generation** | Generate typed API clients from OpenAPI specs (TypeScript, Python, Go, PHP) with breaking change detection |
| **Ansible executor** | Native Go playbook runner with ~30 modules over SSH — no `ansible-playbook` shell-out |
| **Infrastructure** | Hetzner Cloud/Robot provisioning, CloudNS DNS management |
| **Container/VM** | LinuxKit-based VMs via QEMU (Linux) or Hyperkit (macOS) |
| **Developer toolkit** | Cyclomatic complexity analysis, vulnerability scanning, coverage trending, secret scanning |
| **Doc sync** | Collect documentation from multi-repo workspaces into a central location |
## Package layout
```
go-devops/
├── ansible/ Ansible playbook execution engine (native Go, no shell-out)
├── build/ Build system: project detection, archives, checksums
│ ├── builders/ Builders: Go, Wails, Docker, C++, LinuxKit, Taskfile
│ ├── signing/ Code signing: macOS codesign, GPG, Windows signtool
│ └── buildcmd/ CLI handlers for core build / core release
├── container/ LinuxKit VM management, hypervisor abstraction
├── deploy/ Deployment integrations (Coolify PaaS, embedded Python)
├── devkit/ Code quality, security, coverage trending
├── devops/ Portable dev environment management
│ └── sources/ Image download: GitHub Releases, S3/CDN
├── infra/ Infrastructure APIs: Hetzner Cloud, Hetzner Robot, CloudNS
├── release/ Release orchestration: version, changelog, publishing
│ └── publishers/ 8 publisher backends
├── sdk/ OpenAPI SDK generation and breaking change detection
│ └── generators/ Language generators: TypeScript, Python, Go, PHP
├── snapshot/ Frozen release manifest generation (core.json)
└── cmd/ CLI command registrations
├── dev/ Multi-repo workflow commands (work, health, commit, push, pull)
├── docs/ Documentation sync and listing
├── deploy/ Coolify deployment commands
├── setup/ Repository and CI bootstrapping
└── gitcmd/ Git helpers
```
## CLI commands
go-devops registers commands into the `core` CLI binary (built from `forge.lthn.ai/core/cli`). Key commands:
```bash
# Build
core build # Auto-detect project type, build for configured targets
core build --ci # All targets, JSON output
core build sdk # Generate SDKs from OpenAPI spec
# Release
core build release # Build + changelog + publish (requires --we-are-go-for-launch)
# Multi-repo development
core dev health # Quick summary across all repos
core dev work # Combined status, commit, push workflow
core dev commit # Claude-assisted commits for dirty repos
core dev push # Push repos with unpushed commits
core dev pull # Pull repos behind remote
# GitHub integration
core dev issues # List open issues across repos
core dev reviews # PRs needing review
core dev ci # GitHub Actions status
# Documentation
core docs list # Scan repos for docs
core docs sync # Copy docs to central location
core docs sync --target gohelp # Sync to go-help format
# Deployment
core deploy servers # List Coolify servers
core deploy apps # List Coolify applications
# Setup
core setup repo # Generate .core/ configuration for a repo
core setup ci # Bootstrap CI configuration
```
## Configuration
Two YAML files in `.core/` at the project root control build and release behaviour:
| File | Purpose |
|------|---------|
| `.core/build.yaml` | Project name, binary, build flags, cross-compilation targets |
| `.core/release.yaml` | Repository, changelog rules, publisher configs, SDK settings |
See [Build System](build-system.md) and [Publishers](publishers.md) for full configuration reference.
## Core interfaces
Every extensible subsystem is defined by a small interface:
```go
// Builder — project type plugin (build/builders/)
type Builder interface {
Name() string
Detect(fs io.Medium, dir string) (bool, error)
Build(ctx context.Context, cfg *Config, targets []Target) ([]Artifact, error)
}
// Publisher — distribution target plugin (release/publishers/)
type Publisher interface {
Name() string
Publish(ctx context.Context, release *Release, pubCfg PublisherConfig,
relCfg ReleaseConfig, dryRun bool) error
}
// Generator — SDK language generator (sdk/generators/)
type Generator interface {
Language() string
Generate(ctx context.Context, spec, outputDir string, config *Config) error
}
// Signer — code signing plugin (build/signing/)
type Signer interface {
Name() string
Available() bool
Sign(filePath, keyID string) ([]byte, error)
}
```
## Further reading
- [Build System](build-system.md) — Builders, project detection, `.core/build.yaml` reference
- [Publishers](publishers.md) — Release publishers, `.core/release.yaml` reference
- [SDK Generation](sdk-generation.md) — OpenAPI client generation and breaking change detection
- [Doc Sync](sync.md) — Documentation sync across multi-repo workspaces
- [Architecture](architecture.md) — Full architecture deep-dive (Ansible, infra, devkit, containers)
- [Development Guide](development.md) — Building, testing, coding standards

215
docs/publishers.md Normal file
View file

@ -0,0 +1,215 @@
---
title: Release Publishers
description: Release orchestration, changelog generation, eight publisher backends, and .core/release.yaml configuration.
---
# Release Publishers
The `release/` package orchestrates the full release pipeline: version detection from git tags, changelog generation from commit history, and publishing to multiple distribution targets. Configuration lives in `.core/release.yaml`.
## How it works
The build and release stages are deliberately separate, allowing CI pipelines to build once and publish to multiple targets independently.
```
core build # 1. Compile artefacts into dist/
core build release # 2. Version + changelog + publish (requires --we-are-go-for-launch)
```
The release pipeline runs these steps:
1. **Resolve version** — from git tags on HEAD, or increment the latest tag's patch version.
2. **Scan artefacts** — find pre-built files in `dist/`.
3. **Generate changelog** — parse conventional commit messages since the previous tag.
4. **Publish** — iterate configured publishers, calling `Publisher.Publish()` on each.
## Version detection
`DetermineVersion(dir)` checks in priority order:
1. Git tag on `HEAD` (exact match).
2. Most recent tag with patch version incremented (e.g. `v1.2.3` becomes `v1.2.4`).
3. Default `v0.0.1` if no tags exist.
Pre-release suffixes are stripped when incrementing.
## Changelog generation
`Generate()` in `changelog.go` reads the git log since the previous tag and groups entries by conventional commit prefix:
| Prefix | Section |
|--------|---------|
| `feat:` | Features |
| `fix:` | Bug Fixes |
| `perf:` | Performance |
| `refactor:` | Refactoring |
The `changelog` section in `release.yaml` controls which prefixes are included or excluded.
## Publisher interface
All publishers implement:
```go
type Publisher interface {
Name() string
Publish(ctx context.Context, release *Release, pubCfg PublisherConfig,
relCfg ReleaseConfig, dryRun bool) error
}
```
When `dryRun` is `true`, publishers log what they would do without making external calls.
## Publisher backends
### GitHub Releases (`github.go`)
Creates a GitHub Release via the API and uploads artefact files as release assets.
```yaml
publishers:
- type: github
draft: false
prerelease: false
```
### Docker (`docker.go`)
Runs `docker buildx build --push` to a configured container registry. Supports multi-platform images.
```yaml
publishers:
- type: docker
registry: ghcr.io
image: myorg/myapp
```
### Homebrew (`homebrew.go`)
Generates a Ruby formula file and commits it to a Homebrew tap repository.
```yaml
publishers:
- type: homebrew
tap: myorg/homebrew-tap
formula: myapp
```
### npm (`npm.go`)
Runs `npm publish` to the npm registry.
```yaml
publishers:
- type: npm
registry: https://registry.npmjs.org
```
### AUR (`aur.go`)
Generates a `PKGBUILD` and `.SRCINFO`, then pushes to the Arch User Repository git remote.
```yaml
publishers:
- type: aur
package: myapp
```
### Scoop (`scoop.go`)
Generates a JSON manifest and commits it to a Scoop bucket repository.
```yaml
publishers:
- type: scoop
bucket: myorg/scoop-bucket
```
### Chocolatey (`chocolatey.go`)
Generates a `.nuspec` file and calls `choco push`.
```yaml
publishers:
- type: chocolatey
package: myapp
```
### LinuxKit (`linuxkit.go`)
Builds and uploads LinuxKit multi-format VM images (ISO, qcow2, raw, VMDK).
```yaml
publishers:
- type: linuxkit
formats:
- qcow2
- iso
```
## .core/release.yaml reference
```yaml
version: 1
project:
name: my-app
repository: myorg/my-app # GitHub owner/repo (auto-detected from git remote)
changelog:
include: # Conventional commit types to include
- feat
- fix
- perf
- refactor
exclude: # Types to exclude
- chore
- docs
- style
- test
- ci
publishers: # List of publisher configurations
- type: github
draft: false
prerelease: false
# Optional: SDK generation (see sdk-generation.md)
sdk:
spec: openapi.yaml
languages: [typescript, python, go, php]
output: sdk
diff:
enabled: true
fail_on_breaking: false
```
### Field reference
| Field | Type | Description |
|-------|------|-------------|
| `version` | `int` | Config schema version (always `1`) |
| `project.name` | `string` | Project name |
| `project.repository` | `string` | Git remote in `owner/repo` format |
| `changelog.include` | `[]string` | Conventional commit prefixes to include in the changelog |
| `changelog.exclude` | `[]string` | Prefixes to exclude |
| `publishers` | `[]PublisherConfig` | List of publisher configurations |
| `publishers[].type` | `string` | Publisher type: `github`, `docker`, `homebrew`, `npm`, `aur`, `scoop`, `chocolatey`, `linuxkit` |
| `sdk` | `SDKConfig` | Optional SDK generation settings (see [SDK Generation](sdk-generation.md)) |
### Generated configuration
`core setup repo` generates a default `release.yaml` with sensible defaults for the detected project type:
- **Go/Wails projects** get changelog rules for `feat`, `fix`, `perf`, `refactor` and a GitHub publisher.
- **PHP projects** get `feat`, `fix`, `perf` and a GitHub publisher.
- **Other projects** get `feat`, `fix` and a GitHub publisher.
## Adding a new publisher
1. Create `release/publishers/myplatform.go`.
2. Implement `Publisher`:
- `Name()` returns the platform name (matches the `type` field in `release.yaml`).
- `Publish(ctx, release, pubCfg, relCfg, dryRun)` performs the publication. When `dryRun` is `true`, log intent and return `nil`.
3. Register the publisher in `release/config.go`.
4. Write `release/publishers/myplatform_test.go` with dry-run tests. Verify command arguments, generated file content, and interface compliance.

216
docs/sdk-generation.md Normal file
View file

@ -0,0 +1,216 @@
---
title: SDK Generation
description: OpenAPI client generation for TypeScript, Python, Go, and PHP with breaking change detection.
---
# SDK Generation
The `sdk/` package generates typed API client libraries from OpenAPI specifications. It supports four languages, auto-detects spec files, and uses oasdiff for semantic breaking change detection. Configuration lives in the `sdk:` section of `.core/release.yaml`.
## How it works
```
core build sdk # Generate SDKs using .core/release.yaml config
core build sdk --spec api.yaml # Explicit spec file
core build sdk --lang typescript # Single language only
```
The generation pipeline:
1. **Detect spec** — find the OpenAPI file (config, common paths, or Laravel Scramble).
2. **Run diff** — if enabled, compare current spec against the previous release and flag breaking changes.
3. **Generate** — run the appropriate generator for each configured language.
4. **Output** — write client libraries to `sdk/{language}/`.
## OpenAPI spec detection
`detect.go` probes locations in priority order:
1. Path configured in `.core/release.yaml` (`sdk.spec` field).
2. Common file paths:
- `api/openapi.yaml`, `api/openapi.json`
- `openapi.yaml`, `openapi.json`
- `docs/api.yaml`, `docs/openapi.yaml`
- `swagger.yaml`
3. Laravel Scramble — if `scramble/scramble` is in `composer.json`, runs `php artisan scramble:export` to generate the spec.
4. Error if no spec found.
## Generator interface
Each language generator implements:
```go
type Generator interface {
Language() string
Generate(ctx context.Context, spec, outputDir string, config *Config) error
}
```
Generators are registered in a string-keyed registry, allowing overrides.
## Language generators
Each generator uses a native tool when available and falls back to Docker-based generation:
| Language | Native tool | Fallback | Install |
|----------|------------|----------|---------|
| TypeScript | `openapi-typescript-codegen` | openapi-generator (Docker) | `npm i -g openapi-typescript-codegen` |
| Python | `openapi-python-client` | openapi-generator (Docker) | `pip install openapi-python-client` |
| Go | `oapi-codegen` | openapi-generator (Docker) | `go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest` |
| PHP | openapi-generator (Docker) | — | Requires Docker |
### Fallback strategy
When the native tool is not installed, generators automatically fall back to running openapi-generator inside Docker:
```go
func (g *TypeScriptGenerator) Generate(ctx context.Context, opts GenerateOptions) error {
if g.Available() {
return g.generateNative(ctx, opts)
}
return g.generateDocker(ctx, opts)
}
```
## Output structure
Each generator writes to `sdk/{language}/`:
```
sdk/
├── typescript/
│ ├── package.json
│ ├── src/
│ │ ├── index.ts
│ │ ├── client.ts
│ │ └── models/
│ └── tsconfig.json
├── python/
│ ├── setup.py
│ ├── myapi/
│ │ ├── __init__.py
│ │ ├── client.py
│ │ └── models/
│ └── requirements.txt
├── go/
│ ├── go.mod
│ ├── client.go
│ └── models.go
└── php/
├── composer.json
├── src/
│ ├── Client.php
│ └── Models/
└── README.md
```
## Breaking change detection
`diff.go` uses [oasdiff](https://github.com/Tufin/oasdiff) to compare two OpenAPI spec versions semantically. It detects:
- Removed endpoints
- Changed required parameters
- Modified response schemas
- Changed authentication requirements
### Usage
```bash
core sdk diff # Compare current spec vs last release
core sdk diff --spec api.yaml --base v1.0.0
core sdk validate # Validate the spec without generating
```
### CI exit codes
| Exit code | Meaning |
|-----------|---------|
| 0 | No breaking changes |
| 1 | Breaking changes detected |
| 2 | Error (invalid spec, missing file) |
### Diff configuration
The `sdk.diff` section in `release.yaml` controls behaviour:
```yaml
sdk:
diff:
enabled: true # Run diff check before generation
fail_on_breaking: true # Abort on breaking changes (CI-friendly)
```
When `fail_on_breaking` is `false`, breaking changes produce a warning but generation continues.
## Release integration
SDK generation integrates with the release pipeline. When the `sdk:` section is present in `release.yaml`, `core build release` runs SDK generation after building artefacts:
```
core build release
-> build artefacts
-> generate SDKs (if sdk: configured)
-> run diff check (warns or fails on breaking)
-> publish to GitHub release
-> publish SDKs (optional)
```
SDK generation can also run independently:
```bash
core build sdk # Generate using release.yaml config
core build sdk --version v1.2.3 # Explicit version
core build sdk --dry-run # Preview without generating
```
## Configuration reference
In `.core/release.yaml`:
```yaml
sdk:
spec: api/openapi.yaml # OpenAPI spec path (auto-detected if omitted)
languages: # Languages to generate
- typescript
- python
- go
- php
output: sdk # Output directory (default: sdk/)
package:
name: myapi # Base package name
version: "{{.Version}}" # Template — uses release version
diff:
enabled: true # Run breaking change detection
fail_on_breaking: true # Fail on breaking changes (for CI)
publish: # Optional monorepo publishing
repo: myorg/sdks
path: packages/myapi
```
### Field reference
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `sdk.spec` | `string` | auto-detect | Path to OpenAPI spec |
| `sdk.languages` | `[]string` | — | Languages to generate (`typescript`, `python`, `go`, `php`) |
| `sdk.output` | `string` | `sdk/` | Output directory |
| `sdk.package.name` | `string` | — | Base package name for generated clients |
| `sdk.package.version` | `string` | — | Version template (supports `{{.Version}}`) |
| `sdk.diff.enabled` | `bool` | `false` | Run breaking change detection |
| `sdk.diff.fail_on_breaking` | `bool` | `false` | Abort on breaking changes |
| `sdk.publish.repo` | `string` | — | Monorepo target (`owner/repo`) |
| `sdk.publish.path` | `string` | — | Path within the monorepo |
## Dependencies
SDK generation relies on:
| Dependency | Purpose |
|-----------|---------|
| `github.com/getkin/kin-openapi` | OpenAPI 3.x spec parsing and validation |
| `github.com/oasdiff/oasdiff` | Semantic API diff and breaking change detection |

111
docs/sync.md Normal file
View file

@ -0,0 +1,111 @@
---
title: Doc Sync
description: Documentation sync command for collecting docs from multi-repo workspaces into a central location.
---
# Doc Sync
The `core docs` commands scan a multi-repo workspace for documentation and sync it to a central location. This enables unified documentation builds across federated monorepos.
## Commands
### core docs list
Scan all repos in the workspace and display a table showing documentation coverage:
```bash
core docs list # Uses auto-detected repos.yaml
core docs list --registry path/to/repos.yaml
```
Output shows which repos have:
- `README.md`
- `CLAUDE.md`
- `CHANGELOG.md`
- `docs/` directory (with file count)
### core docs sync
Copy `docs/` directories from all repos into a central output location:
```bash
core docs sync # Sync to default target (php)
core docs sync --dry-run # Preview without writing
core docs sync --target gohelp # Sync to go-help format
core docs sync --target zensical # Sync to Zensical/Hugo format
core docs sync --output /path/to/dest # Custom output directory
core docs sync --registry repos.yaml # Explicit registry file
```
## Sync targets
The `--target` flag controls the output format and default destination:
### php (default)
Copies documentation to `core-php/docs/packages/{name}/`. Each repo's `docs/` directory is copied as-is, with directory names mapped:
- `core` maps to `packages/go/`
- `core-admin` maps to `packages/admin/`
- `core-api` maps to `packages/api/`
Skips `core-php` (the destination) and `core-template`.
### gohelp
Plain copy to a go-help content directory (`docs/content/` by default). No frontmatter injection. Directory names follow the same mapping as the php target.
### zensical
Copies to a Zensical/Hugo docs directory (`docs-site/docs/` by default) with automatic Hugo frontmatter injection. Repos are mapped to content sections:
| Repo pattern | Section | Folder |
|-------------|---------|--------|
| `cli` | `getting-started` | — |
| `core` | `cli` | — |
| `go-*` | `go` | repo name |
| `core-*` | `php` | name without prefix |
Frontmatter (title and weight) is added to markdown files that lack it. READMEs become `index.md` files. `KB/` directories are synced to a `kb/` section.
## How it works
1. **Load registry** — finds `repos.yaml` (auto-detected or explicit path) and loads the repo list. Respects `workspace.yaml` for custom `packages_dir`.
2. **Scan repos** — walks each repo looking for `README.md`, `CLAUDE.md`, `CHANGELOG.md`, `docs/*.md` (recursive), and `KB/*.md` (recursive). The `docs/plans/` subdirectory is skipped.
3. **Display plan** — shows which repos have documentation and where files will be written.
4. **Confirm** — prompts for confirmation (skipped in `--dry-run` mode).
5. **Copy** — clears existing output directories and copies files. For the zensical target, injects Hugo frontmatter where missing.
## Flags
| Flag | Default | Description |
|------|---------|-------------|
| `--registry` | auto-detect | Path to `repos.yaml` |
| `--dry-run` | `false` | Preview sync plan without writing files |
| `--output` | target-dependent | Output directory (overrides target default) |
| `--target` | `php` | Output format: `php`, `gohelp`, or `zensical` |
## Registry discovery
The registry (list of repos) is found by:
1. Explicit `--registry` path if provided.
2. Auto-detection: `repos.yaml` in the current directory or parent directories.
3. Fallback: scan the current directory for git repositories.
If a `workspace.yaml` file exists alongside the registry, its `packages_dir` setting overrides the default repo path resolution.
## RepoDocInfo
Each scanned repo produces a `RepoDocInfo` struct:
| Field | Type | Description |
|-------|------|-------------|
| `Name` | `string` | Repository name |
| `Path` | `string` | Absolute filesystem path |
| `HasDocs` | `bool` | Whether any documentation was found |
| `Readme` | `string` | Path to `README.md` (empty if missing) |
| `ClaudeMd` | `string` | Path to `CLAUDE.md` (empty if missing) |
| `Changelog` | `string` | Path to `CHANGELOG.md` (empty if missing) |
| `DocsFiles` | `[]string` | Relative paths of files in `docs/` |
| `KBFiles` | `[]string` | Relative paths of files in `KB/` |