feat: create disinto skill package (SKILL.md + helper scripts) (#710)

Add skill/ directory implementing the Agent Skills open standard (SKILL.md
format) for the disinto factory. Includes:

- SKILL.md with YAML frontmatter, 9-agent architecture overview, env var
  documentation, 6 common workflows, and gotchas section (170 lines)
- scripts/factory-status.sh — query agent status, open issues, CI pipelines
- scripts/file-issue.sh — create forge issues with label resolution and
  secret scanning
- scripts/read-journal.sh — read planner/supervisor/exec journals by date
- templates/issue-template.md — standard issue body format

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-26 07:26:54 +00:00
parent 805fa69770
commit 26fcb186a0
5 changed files with 485 additions and 0 deletions

114
skill/scripts/factory-status.sh Executable file
View file

@ -0,0 +1,114 @@
#!/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 exec)
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

91
skill/scripts/file-issue.sh Executable file
View file

@ -0,0 +1,91 @@
#!/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 ;;
*) echo "Unknown option: $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}"

89
skill/scripts/read-journal.sh Executable file
View file

@ -0,0 +1,89 @@
#!/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 exec
# --date: specific date (default: today)
# --list: list available journal dates instead of reading
#
# Required env: PROJECT_REPO_ROOT
usage() {
sed -n '3,10s/^# //p' "$0"
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
: "${PROJECT_REPO_ROOT:?PROJECT_REPO_ROOT is required}"
if [[ -z "$agent" ]]; then
echo "Error: agent name is required (planner, supervisor, exec)" >&2
echo "" >&2
usage
fi
# --- Resolve journal directory ---
case "$agent" in
planner) journal_dir="${PROJECT_REPO_ROOT}/planner/journal" ;;
supervisor) journal_dir="${PROJECT_REPO_ROOT}/supervisor/journal" ;;
exec) journal_dir="${PROJECT_REPO_ROOT}/exec/journal" ;;
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, exec, 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