diff --git a/disinto-factory/SKILL.md b/disinto-factory/SKILL.md index 8c6a672..8e17508 100644 --- a/disinto-factory/SKILL.md +++ b/disinto-factory/SKILL.md @@ -9,8 +9,6 @@ You are helping the user set up and operate a **disinto autonomous code factory* of bash scripts and Claude CLI that automates the full development lifecycle: picking up issues, implementing via Claude, creating PRs, running CI, reviewing, merging, and mirroring. -This guide shows how to set up the factory to develop an **external project** (e.g., `johba/harb`). - ## First-time setup Walk the user through these steps interactively. Ask questions where marked with [ASK]. @@ -29,34 +27,20 @@ docker --version && git --version && jq --version && curl --version && tmux -V & Any missing tool — help the user install it before continuing. -### 2. Clone disinto and choose a target project +### 2. Clone and init -Clone the disinto factory itself: ```bash git clone https://codeberg.org/johba/disinto.git && cd disinto ``` -[ASK] What repository should the factory develop? Provide the **remote repository URL** in one of these formats: -- Full URL: `https://github.com/johba/harb.git` or `https://codeberg.org/johba/harb.git` -- Short slug: `johba/harb` (uses local Forgejo as the primary remote) +[ASK] What repo should the factory develop? Options: +- **Itself** (self-development): `bin/disinto init https://codeberg.org/johba/disinto --yes --repo-root $(pwd)` +- **Another project**: `bin/disinto init --yes` -The factory will clone from the remote URL (if provided) or from your local Forgejo, then mirror to the remote. - -Then initialize the factory for that project: -```bash -bin/disinto init johba/harb --yes -# or with full URL: -bin/disinto init https://github.com/johba/harb.git --yes -``` - -The `init` command will: -- Create all bot users (dev-bot, review-bot, etc.) on the local Forgejo -- Generate and save `WOODPECKER_TOKEN` -- Start the stack containers -- Clone the target repo into the agent workspace - -> **Note:** The `--repo-root` flag is optional and only needed if you want to customize -> where the cloned repo lives. By default, it goes under `/home/agent/repos/`. +Run the init and watch for: +- All bot users created (dev-bot, review-bot, etc.) +- `WOODPECKER_TOKEN` generated and saved +- Stack containers all started ### 3. Post-init verification @@ -86,48 +70,7 @@ docker exec disinto-agents-1 chown -R agent:agent /home/agent/repos docker exec -u agent disinto-agents-1 bash -c "source /home/agent/disinto/.env && git clone http://dev-bot:\${FORGE_TOKEN}@forgejo:3000//.git /home/agent/repos/" ``` -### 4. Create the project configuration file - -The factory uses a TOML file to configure how it manages your project. Create -`projects/.toml` based on the template format: - -```toml -# projects/harb.toml - -name = "harb" -repo = "johba/harb" -forge_url = "http://localhost:3000" -repo_root = "/home/agent/repos/harb" -primary_branch = "master" - -[ci] -woodpecker_repo_id = 0 -stale_minutes = 60 - -[services] -containers = ["ponder"] - -[monitoring] -check_prs = true -check_dev_agent = true -check_pipeline_stall = true - -# [mirrors] -# github = "git@github.com:johba/harb.git" -# codeberg = "git@codeberg.org:johba/harb.git" -``` - -**Key fields:** -- `name`: Project identifier (used for file names, logs, etc.) -- `repo`: The source repo in `owner/name` format -- `forge_url`: URL of your local Forgejo instance -- `repo_root`: Where the agent clones the repo -- `primary_branch`: Default branch name (e.g., `main` or `master`) -- `woodpecker_repo_id`: Set to `0` initially; auto-populated on first CI run -- `containers`: List of Docker containers the factory should manage -- `mirrors`: Optional external forge URLs for backup/sync - -### 5. Mirrors (optional) +### 4. Mirrors (optional) [ASK] Should the factory mirror to external forges? If yes, which? - GitHub: need repo URL and SSH key added to GitHub account @@ -145,7 +88,7 @@ ssh -T git@github.com 2>&1; ssh -T git@codeberg.org 2>&1 If SSH host keys are missing: `ssh-keyscan github.com codeberg.org >> ~/.ssh/known_hosts 2>/dev/null` -Edit `projects/.toml` to uncomment and configure mirrors: +Edit `projects/.toml` to add mirrors: ```toml [mirrors] github = "git@github.com:Org/repo.git" @@ -157,7 +100,7 @@ Test with a manual push: source .env && source lib/env.sh && export PROJECT_TOML=projects/.toml && source lib/load-project.sh && source lib/mirrors.sh && mirror_push ``` -### 6. Seed the backlog +### 5. Seed the backlog [ASK] What should the factory work on first? Brainstorm with the user. @@ -185,12 +128,10 @@ Use labels: - `blocked` — parked, not for the factory - No label — tracked but not for autonomous work -### 7. Watch it work +### 6. Watch it work The dev-agent polls every 5 minutes. Trigger manually to see it immediately: ```bash -source .env -export PROJECT_TOML=projects/.toml docker exec -u agent disinto-agents-1 bash -c "cd /home/agent/disinto && bash dev/dev-poll.sh projects/.toml" ``` diff --git a/lib/env.sh b/lib/env.sh index cfaa523..48cd541 100755 --- a/lib/env.sh +++ b/lib/env.sh @@ -30,18 +30,13 @@ if [ -f "$FACTORY_ROOT/.env.enc" ] && command -v sops &>/dev/null; then _saved_forge_url="${FORGE_URL:-}" _saved_forge_token="${FORGE_TOKEN:-}" # Use temp file + validate dotenv format before sourcing (avoids eval injection) - _tmpenv=$(mktemp) || { echo "Warning: failed to create temp file for .env.enc" >&2; exit 1; } + _tmpenv=$(mktemp) || { echo "Warning: failed to create temp file for .env.enc" >&2; } if sops -d --output-type dotenv "$FACTORY_ROOT/.env.enc" > "$_tmpenv" 2>/dev/null; then - # Validate: non-empty, non-comment lines must match KEY=value pattern - # Filter out blank lines and comments before validation - _validated=$(grep -E '^[A-Za-z_][A-Za-z0-9_]*=' "$_tmpenv" 2>/dev/null || true) - if [ -n "$_validated" ]; then - # Write validated content to a second temp file and source it - _validated_env=$(mktemp) - printf '%s\n' "$_validated" > "$_validated_env" + # Validate: each non-empty, non-comment line must match KEY=value pattern + if grep -qE '^[A-Za-z_][A-Za-z0-9_]*=' "$_tmpenv" 2>/dev/null && \ + ! grep -qE '^[^A-Za-z_]' "$_tmpenv" 2>/dev/null; then # shellcheck source=/dev/null - source "$_validated_env" - rm -f "$_validated_env" + source "$_tmpenv" else echo "Warning: .env.enc decryption output failed format validation" >&2 fi diff --git a/lib/mirrors.sh b/lib/mirrors.sh index 3ba561d..1868dba 100644 --- a/lib/mirrors.sh +++ b/lib/mirrors.sh @@ -20,9 +20,8 @@ mirror_push() { if [[ ! "$upper_name" =~ ^[A-Z_][A-Z0-9_]*$ ]]; then continue fi - # Use indirect expansion safely (no eval) — MIRROR_ prefix required - local varname="MIRROR_${upper_name}" - url="${!varname:-}" + # Use indirect expansion safely (no eval) + url="${!upper_name:-}" [ -z "$url" ] && continue # Ensure remote exists with correct URL