disinto/lib/AGENTS.md
Claude 514de48f58
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
chore: gardener housekeeping 2026-04-07
2026-04-07 18:05:41 +00:00

14 KiB

Shared Helpers (lib/)

All agents source lib/env.sh as their first action. Additional helpers are sourced as needed.

File What it provides Sourced by
lib/env.sh Loads .env, sets FACTORY_ROOT, exports project config (FORGE_REPO, PROJECT_NAME, etc.), defines log(), forge_api(), forge_api_all() (paginates all pages; accepts optional second TOKEN parameter, defaults to $FORGE_TOKEN; handles invalid/empty JSON responses gracefully — returns empty on parse error instead of crashing), woodpecker_api(), wpdb(), memory_guard() (skips agent if RAM < threshold). Auto-loads project TOML if PROJECT_TOML is set. Exports per-agent tokens (FORGE_PLANNER_TOKEN, FORGE_GARDENER_TOKEN, FORGE_VAULT_TOKEN, FORGE_SUPERVISOR_TOKEN, FORGE_PREDICTOR_TOKEN) — each falls back to $FORGE_TOKEN if not set. Vault-only token guard (AD-006): unset GITHUB_TOKEN CLAWHUB_TOKEN so agents never hold external-action tokens — only the runner container receives them. Container note: when DISINTO_CONTAINER=1, .env is NOT re-sourced — compose already injects env vars (including FORGE_URL=http://forgejo:3000) and re-sourcing would clobber them. Save/restore scope (#364): only FORGE_URL is preserved across .env re-sourcing (compose injects http://forgejo:3000, .env has http://localhost:3000). FORGE_TOKEN is NOT preserved so refreshed tokens in .env take effect immediately. Required env var: FORGE_PASS — bot password for git HTTP push (Forgejo 11.x rejects API tokens for git push, #361). Every agent
lib/ci-helpers.sh ci_passed() — returns 0 if CI state is "success" (or no CI configured). ci_required_for_pr() — returns 0 if PR has code files (CI required), 1 if non-code only (CI not required). is_infra_step() — returns 0 if a single CI step failure matches infra heuristics (clone/git exit 128, any exit 137, log timeout patterns). classify_pipeline_failure() — returns "infra <reason>" if any failed Woodpecker step matches infra heuristics via is_infra_step(), else "code". ensure_priority_label() — looks up (or creates) the priority label and returns its ID; caches in _PRIORITY_LABEL_ID. ci_commit_status <sha> — queries Woodpecker directly for CI state, falls back to forge commit status API. ci_pipeline_number <sha> — returns the Woodpecker pipeline number for a commit, falls back to parsing forge status target_url. ci_promote <repo_id> <pipeline_num> <environment> — promotes a pipeline to a named Woodpecker environment (vault-gated deployment: vault approves, vault-fire calls this — vault redesign in progress, see #73-#77). ci_get_logs <pipeline_number> [--step <name>] — reads CI logs from Woodpecker SQLite database via lib/ci-log-reader.py; outputs last 200 lines to stdout. Requires mounted woodpecker-data volume at /woodpecker-data. dev-poll, review-poll, review-pr
lib/ci-debug.sh CLI tool for Woodpecker CI: list, status, logs, failures subcommands. Not sourced — run directly. Humans / dev-agent (tool access)
lib/ci-log-reader.py Python tool: reads CI logs from Woodpecker SQLite database. <pipeline_number> [--step <name>] — returns last 200 lines from failed steps (or specified step). Used by ci_get_logs() in ci-helpers.sh. Requires WOODPECKER_DATA_DIR (default: /woodpecker-data). ci-helpers.sh
lib/load-project.sh Parses a projects/*.toml file into env vars (PROJECT_NAME, FORGE_REPO, WOODPECKER_REPO_ID, monitoring toggles, mirror config, etc.). Also exports FORGE_REPO_OWNER (the owner component of FORGE_REPO, e.g. disinto-admin from disinto-admin/disinto). env.sh (when PROJECT_TOML is set)
lib/parse-deps.sh Extracts dependency issue numbers from an issue body (stdin → stdout, one number per line). Matches ## Dependencies / ## Depends on / ## Blocked by sections and inline depends on #N / blocked by #N patterns. Inline scan skips fenced code blocks to prevent false positives from code examples in issue bodies. Not sourced — executed via bash lib/parse-deps.sh. dev-poll
lib/formula-session.sh acquire_cron_lock(), load_formula(), load_formula_or_profile(), build_context_block(), ensure_ops_repo(), ops_commit_and_push(), build_prompt_footer(), build_sdk_prompt_footer(), formula_worktree_setup(), formula_prepare_profile_context(), formula_lessons_block(), profile_write_journal(), profile_load_lessons(), ensure_profile_repo(), _profile_has_repo(), _count_undigested_journals(), _profile_digest_journals(), _profile_commit_and_push(), resolve_agent_identity(), build_graph_section(), build_scratch_instruction(), read_scratch_context(), cleanup_stale_crashed_worktrees() — shared helpers for formula-driven cron agents (lock, .profile repo management, prompt assembly, worktree setup). Memory guard is provided by memory_guard() in lib/env.sh (not duplicated here). resolve_agent_identity() — sets FORGE_TOKEN, AGENT_IDENTITY, FORGE_REMOTE from per-agent token env vars and FORGE_URL remote detection. build_graph_section() generates the structural-analysis section (runs lib/build-graph.py, formats JSON output) — previously duplicated in planner-run.sh and predictor-run.sh, now shared here. cleanup_stale_crashed_worktrees() — thin wrapper around worktree_cleanup_stale() from lib/worktree.sh (kept for backwards compatibility). planner-run.sh, predictor-run.sh, gardener-run.sh, supervisor-run.sh, dev-agent.sh
lib/guard.sh check_active(agent_name) — reads $FACTORY_ROOT/state/.{agent_name}-active; exits 0 (skip) if the file is absent. Factory is off by default — state files must be created to enable each agent. Logs a message to stderr when skipping ([check_active] SKIP: state file not found), so agent dropout is visible in cron logs. Sourced by dev-poll.sh, review-poll.sh, predictor-run.sh, supervisor-run.sh. cron entry points
lib/mirrors.sh mirror_push() — pushes $PRIMARY_BRANCH + tags to all configured mirror remotes (fire-and-forget background pushes). Reads MIRROR_NAMES and MIRROR_* vars exported by load-project.sh from the [mirrors] TOML section. Failures are logged but never block the pipeline. Sourced by dev-poll.sh — called after every successful merge. dev-poll.sh
lib/build-graph.py Python tool: parses VISION.md, prerequisites.md (from ops repo), AGENTS.md, formulas/*.toml, evidence/ (from ops repo), and forge issues/labels into a NetworkX DiGraph. Runs structural analyses (orphaned objectives, stale prerequisites, thin evidence, circular deps) and outputs a JSON report. Used by review-pr.sh (per-PR changed-file analysis) and predictor-run.sh (full-project analysis) to provide structural context to Claude. review-pr.sh, predictor-run.sh
lib/secret-scan.sh scan_for_secrets() — detects potential secrets (API keys, bearer tokens, private keys, URLs with embedded credentials) in text; returns 1 if secrets found. redact_secrets() — replaces detected secret patterns with [REDACTED]. issue-lifecycle.sh
lib/stack-lock.sh File-based lock protocol for singleton project stack access. stack_lock_acquire(holder, project) — polls until free, breaks stale heartbeats (>10 min old), claims lock. stack_lock_release(project) — deletes lock file. stack_lock_check(project) — inspect current lock state. stack_lock_heartbeat(project) — update heartbeat timestamp (callers must call every 2 min while holding). Lock files at ~/data/locks/<project>-stack.lock. docker/edge/dispatcher.sh, reproduce formula
lib/tea-helpers.sh tea_file_issue(title, body, labels...) — create issue via tea CLI with secret scanning; sets FILED_ISSUE_NUM. tea_relabel(issue_num, labels...) — replace labels using tea's edit subcommand (not label). tea_comment(issue_num, body) — add comment with secret scanning. tea_close(issue_num) — close issue. All use TEA_LOGIN and FORGE_REPO from env.sh. Labels by name (no ID lookup). Tea binary download verified via sha256 checksum. Sourced by env.sh when tea binary is available. env.sh (conditional)
lib/worktree.sh Reusable git worktree management: worktree_create(path, branch, [base_ref]) — create worktree, checkout base, fetch submodules. worktree_recover(path, branch, [remote]) — detect existing worktree, reuse if on correct branch (sets _WORKTREE_REUSED), otherwise clean and recreate. worktree_cleanup(path)git worktree remove --force, clear Claude Code project cache (~/.claude/projects/ matching path). worktree_cleanup_stale([max_age_hours]) — scan /tmp for orphaned worktrees older than threshold, skip preserved and active tmux worktrees, prune. worktree_preserve(path, reason) — mark worktree as preserved for debugging (writes .worktree-preserved marker, skipped by stale cleanup). dev-agent.sh, supervisor-run.sh, planner-run.sh, predictor-run.sh, gardener-run.sh
lib/pr-lifecycle.sh Reusable PR lifecycle library: pr_create(), pr_find_by_branch(), pr_poll_ci(), pr_poll_review(), pr_merge(), pr_is_merged(), pr_walk_to_merge(), build_phase_protocol_prompt(). Requires lib/ci-helpers.sh. dev-agent.sh (future)
lib/issue-lifecycle.sh Reusable issue lifecycle library: issue_claim() (add in-progress, remove backlog), issue_release() (remove in-progress, add backlog), issue_block() (post diagnostic comment with secret redaction, add blocked label), issue_close(), issue_check_deps() (parse deps, check transitive closure; sets _ISSUE_BLOCKED_BY, _ISSUE_SUGGESTION), issue_suggest_next() (find next unblocked backlog issue; sets _ISSUE_NEXT), issue_post_refusal() (structured refusal comment with dedup). Label IDs cached in globals on first lookup. Sources lib/secret-scan.sh. dev-agent.sh (future)
lib/vault.sh Vault PR helper — create vault action PRs on ops repo via Forgejo API (works from containers without SSH). vault_request <action_id> <toml_content> validates TOML (using validate_vault_action from vault/vault-env.sh), creates branch vault/<action-id>, writes vault/actions/<action-id>.toml, creates PR targeting main with title vault: <action-id> and body from context field, returns PR number. Idempotent: if PR exists, returns existing number. Requires FORGE_TOKEN, FORGE_URL, FORGE_REPO, FORGE_OPS_REPO. Uses the calling agent's own token (saves/restores FORGE_TOKEN around sourcing vault-env.sh), so approval workflow respects individual agent identities. dev-agent (vault actions), future vault dispatcher
lib/branch-protection.sh Branch protection helpers for Forgejo repos. setup_vault_branch_protection() — configures admin-only merge protection on main (require 1 approval, restrict merge to admin role, block direct pushes). setup_profile_branch_protection() — same protection for .profile repos. verify_branch_protection() — checks protection is correctly configured. remove_branch_protection() — removes protection (cleanup/testing). Handles race condition after initial push: retries with backoff if Forgejo hasn't processed the branch yet. Requires FORGE_TOKEN, FORGE_URL, FORGE_OPS_REPO. bin/disinto (hire-an-agent)
lib/agent-sdk.sh agent_run([--resume SESSION_ID] [--worktree DIR] PROMPT) — one-shot claude -p invocation with session persistence. Saves session ID to SID_FILE, reads it back on resume. agent_recover_session() — restore previous session ID from SID_FILE on startup. Nudge guard: skips nudge injection if the worktree is clean and no push is expected, preventing spurious re-invocations. Callers must define SID_FILE, LOGFILE, and log() before sourcing. formula-driven agents (dev-agent, planner-run, predictor-run, gardener-run)
lib/forge-setup.sh setup_forge() — Forgejo instance provisioning: creates admin user, bot accounts, org, repos (code + ops), configures webhooks, sets repo topics. Extracted from bin/disinto. Requires FORGE_URL, FORGE_TOKEN, FACTORY_ROOT. Password storage (#361): after creating each bot account, stores its password in .env as FORGE_<BOT>_PASS (e.g. FORGE_PASS, FORGE_REVIEW_PASS, etc.) for use by forge-push.sh. bin/disinto (init)
lib/forge-push.sh push_to_forge() — pushes a local clone to the Forgejo remote and verifies the push. _assert_forge_push_globals() validates required env vars before use. Requires FORGE_URL, FORGE_PASS, FACTORY_ROOT, PRIMARY_BRANCH. Auth: uses FORGE_PASS (bot password) for git HTTP push — Forgejo 11.x rejects API tokens for git push (#361). bin/disinto (init)
lib/ops-setup.sh setup_ops_repo() — creates ops repo on Forgejo if it doesn't exist, configures bot collaborators, clones/initializes ops repo locally, seeds directory structure (vault, knowledge, evidence). Exports _ACTUAL_OPS_SLUG. bin/disinto (init)
lib/ci-setup.sh _install_cron_impl() — installs crontab entries for project agents. _create_woodpecker_oauth_impl() — creates OAuth2 app on Forgejo for Woodpecker. _generate_woodpecker_token_impl() — auto-generates WOODPECKER_TOKEN via OAuth2 flow. _activate_woodpecker_repo_impl() — activates repo in Woodpecker. All gated by _load_ci_context() which validates required env vars. bin/disinto (init)
lib/generators.sh Template generation for disinto init: generate_compose() — docker-compose.yml, generate_caddyfile() — Caddyfile, generate_staging_index() — staging index, generate_deploy_pipelines() — Woodpecker deployment pipeline configs. Requires FACTORY_ROOT, PROJECT_NAME, PRIMARY_BRANCH. bin/disinto (init)
lib/hire-agent.sh disinto_hire_an_agent() — user creation, .profile repo setup, formula copying, branch protection, and state marker creation for hiring a new agent. Requires FORGE_URL, FORGE_TOKEN, FACTORY_ROOT, PROJECT_NAME. Extracted from bin/disinto. bin/disinto (hire)
lib/release.sh disinto_release() — vault TOML creation, branch setup on ops repo, PR creation, and auto-merge request for a versioned release. _assert_release_globals() validates required env vars. Requires FORGE_URL, FORGE_TOKEN, FORGE_OPS_REPO, FACTORY_ROOT, PRIMARY_BRANCH. Extracted from bin/disinto. bin/disinto (release)