chore: delete obsolete skill/ folder — replaced by disinto-factory/
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
The old skill/ reflects tmux-based pre-containerization architecture. disinto-factory/ is the current skill with Docker Compose setup. Closes #16 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7fd61e9d0e
commit
c1939fbb9a
5 changed files with 0 additions and 669 deletions
350
skill/SKILL.md
350
skill/SKILL.md
|
|
@ -1,350 +0,0 @@
|
||||||
---
|
|
||||||
name: disinto
|
|
||||||
description: >-
|
|
||||||
Operate the disinto autonomous code factory. Use when bootstrapping a new
|
|
||||||
project with `disinto init`, managing factory agents, filing issues on the
|
|
||||||
forge, reading agent journals, querying CI pipelines, checking the dependency
|
|
||||||
graph, or inspecting factory health.
|
|
||||||
license: AGPL-3.0
|
|
||||||
metadata:
|
|
||||||
author: johba
|
|
||||||
version: "0.2.0"
|
|
||||||
env_vars:
|
|
||||||
required:
|
|
||||||
- FORGE_TOKEN
|
|
||||||
- FORGE_API
|
|
||||||
- PROJECT_REPO_ROOT
|
|
||||||
optional:
|
|
||||||
- WOODPECKER_SERVER
|
|
||||||
- WOODPECKER_TOKEN
|
|
||||||
- WOODPECKER_REPO_ID
|
|
||||||
tools:
|
|
||||||
- bash
|
|
||||||
- curl
|
|
||||||
- jq
|
|
||||||
- git
|
|
||||||
---
|
|
||||||
|
|
||||||
# Disinto Factory Skill
|
|
||||||
|
|
||||||
You are the human's assistant for operating the disinto autonomous code factory.
|
|
||||||
You ask the questions, explain the choices, and run the commands on the human's
|
|
||||||
behalf. The human makes decisions; you execute.
|
|
||||||
|
|
||||||
Disinto manages eight agents that implement issues, review PRs, plan from a
|
|
||||||
vision, predict risks, groom the backlog, gate actions, and keep the system
|
|
||||||
healthy — all driven by cron and Claude.
|
|
||||||
|
|
||||||
## System requirements
|
|
||||||
|
|
||||||
Before bootstrapping, verify the target machine meets these minimums:
|
|
||||||
|
|
||||||
| Requirement | Detail |
|
|
||||||
|-------------|--------|
|
|
||||||
| **VPS** | 8 GB+ RAM (4 GB swap recommended) |
|
|
||||||
| **Docker + Docker Compose** | Required for the default containerized stack |
|
|
||||||
| **Claude Code CLI** | Authenticated with API access (`claude --version`) |
|
|
||||||
| **`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`** | Set in the factory environment — prevents auto-update pings in production |
|
|
||||||
| **Disk** | Sufficient for CI images, git mirrors, and agent worktrees (40 GB+ recommended) |
|
|
||||||
| **tmux** | Required for persistent dev sessions |
|
|
||||||
| **git, jq, python3, curl** | Used by agents and helper scripts |
|
|
||||||
|
|
||||||
Optional but recommended:
|
|
||||||
|
|
||||||
| Tool | Purpose |
|
|
||||||
|------|---------|
|
|
||||||
| **sops + age** | Encrypt secrets at rest (`.env.enc`) |
|
|
||||||
|
|
||||||
## Bootstrapping with `disinto init`
|
|
||||||
|
|
||||||
The primary setup path. Walk the human through each step.
|
|
||||||
|
|
||||||
### Step 1 — Check prerequisites
|
|
||||||
|
|
||||||
Confirm Docker, Claude Code CLI, and required tools are installed:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker --version && docker compose version
|
|
||||||
claude --version
|
|
||||||
tmux -V && git --version && jq --version && python3 --version
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 2 — Run `disinto init`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
disinto init <repo-url>
|
|
||||||
```
|
|
||||||
|
|
||||||
Accepts GitHub, Codeberg, or any git URL. Common variations:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
disinto init https://github.com/org/repo # default (docker compose)
|
|
||||||
disinto init org/repo --forge-url http://forge:3000 # custom forge URL
|
|
||||||
disinto init org/repo --bare # bare-metal, no compose
|
|
||||||
disinto init org/repo --yes # skip confirmation prompts
|
|
||||||
```
|
|
||||||
|
|
||||||
### What `disinto init` does
|
|
||||||
|
|
||||||
1. **Generates `docker-compose.yml`** with four services: Forgejo, Woodpecker
|
|
||||||
server, Woodpecker agent, and the agents container.
|
|
||||||
2. **Starts a local Forgejo instance** via Docker (at `http://localhost:3000`).
|
|
||||||
3. **Creates admin + bot users** (dev-bot, review-bot) with API tokens.
|
|
||||||
4. **Creates the repo** on Forgejo and pushes the code.
|
|
||||||
5. **Sets up Woodpecker CI** — OAuth2 app on Forgejo, activates the repo.
|
|
||||||
6. **Generates `projects/<name>.toml`** — per-project config with paths, CI IDs,
|
|
||||||
and forge URL.
|
|
||||||
7. **Creates standard labels** (backlog, in-progress, blocked, etc.).
|
|
||||||
8. **Configures git mirror remotes** if `[mirrors]` is set in the TOML.
|
|
||||||
9. **Encrypts secrets** to `.env.enc` if sops + age are available.
|
|
||||||
10. **Brings up the full docker compose stack**.
|
|
||||||
|
|
||||||
### Step 3 — Set environment variable
|
|
||||||
|
|
||||||
Ensure `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` is set in the factory
|
|
||||||
environment (`.env` or the agents container). This prevents Claude Code from
|
|
||||||
making auto-update and telemetry requests in production.
|
|
||||||
|
|
||||||
### Step 4 — Verify
|
|
||||||
|
|
||||||
```bash
|
|
||||||
disinto status
|
|
||||||
```
|
|
||||||
|
|
||||||
## Docker stack architecture
|
|
||||||
|
|
||||||
The default deployment is a docker-compose stack with four services:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────────┐
|
|
||||||
│ disinto-net │
|
|
||||||
│ │
|
|
||||||
│ ┌──────────┐ ┌─────────────┐ ┌────────────┐ │
|
|
||||||
│ │ Forgejo │ │ Woodpecker │ │ Woodpecker │ │
|
|
||||||
│ │ (forge) │◀─│ (CI server)│◀─│ (agent) │ │
|
|
||||||
│ │ :3000 │ │ :8000 │ │ │ │
|
|
||||||
│ └──────────┘ └─────────────┘ └────────────┘ │
|
|
||||||
│ ▲ │
|
|
||||||
│ │ │
|
|
||||||
│ ┌─────┴──────────────────────────────────────┐ │
|
|
||||||
│ │ agents │ │
|
|
||||||
│ │ (cron → dev, review, gardener, planner, │ │
|
|
||||||
│ │ predictor, supervisor, action, vault) │ │
|
|
||||||
│ │ Claude CLI mounted from host │ │
|
|
||||||
│ └────────────────────────────────────────────┘ │
|
|
||||||
└──────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
| Service | Image | Purpose |
|
|
||||||
|---------|-------|---------|
|
|
||||||
| **forgejo** | `codeberg.org/forgejo/forgejo:11.0` | Git forge, issue tracker, PR reviews |
|
|
||||||
| **woodpecker** | `woodpeckerci/woodpecker-server:v3` | CI server, triggers on push |
|
|
||||||
| **woodpecker-agent** | `woodpeckerci/woodpecker-agent:v3` | Runs CI pipelines in Docker |
|
|
||||||
| **agents** | `./docker/agents` (custom) | All eight factory agents, driven by cron |
|
|
||||||
|
|
||||||
The agents container mounts the Claude CLI binary and `~/.claude` credentials
|
|
||||||
from the host. Secrets are loaded from `.env` (or decrypted from `.env.enc`).
|
|
||||||
|
|
||||||
## Git mirror
|
|
||||||
|
|
||||||
The factory assumes a local git mirror on the Forgejo instance to avoid
|
|
||||||
rate limits from upstream forges (GitHub, Codeberg). When `disinto init` runs:
|
|
||||||
|
|
||||||
1. The repo is cloned from the upstream URL.
|
|
||||||
2. A `forgejo` remote is added pointing to the local Forgejo instance.
|
|
||||||
3. All branches and tags are pushed to Forgejo.
|
|
||||||
4. If `[mirrors]` is configured in the project TOML, additional remotes
|
|
||||||
(e.g. GitHub, Codeberg) are set up and synced via `lib/mirrors.sh`.
|
|
||||||
|
|
||||||
All agent work happens against the local Forgejo forge. This means:
|
|
||||||
- No GitHub/Codeberg API rate limits on polling.
|
|
||||||
- CI triggers are local (Woodpecker watches Forgejo webhooks).
|
|
||||||
- Mirror pushes are fire-and-forget background operations after merge.
|
|
||||||
|
|
||||||
To configure mirrors in the project TOML:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[mirrors]
|
|
||||||
github = "git@github.com:user/repo.git"
|
|
||||||
codeberg = "git@codeberg.org:user/repo.git"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Required environment
|
|
||||||
|
|
||||||
| Variable | Purpose |
|
|
||||||
|----------|---------|
|
|
||||||
| `FORGE_TOKEN` | Forgejo/Gitea API token with repo scope |
|
|
||||||
| `FORGE_API` | Base API URL, e.g. `https://forge.example/api/v1/repos/owner/repo` |
|
|
||||||
| `PROJECT_REPO_ROOT` | Absolute path to the checked-out disinto repository |
|
|
||||||
|
|
||||||
Optional:
|
|
||||||
|
|
||||||
| Variable | Purpose |
|
|
||||||
|----------|---------|
|
|
||||||
| `WOODPECKER_SERVER` | Woodpecker CI base URL (for pipeline queries) |
|
|
||||||
| `WOODPECKER_TOKEN` | Woodpecker API bearer token |
|
|
||||||
| `WOODPECKER_REPO_ID` | Numeric repo ID in Woodpecker |
|
|
||||||
|
|
||||||
## The eight agents
|
|
||||||
|
|
||||||
| Agent | Role | Runs via |
|
|
||||||
|-------|------|----------|
|
|
||||||
| **Dev** | Picks backlog issues, implements in worktrees, opens PRs | `dev/dev-poll.sh` (cron) |
|
|
||||||
| **Review** | Reviews PRs against conventions, approves or requests changes | `review/review-poll.sh` (cron) |
|
|
||||||
| **Gardener** | Grooms backlog: dedup, quality gates, dust bundling, stale cleanup | `gardener/gardener-run.sh` (cron 0,6,12,18 UTC) |
|
|
||||||
| **Planner** | Tracks vision progress, maintains prerequisite tree, files constraint issues | `planner/planner-run.sh` (cron daily 07:00 UTC) |
|
|
||||||
| **Predictor** | Challenges claims, detects structural risks, files predictions | `predictor/predictor-run.sh` (cron daily 06:00 UTC) |
|
|
||||||
| **Supervisor** | Monitors health (RAM, disk, CI, agents), auto-fixes, escalates | `supervisor/supervisor-run.sh` (cron */20) |
|
|
||||||
| **Action** | Executes operational tasks dispatched by planner via formulas | `action/action-poll.sh` (cron) |
|
|
||||||
| **Vault** | Gates dangerous actions, manages resource procurement | `vault/vault-poll.sh` (cron) |
|
|
||||||
|
|
||||||
### How agents interact
|
|
||||||
|
|
||||||
```
|
|
||||||
Planner ──creates-issues──▶ Backlog ◀──grooms── Gardener
|
|
||||||
│ │
|
|
||||||
│ ▼
|
|
||||||
│ Dev (implements)
|
|
||||||
│ │
|
|
||||||
│ ▼
|
|
||||||
│ Review (approves/rejects)
|
|
||||||
│ │
|
|
||||||
│ ▼
|
|
||||||
▼ Merged
|
|
||||||
Predictor ──challenges──▶ Planner (triages predictions)
|
|
||||||
Supervisor ──monitors──▶ All agents (health, escalation)
|
|
||||||
Vault ──gates──▶ Action, Dev (dangerous operations)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue lifecycle
|
|
||||||
|
|
||||||
`backlog` → `in-progress` → PR → CI → review → merge → closed.
|
|
||||||
|
|
||||||
Key labels: `backlog`, `priority`, `in-progress`, `blocked`, `underspecified`,
|
|
||||||
`tech-debt`, `vision`, `action`, `prediction/unreviewed`.
|
|
||||||
|
|
||||||
Issues declare dependencies in a `## Dependencies` section listing `#N`
|
|
||||||
references. Dev-poll only picks issues whose dependencies are all closed.
|
|
||||||
|
|
||||||
## Available scripts
|
|
||||||
|
|
||||||
- **`scripts/factory-status.sh`** — Show agent status, open issues, and CI
|
|
||||||
pipeline state. Pass `--agents`, `--issues`, or `--ci` for specific sections.
|
|
||||||
- **`scripts/file-issue.sh`** — Create an issue on the forge with proper labels
|
|
||||||
and formatting. Pass `--title`, `--body`, and optionally `--labels`.
|
|
||||||
- **`scripts/read-journal.sh`** — Read agent journal entries. Pass agent name
|
|
||||||
(`planner`, `supervisor`) and optional `--date YYYY-MM-DD`.
|
|
||||||
|
|
||||||
## Common workflows
|
|
||||||
|
|
||||||
### 1. Bootstrap a new project
|
|
||||||
|
|
||||||
Walk the human through `disinto init`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Verify prerequisites
|
|
||||||
docker --version && claude --version
|
|
||||||
|
|
||||||
# 2. Bootstrap
|
|
||||||
disinto init https://github.com/org/repo
|
|
||||||
|
|
||||||
# 3. Verify
|
|
||||||
disinto status
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Check factory health
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bash scripts/factory-status.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
This shows: which agents are active, recent open issues, and CI pipeline
|
|
||||||
status. Use `--agents` for just the agent status section.
|
|
||||||
|
|
||||||
### 3. Read what the planner decided today
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bash scripts/read-journal.sh planner
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns today's planner journal: predictions triaged, prerequisite tree
|
|
||||||
updates, top constraints, issues created, and observations.
|
|
||||||
|
|
||||||
### 4. File a new issue
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bash scripts/file-issue.sh --title "fix: broken auth flow" \
|
|
||||||
--body "$(cat scripts/../templates/issue-template.md)" \
|
|
||||||
--labels backlog
|
|
||||||
```
|
|
||||||
|
|
||||||
Or generate the body inline — the template shows the expected format with
|
|
||||||
acceptance criteria and affected files sections.
|
|
||||||
|
|
||||||
### 5. Check the dependency graph
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 "${PROJECT_REPO_ROOT}/lib/build-graph.py" \
|
|
||||||
--project-root "${PROJECT_REPO_ROOT}" \
|
|
||||||
--output /tmp/graph-report.json
|
|
||||||
cat /tmp/graph-report.json | jq '.analyses'
|
|
||||||
```
|
|
||||||
|
|
||||||
The graph builder parses VISION.md, the prerequisite tree, formulas, and open
|
|
||||||
issues. It detects: orphan issues (not referenced), dependency cycles,
|
|
||||||
disconnected clusters, bottleneck nodes, and thin objectives.
|
|
||||||
|
|
||||||
### 6. Query a specific CI pipeline
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bash scripts/factory-status.sh --ci
|
|
||||||
```
|
|
||||||
|
|
||||||
Or query Woodpecker directly:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -s -H "Authorization: Bearer ${WOODPECKER_TOKEN}" \
|
|
||||||
"${WOODPECKER_SERVER}/api/repos/${WOODPECKER_REPO_ID}/pipelines?per_page=5" \
|
|
||||||
| jq '.[] | {number, status, commit: .commit[:8], branch}'
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7. Manage the docker stack
|
|
||||||
|
|
||||||
```bash
|
|
||||||
disinto up # start all services
|
|
||||||
disinto down # stop all services
|
|
||||||
disinto logs # tail all service logs
|
|
||||||
disinto logs forgejo # tail specific service
|
|
||||||
disinto shell # shell into agents container
|
|
||||||
```
|
|
||||||
|
|
||||||
### 8. Read and interpret VISION.md progress
|
|
||||||
|
|
||||||
Read `VISION.md` at the repo root for the full vision. Then cross-reference
|
|
||||||
with the prerequisite tree:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat "${OPS_REPO_ROOT}/prerequisites.md"
|
|
||||||
```
|
|
||||||
|
|
||||||
The prerequisite tree maps vision objectives to concrete issues. Items marked
|
|
||||||
`[x]` are complete; items marked `[ ]` show what blocks progress. The planner
|
|
||||||
updates this daily.
|
|
||||||
|
|
||||||
## Gotchas
|
|
||||||
|
|
||||||
- **Single-threaded pipeline**: only one issue is in-progress per project at a
|
|
||||||
time. Don't file issues expecting parallel work.
|
|
||||||
- **Secrets via env vars only**: never embed secrets in issue bodies, PR
|
|
||||||
descriptions, or comments. Use `$VAR_NAME` references.
|
|
||||||
- **Formulas are not skills**: formulas in `formulas/` are TOML issue templates
|
|
||||||
for multi-step agent tasks. Skills teach assistants; formulas drive agents.
|
|
||||||
- **Predictor journals**: the predictor does not write journal files. Its memory
|
|
||||||
lives in `prediction/unreviewed` and `prediction/actioned` issues.
|
|
||||||
- **State files**: agent activity is tracked via `state/.{agent}-active` files.
|
|
||||||
These are presence files, not logs.
|
|
||||||
- **ShellCheck required**: all `.sh` files must pass ShellCheck. CI enforces this.
|
|
||||||
- **Local forge is the source of truth**: all agent work targets the local
|
|
||||||
Forgejo instance. Upstream mirrors are synced after merge.
|
|
||||||
- **`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`**: must be set in production
|
|
||||||
to prevent Claude Code from making auto-update requests.
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# factory-status.sh — query agent status, open issues, and CI pipelines
|
|
||||||
#
|
|
||||||
# Usage: factory-status.sh [--agents] [--issues] [--ci] [--help]
|
|
||||||
# No flags: show all sections
|
|
||||||
# --agents: show only agent activity status
|
|
||||||
# --issues: show only open issues summary
|
|
||||||
# --ci: show only CI pipeline status
|
|
||||||
#
|
|
||||||
# Required env: FORGE_TOKEN, FORGE_API, PROJECT_REPO_ROOT
|
|
||||||
# Optional env: WOODPECKER_SERVER, WOODPECKER_TOKEN, WOODPECKER_REPO_ID
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
sed -n '3,10s/^# //p' "$0"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
show_agents=false
|
|
||||||
show_issues=false
|
|
||||||
show_ci=false
|
|
||||||
show_all=true
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
--agents) show_agents=true; show_all=false; shift ;;
|
|
||||||
--issues) show_issues=true; show_all=false; shift ;;
|
|
||||||
--ci) show_ci=true; show_all=false; shift ;;
|
|
||||||
--help|-h) usage ;;
|
|
||||||
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
: "${FORGE_TOKEN:?FORGE_TOKEN is required}"
|
|
||||||
: "${FORGE_API:?FORGE_API is required}"
|
|
||||||
: "${PROJECT_REPO_ROOT:?PROJECT_REPO_ROOT is required}"
|
|
||||||
|
|
||||||
forge_get() {
|
|
||||||
curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
|
||||||
-H "Accept: application/json" \
|
|
||||||
"${FORGE_API}$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Agent status ---
|
|
||||||
print_agent_status() {
|
|
||||||
echo "## Agent Status"
|
|
||||||
echo ""
|
|
||||||
local state_dir="${PROJECT_REPO_ROOT}/state"
|
|
||||||
local agents=(dev review gardener supervisor planner predictor action vault)
|
|
||||||
for agent in "${agents[@]}"; do
|
|
||||||
local state_file="${state_dir}/.${agent}-active"
|
|
||||||
if [[ -f "$state_file" ]]; then
|
|
||||||
echo " ${agent}: ACTIVE (since $(stat -c '%y' "$state_file" 2>/dev/null | cut -d. -f1 || echo 'unknown'))"
|
|
||||||
else
|
|
||||||
echo " ${agent}: idle"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Open issues ---
|
|
||||||
print_open_issues() {
|
|
||||||
echo "## Open Issues"
|
|
||||||
echo ""
|
|
||||||
local issues
|
|
||||||
issues=$(forge_get "/issues?state=open&type=issues&limit=50&sort=created&direction=desc" 2>/dev/null) || {
|
|
||||||
echo " (failed to fetch issues from forge)"
|
|
||||||
echo ""
|
|
||||||
return
|
|
||||||
}
|
|
||||||
local count
|
|
||||||
count=$(echo "$issues" | jq 'length')
|
|
||||||
echo " Total open: ${count}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Group by key labels
|
|
||||||
for label in backlog priority in-progress blocked; do
|
|
||||||
local labeled
|
|
||||||
labeled=$(echo "$issues" | jq --arg l "$label" '[.[] | select(.labels[]?.name == $l)]')
|
|
||||||
local n
|
|
||||||
n=$(echo "$labeled" | jq 'length')
|
|
||||||
if [[ "$n" -gt 0 ]]; then
|
|
||||||
echo " [${label}] (${n}):"
|
|
||||||
echo "$labeled" | jq -r '.[] | " #\(.number) \(.title)"' | head -10
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- CI pipelines ---
|
|
||||||
print_ci_status() {
|
|
||||||
echo "## CI Pipelines"
|
|
||||||
echo ""
|
|
||||||
if [[ -z "${WOODPECKER_SERVER:-}" || -z "${WOODPECKER_TOKEN:-}" || -z "${WOODPECKER_REPO_ID:-}" ]]; then
|
|
||||||
echo " (Woodpecker not configured — set WOODPECKER_SERVER, WOODPECKER_TOKEN, WOODPECKER_REPO_ID)"
|
|
||||||
echo ""
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
local pipelines
|
|
||||||
pipelines=$(curl -sf -H "Authorization: Bearer ${WOODPECKER_TOKEN}" \
|
|
||||||
"${WOODPECKER_SERVER}/api/repos/${WOODPECKER_REPO_ID}/pipelines?per_page=10" 2>/dev/null) || {
|
|
||||||
echo " (failed to fetch pipelines from Woodpecker)"
|
|
||||||
echo ""
|
|
||||||
return
|
|
||||||
}
|
|
||||||
echo "$pipelines" | jq -r '.[] | " #\(.number) [\(.status)] \(.branch) \(.commit[:8]) — \(.message // "" | split("\n")[0])"' | head -10
|
|
||||||
echo ""
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Output ---
|
|
||||||
if $show_all || $show_agents; then print_agent_status; fi
|
|
||||||
if $show_all || $show_issues; then print_open_issues; fi
|
|
||||||
if $show_all || $show_ci; then print_ci_status; fi
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# file-issue.sh — create an issue on the forge with labels
|
|
||||||
#
|
|
||||||
# Usage: file-issue.sh --title TITLE --body BODY [--labels LABEL1,LABEL2] [--help]
|
|
||||||
#
|
|
||||||
# Required env: FORGE_TOKEN, FORGE_API
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
sed -n '3,8s/^# //p' "$0"
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
title=""
|
|
||||||
body=""
|
|
||||||
labels=""
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
--title) title="$2"; shift 2 ;;
|
|
||||||
--body) body="$2"; shift 2 ;;
|
|
||||||
--labels) labels="$2"; shift 2 ;;
|
|
||||||
--help|-h) usage ;;
|
|
||||||
*) printf 'file-issue: unknown option: %s\n' "$1" >&2; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
: "${FORGE_TOKEN:?FORGE_TOKEN is required}"
|
|
||||||
: "${FORGE_API:?FORGE_API is required}"
|
|
||||||
|
|
||||||
if [[ -z "$title" ]]; then
|
|
||||||
echo "Error: --title is required" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [[ -z "$body" ]]; then
|
|
||||||
echo "Error: --body is required" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Resolve label names to IDs ---
|
|
||||||
label_ids="[]"
|
|
||||||
if [[ -n "$labels" ]]; then
|
|
||||||
all_labels=$(curl -sf -H "Authorization: token ${FORGE_TOKEN}" \
|
|
||||||
-H "Accept: application/json" \
|
|
||||||
"${FORGE_API}/labels?limit=50" 2>/dev/null) || {
|
|
||||||
echo "Warning: could not fetch labels, creating issue without labels" >&2
|
|
||||||
all_labels="[]"
|
|
||||||
}
|
|
||||||
label_ids="["
|
|
||||||
first=true
|
|
||||||
IFS=',' read -ra label_arr <<< "$labels"
|
|
||||||
for lname in "${label_arr[@]}"; do
|
|
||||||
lname=$(echo "$lname" | xargs) # trim whitespace
|
|
||||||
lid=$(echo "$all_labels" | jq -r --arg n "$lname" '.[] | select(.name == $n) | .id')
|
|
||||||
if [[ -n "$lid" ]]; then
|
|
||||||
if ! $first; then label_ids+=","; fi
|
|
||||||
label_ids+="$lid"
|
|
||||||
first=false
|
|
||||||
else
|
|
||||||
echo "Warning: label '${lname}' not found, skipping" >&2
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
label_ids+="]"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Secret scan (refuse to post bodies containing obvious secrets) ---
|
|
||||||
if echo "$body" | grep -qiE '(sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]{36}|AKIA[A-Z0-9]{16}|-----BEGIN (RSA |EC )?PRIVATE KEY)'; then
|
|
||||||
echo "Error: body appears to contain a secret — refusing to post" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Create the issue ---
|
|
||||||
payload=$(jq -n \
|
|
||||||
--arg t "$title" \
|
|
||||||
--arg b "$body" \
|
|
||||||
--argjson l "$label_ids" \
|
|
||||||
'{title: $t, body: $b, labels: $l}')
|
|
||||||
|
|
||||||
response=$(curl -sf -X POST \
|
|
||||||
-H "Authorization: token ${FORGE_TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "$payload" \
|
|
||||||
"${FORGE_API}/issues") || {
|
|
||||||
echo "Error: failed to create issue" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
number=$(echo "$response" | jq -r '.number')
|
|
||||||
url=$(echo "$response" | jq -r '.html_url')
|
|
||||||
echo "Created issue #${number}: ${url}"
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# read-journal.sh — read agent journal entries
|
|
||||||
#
|
|
||||||
# Usage: read-journal.sh AGENT [--date YYYY-MM-DD] [--list] [--help]
|
|
||||||
# AGENT: planner, supervisor, or predictor
|
|
||||||
# --date: specific date (default: today)
|
|
||||||
# --list: list available journal dates instead of reading
|
|
||||||
#
|
|
||||||
# Required env: PROJECT_REPO_ROOT
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
cat <<'USAGE'
|
|
||||||
read-journal.sh AGENT [--date YYYY-MM-DD] [--list] [--help]
|
|
||||||
AGENT: planner, supervisor, or predictor
|
|
||||||
--date: specific date (default: today)
|
|
||||||
--list: list available journal dates instead of reading
|
|
||||||
USAGE
|
|
||||||
exit 0
|
|
||||||
}
|
|
||||||
|
|
||||||
agent=""
|
|
||||||
target_date=$(date +%Y-%m-%d)
|
|
||||||
list_mode=false
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
--date) target_date="$2"; shift 2 ;;
|
|
||||||
--list) list_mode=true; shift ;;
|
|
||||||
--help|-h) usage ;;
|
|
||||||
-*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
||||||
*)
|
|
||||||
if [[ -z "$agent" ]]; then
|
|
||||||
agent="$1"
|
|
||||||
else
|
|
||||||
echo "Unexpected argument: $1" >&2; exit 1
|
|
||||||
fi
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
: "${OPS_REPO_ROOT:?OPS_REPO_ROOT is required}"
|
|
||||||
|
|
||||||
if [[ -z "$agent" ]]; then
|
|
||||||
echo "Error: agent name is required (planner, supervisor, predictor)" >&2
|
|
||||||
echo "" >&2
|
|
||||||
usage
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Resolve journal directory ---
|
|
||||||
case "$agent" in
|
|
||||||
planner) journal_dir="${OPS_REPO_ROOT}/journal/planner" ;;
|
|
||||||
supervisor) journal_dir="${OPS_REPO_ROOT}/journal/supervisor" ;;
|
|
||||||
predictor)
|
|
||||||
echo "The predictor does not write journal files."
|
|
||||||
echo "Its memory lives in forge issues labeled 'prediction/unreviewed' and 'prediction/actioned'."
|
|
||||||
echo ""
|
|
||||||
echo "Query predictions with:"
|
|
||||||
echo " curl -sH 'Authorization: token \${FORGE_TOKEN}' '\${FORGE_API}/issues?state=open&labels=prediction%2Funreviewed'"
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Error: unknown agent '${agent}'" >&2
|
|
||||||
echo "Available: planner, supervisor, predictor" >&2
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [[ ! -d "$journal_dir" ]]; then
|
|
||||||
echo "No journal directory found at ${journal_dir}" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- List mode ---
|
|
||||||
if $list_mode; then
|
|
||||||
echo "Available journal dates for ${agent}:"
|
|
||||||
find "$journal_dir" -maxdepth 1 -name '*.md' -printf '%f\n' 2>/dev/null | sed 's|\.md$||' | sort -r | head -20
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- Read specific date ---
|
|
||||||
journal_file="${journal_dir}/${target_date}.md"
|
|
||||||
if [[ -f "$journal_file" ]]; then
|
|
||||||
cat "$journal_file"
|
|
||||||
else
|
|
||||||
echo "No journal entry for ${agent} on ${target_date}" >&2
|
|
||||||
echo "" >&2
|
|
||||||
echo "Recent entries:" >&2
|
|
||||||
find "$journal_dir" -maxdepth 1 -name '*.md' -printf '%f\n' 2>/dev/null | sed 's|\.md$||' | sort -r | head -5 >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
## Summary
|
|
||||||
|
|
||||||
<!-- One or two sentences: what and why -->
|
|
||||||
|
|
||||||
## Acceptance criteria
|
|
||||||
|
|
||||||
- [ ] <!-- Criterion 1 -->
|
|
||||||
- [ ] <!-- Criterion 2 -->
|
|
||||||
- [ ] <!-- Criterion 3 -->
|
|
||||||
|
|
||||||
## Affected files
|
|
||||||
|
|
||||||
<!-- List files/directories this issue will touch -->
|
|
||||||
|
|
||||||
- `path/to/file.sh`
|
|
||||||
|
|
||||||
## Dependencies
|
|
||||||
|
|
||||||
<!-- List issue numbers this depends on, or "None" -->
|
|
||||||
|
|
||||||
None
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue