# Machine-Level Tool Reference Centralized reference for tools, credentials, and CLI patterns available across all projects. Project-specific tooling belongs in the project's `AGENTS.md`, not here. All tool suites are located at `~/.config/mosaic/tools/`. ## Tool Suites ### Git Wrappers (Use First) Mosaic wrappers at `~/.config/mosaic/tools/git/*.sh` handle platform detection and edge cases. Always use these before raw CLI commands. This index is complete and is kept complete mechanically: `tools/quality/scripts/check-tools-index.sh` fails CI when a wrapper ships without an entry here, or when an entry here names a wrapper that no longer exists. A wrapper missing from this list is, from inside an agent session, indistinguishable from a wrapper that was never written — which is how the APPROVE/APPROVED incident below happened. Every command takes `--help`. All of them accept `--login ` to pin the acting identity; supply it explicitly on any host where the provider CLI's default account is an admin. | Issues | | | --- | --- | | `issue-create.sh` | Create an issue (Gitea or GitHub) | | `issue-view.sh` | Show one issue | | `issue-list.sh` | List issues | | `issue-edit.sh` | Edit title/body/labels/milestone | | `issue-comment.sh` | Add a comment | | `issue-assign.sh` | Assign or unassign | | `issue-close.sh` | Close an issue | | `issue-reopen.sh` | Reopen a closed issue | | Pull requests | | | --- | --- | | `pr-create.sh` | Open a pull request | | `pr-view.sh` | Show one PR | | `pr-list.sh` | List PRs | | `pr-diff.sh` | Fetch a PR's diff | | `pr-metadata.sh` | PR metadata as JSON (head SHA, base, state, mergeability) | | `pr-review.sh` | **Place a review verdict — see the dialect note below** | | `pr-ci-wait.sh` | Block until the PR's CI reaches a terminal state | | `pr-merge.sh` | Merge a PR | | `pr-close.sh` | Close a PR without merging | | Milestones | | | --- | --- | | `milestone-create.sh` | Create a milestone | | `milestone-list.sh` | List milestones | | `milestone-close.sh` | Close a milestone | | Gates and guards | | | --- | --- | | `ci-queue-wait.sh` | CI queue guard — required before push/merge (see below) | | `push-guard.sh` | Refuse verifications that pass for the wrong reason (e.g. green against an unpushed tree) | | `mutate-push-guard.sh` | Regenerate the guard's mutation-coverage table from measurement, so the table cannot drift from the guard | | `verify-clean-clone.sh` | Prove the **committed** artifact runs, from a clean clone — not the working tree | | Context | | | --- | --- | | `detect-platform.sh` | Resolve the provider (Gitea vs GitHub) for the current repo; every other wrapper uses it | | `lane-brief.sh` | Live dispatch brief for a repo "lane" (milestone/label) straight from the provider | | Workspace | | | --- | --- | | `mosaic-worktree.sh` | Create/list/remove git worktrees — **the only supported way**; see below | | `wrapper-guard.sh` | PreToolUse hook that enforces the two rules above; not called by hand | **Workspace placement is derived, not chosen.** `mosaic-worktree.sh new ` takes a branch name and nothing else. Every path comes out of `git worktree list --porcelain` — main worktree, repo name, parent dir, then `/-worktrees/`. There is no placement flag because a decision an agent has to make is a decision that drifts: the rule "big work goes on a work filesystem" already existed in prose and 255 GB accumulated in `$HOME` across 842 directories anyway, under five simultaneous conventions on a single host. ```bash ~/.config/mosaic/tools/git/mosaic-worktree.sh new [--from ] ~/.config/mosaic/tools/git/mosaic-worktree.sh path # derived path, no side effect ~/.config/mosaic/tools/git/mosaic-worktree.sh list # this repo's worktrees + state ~/.config/mosaic/tools/git/mosaic-worktree.sh rm # removal is part of the task ~/.config/mosaic/tools/git/mosaic-worktree.sh gc [--apply] # reclaim clean + fully-pushed ones ``` Worktrees rather than clones, because `git worktree list` makes every checkout enumerable — a bare clone dropped somewhere on disk can never be safely reclaimed, so it is never reclaimed. `rm` and `gc` decide by **evidence, never by size or age**: a worktree is reclaimable only when `git status --porcelain` is empty *and* `git rev-list --count HEAD --not --remotes` is 0. Anything else is preserved and reported. `--force` exists and is yours to type deliberately. `wrapper-guard.sh` is registered as a Claude Code `PreToolUse` hook on `Bash` (see `runtime/claude/settings.json`). It blocks exactly three things and lets everything else through: a `git clone`/`git worktree add` targeting `$HOME`; a raw provider-API **write** to an endpoint that already has a wrapper above (reads are untouched — they are how you gather evidence); and the literal `"event": "APPROVE"`. For a genuine gap no wrapper can express, prefix `MOSAIC_WRAPPER_OVERRIDE=1`. Reaching for the override twice for the same call means the wrapper has a missing flag — extend the wrapper. ```bash ~/.config/mosaic/tools/git/issue-create.sh --help ~/.config/mosaic/tools/git/pr-review.sh --pr 42 --event APPROVED --body "..." # CI queue guard (required before push/merge; defaults to the checked-out branch) ~/.config/mosaic/tools/git/ci-queue-wait.sh --purpose push|merge ``` **Review dialect — the reason `pr-review.sh` is not optional.** Gitea's approve event is `APPROVED`; GitHub's is `APPROVE`. Send GitHub's spelling to a Gitea host and it answers **HTTP 200**, files the review as PENDING, and then rejects the submit with `422 review stay pending` — the verdict looks placed and is not. (`REQUEST_CHANGES` is spelled identically on both, so only the approve path carries the trap.) `pr-review.sh` sends the correct token for the detected provider. Whatever you use, re-read `GET /pulls/{n}/reviews` and assert the state before reporting a verdict placed. The guard exits nonzero for any provider-asserted non-green, missing, or malformed CI state. If credentials or the provider are unavailable, it emits `CANNOT_ASSERT` and writes a JSONL audit record. Push degrades to exit 0 so recovery work is not bricked; merge holds with retryable exit 75 until the provider recovers, then self-clears without manual reset. Neither outcome is evidence that CI was clear. `pr-merge.sh` automatically inspects the exact PR head repository and full commit SHA rather than its `main` base; this also handles fork PRs without branch-name ambiguity. Pass `--expect-head ` to bind a commit-specific review or merge-gate verdict; Gitea uses atomic `head_commit_id` and GitHub uses `--match-head-commit`. ### Code Review (Codex) ```bash ~/.config/mosaic/tools/codex/codex-code-review.sh --uncommitted ~/.config/mosaic/tools/codex/codex-security-review.sh --uncommitted ``` ### Infrastructure — Portainer ```bash ~/.config/mosaic/tools/portainer/stack-status.sh -n ~/.config/mosaic/tools/portainer/stack-redeploy.sh -n ~/.config/mosaic/tools/portainer/stack-list.sh ~/.config/mosaic/tools/portainer/endpoint-list.sh ``` ### Infrastructure — Coolify (DEPRECATED) > Coolify has been superseded by Portainer Docker Swarm in this stack. > Tools remain for reference but should not be used for new deployments. ```bash # DEPRECATED — do not use for new deployments ~/.config/mosaic/tools/coolify/project-list.sh ~/.config/mosaic/tools/coolify/service-list.sh ~/.config/mosaic/tools/coolify/service-status.sh -u ~/.config/mosaic/tools/coolify/deploy.sh -u ~/.config/mosaic/tools/coolify/env-set.sh -u -k KEY -v VALUE ``` ### Identity — Authentik ```bash ~/.config/mosaic/tools/authentik/user-list.sh ~/.config/mosaic/tools/authentik/user-create.sh -u -n -e ~/.config/mosaic/tools/authentik/group-list.sh ~/.config/mosaic/tools/authentik/app-list.sh ~/.config/mosaic/tools/authentik/flow-list.sh ~/.config/mosaic/tools/authentik/admin-status.sh ``` ### CI/CD — Woodpecker Multi-instance support: `-a ` selects a named instance. Omit `-a` to use the default from `woodpecker.default` in credentials.json. | Instance | URL | Serves | | ------------------ | ------------------ | ---------------------------------- | | `mosaic` (default) | ci.mosaicstack.dev | Mosaic repos (git.mosaicstack.dev) | | `usc` | ci.uscllc.com | USC repos (git.uscllc.com) | ```bash # List recent pipelines ~/.config/mosaic/tools/woodpecker/pipeline-list.sh [-r owner/repo] [-a instance] # Check latest or specific pipeline status ~/.config/mosaic/tools/woodpecker/pipeline-status.sh [-r owner/repo] [-n number] [-a instance] # Trigger a build ~/.config/mosaic/tools/woodpecker/pipeline-trigger.sh [-r owner/repo] [-b branch] [-a instance] ``` Instance selection rule: match `-a` to the git remote host of the target repo. If the repo is on `git.uscllc.com`, use `-a usc`. If on `git.mosaicstack.dev`, use `-a mosaic` (or omit, since it's the default). ### DNS — Cloudflare Multi-instance support: `-a ` selects a named instance (e.g. `personal`, `work`). Omit `-a` to use the default from `cloudflare.default` in credentials.json. ```bash # List zones (domains) ~/.config/mosaic/tools/cloudflare/zone-list.sh [-a instance] # List DNS records (zone by name or ID) ~/.config/mosaic/tools/cloudflare/record-list.sh -z [-a instance] [-t type] [-n name] # Create DNS record ~/.config/mosaic/tools/cloudflare/record-create.sh -z -t -n -c [-a instance] [-p] [-l ttl] [-P priority] # Update DNS record ~/.config/mosaic/tools/cloudflare/record-update.sh -z -r -t -n -c [-a instance] [-p] [-l ttl] # Delete DNS record ~/.config/mosaic/tools/cloudflare/record-delete.sh -z -r [-a instance] ``` ### IT Service — GLPI ```bash ~/.config/mosaic/tools/glpi/ticket-list.sh ~/.config/mosaic/tools/glpi/ticket-create.sh -t -c <content> ~/.config/mosaic/tools/glpi/computer-list.sh ~/.config/mosaic/tools/glpi/user-list.sh ``` ### Health Check ```bash # Check all configured services ~/.config/mosaic/tools/health/stack-health.sh # Check a specific service ~/.config/mosaic/tools/health/stack-health.sh -s portainer # JSON output for automation ~/.config/mosaic/tools/health/stack-health.sh -f json ``` ### Shared Credential Loader ```bash # Source in any script to load service credentials source ~/.config/mosaic/tools/_lib/credentials.sh load_credentials <service-name> # Supported: portainer, coolify, authentik, glpi, github, gitea-mosaicstack, gitea-usc, woodpecker, cloudflare, turbo-cache, openbrain ``` ### OpenBrain — Semantic Memory (PRIMARY) Self-hosted semantic brain backed by pgvector. Primary shared memory layer for all agents across all sessions and harnesses. Stores and retrieves decisions, context, and observations via semantic search. **Credentials:** `load_credentials openbrain` → exports `OPENBRAIN_URL`, `OPENBRAIN_TOKEN` Configure in your credentials.json: ```json "openbrain": { "url": "https://<your-openbrain-host>", "api_key": "<your-api-key>" } ``` **REST API** (any language, any harness): ```bash source ~/.config/mosaic/tools/_lib/credentials.sh && load_credentials openbrain # Search by meaning curl -s -X POST -H "Authorization: Bearer $OPENBRAIN_TOKEN" -H "Content-Type: application/json" \ -d '{"query": "your search", "limit": 5}' "$OPENBRAIN_URL/v1/search" # Capture a thought curl -s -X POST -H "Authorization: Bearer $OPENBRAIN_TOKEN" -H "Content-Type: application/json" \ -d '{"content": "...", "source": "agent-name", "metadata": {}}' "$OPENBRAIN_URL/v1/thoughts" # Recent activity curl -s -H "Authorization: Bearer $OPENBRAIN_TOKEN" "$OPENBRAIN_URL/v1/thoughts/recent?limit=5" # Stats curl -s -H "Authorization: Bearer $OPENBRAIN_TOKEN" "$OPENBRAIN_URL/v1/stats" ``` **Python client** (if the OpenBrain client is on your PYTHONPATH): ```bash python tools/openbrain_client.py search "topic" python tools/openbrain_client.py capture "decision or observation" --source agent-name python tools/openbrain_client.py recent --limit 5 python tools/openbrain_client.py stats ``` **MCP (Claude Code sessions):** When connected, `mcp__openbrain__capture/search/recent/stats` tools are available natively — prefer those over CLI when in a Claude session. **Rule: capture when you LEARN something. Never when you DO something.** | Trigger | Action | Retention | | ----------------------------------------- | ----------------------------------------- | --------------------- | | Session start | `search` + `recent` to load prior context | — | | Architectural or tooling decision made | Capture with rationale | `long` or `permanent` | | Gotcha or non-obvious behavior discovered | Capture immediately | `medium` | | User preference stated or confirmed | Capture | `permanent` | | Cross-project pattern identified | Capture | `permanent` | | Prior decision superseded | UPDATE existing thought | (keep tier) | **Never capture:** task started, commit pushed, PR opened, test results, file edits, CI status. Full protocol and cleanup tools: `~/.config/mosaic/guides/MEMORY.md` Smart capture wrapper (enforces schema + dedup): `~/.config/mosaic/tools/openbrain/capture.sh` ### Excalidraw — Diagram Export (MCP) Headless `.excalidraw` → SVG export via `@excalidraw/excalidraw`. Available as MCP tools in Claude Code sessions. **MCP tools (when connected):** | Tool | Input | Output | | ----------------------------------------- | --------------------------------------------- | ---------------------------------------------------- | | `mcp__excalidraw__excalidraw_to_svg` | `elements` JSON string + optional `app_state` | SVG string | | `mcp__excalidraw__excalidraw_file_to_svg` | `file_path` to `.excalidraw` | SVG string + writes `.svg` alongside | | `mcp__excalidraw__list_diagrams` | (none) | Available templates (requires `EXCALIDRAW_GEN_PATH`) | | `mcp__excalidraw__generate_diagram` | `name`, optional `output_path` | Path to generated `.excalidraw` | | `mcp__excalidraw__generate_and_export` | `name`, optional `output_path` | Paths to `.excalidraw` and `.svg` | **Diagram generation** (`list_diagrams`, `generate_diagram`, `generate_and_export`) requires `EXCALIDRAW_GEN_PATH` env var pointing to `excalidraw_gen.py`. Set in environment or shell profile: ```bash export EXCALIDRAW_GEN_PATH="$HOME/.config/mosaic/tools/excalidraw/excalidraw_gen.py" ``` **Manual registration:** ```bash mosaic-ensure-excalidraw # install deps + register with Claude mosaic-ensure-excalidraw --check # verify registration ``` ## Git Providers | Instance | URL | CLI | Purpose | | ----------------------------- | --- | --- | ------- | | (add your git providers here) | | | | ## Credentials **Location:** (configure your credential file path) **Loader:** `source ~/.config/mosaic/tools/_lib/credentials.sh && load_credentials <service>` **Never expose actual values. Never commit credential files.** ## CLI Gotchas (Add platform-specific CLI gotchas as you discover them.) ## Safety Defaults - Prefer `trash` over `rm` when available — recoverable beats gone forever - Never run destructive commands without explicit instruction - Write it down — "mental notes" don't survive session restarts; files do