ACPcodex / scripts /acpx-subagents
smlflg's picture
Initial public upload from Projekte/ACPcodex
28f408e verified
Raw History Blame Contribute Delete
8.55 kB
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd -- "$SCRIPT_DIR/.." && pwd)"
TARGET_CWD="${TARGET_CWD:-$PROJECT_DIR}"
CONFIG_FILE="${SUBAGENTS_CONFIG:-$PROJECT_DIR/subagents.conf}"
AGENTS=(ui pipeline rules tests)
declare -A AGENT_SCOPES=(
[ui]="templates/ static/ frontend/ src/ui/ src/components/ src/app/"
[pipeline]="pipeline/ runtime/ schemas/ guards/ src/pipeline/ src/runtime/"
[rules]="rules/ rule-packs/ policies/ validation/ src/rules/"
[tests]="tests/ test/ spec/ fixtures/ harness/ e2e/"
)
declare -A AGENT_RESPONSIBILITIES=(
[ui]="Implement and review user-facing UI, templates, static assets, and component behavior."
[pipeline]="Implement and review pipeline flow, runtime behavior, schemas, and guard logic."
[rules]="Implement and review rule packs, policy definitions, validation behavior, and rule docs."
[tests]="Add and maintain focused test coverage, fixtures, harnesses, and test documentation."
)
ACPX_CMD=()
usage() {
cat <<'USAGE'
Usage:
acpx-subagents [--cwd DIR] [--config FILE] doctor
acpx-subagents [--cwd DIR] [--config FILE] ensure [agent...]
acpx-subagents [--cwd DIR] [--config FILE] send [--no-wait] [--file FILE] <agent> [prompt...]
acpx-subagents [--cwd DIR] [--config FILE] parallel [--no-wait|--wait] [agent...] -- [prompt...]
acpx-subagents [--cwd DIR] [--config FILE] sessions
acpx-subagents [--cwd DIR] [--config FILE] show <agent>
acpx-subagents [--cwd DIR] [--config FILE] history <agent> [limit]
acpx-subagents [--cwd DIR] [--config FILE] close [agent...]
Environment:
ACPX_BIN Override acpx command, e.g. "acpx" or "npx -y acpx@0.1.15"
SUBAGENTS_CONFIG Override config file path
TARGET_CWD Default target repository/workspace
USAGE
}
fail() {
printf 'error: %s\n' "$*" >&2
exit 1
}
absolute_dir() {
local dir="$1"
[[ -d "$dir" ]] || fail "directory does not exist: $dir"
(cd -- "$dir" && pwd)
}
load_config() {
[[ -f "$CONFIG_FILE" ]] || return 0
# shellcheck source=/dev/null
source "$CONFIG_FILE"
}
agent_exists() {
local candidate="$1"
local agent
for agent in "${AGENTS[@]}"; do
[[ "$agent" == "$candidate" ]] && return 0
done
return 1
}
agents_or_all() {
if (($# == 0)); then
printf '%s\n' "${AGENTS[@]}"
return 0
fi
local agent
for agent in "$@"; do
agent_exists "$agent" || fail "unknown agent '$agent' (known: ${AGENTS[*]})"
printf '%s\n' "$agent"
done
}
resolve_acpx() {
if ((${#ACPX_CMD[@]} > 0)); then
return 0
fi
if [[ -n "${ACPX_BIN:-}" ]]; then
read -r -a ACPX_CMD <<< "$ACPX_BIN"
elif command -v acpx >/dev/null 2>&1; then
ACPX_CMD=(acpx)
else
ACPX_CMD=(npx -y acpx@latest)
fi
}
run_acpx() {
resolve_acpx
"${ACPX_CMD[@]}" --cwd "$TARGET_CWD" codex "$@"
}
compose_prompt() {
local agent="$1"
local task="$2"
local scope="${AGENT_SCOPES[$agent]:-not configured}"
local responsibility="${AGENT_RESPONSIBILITIES[$agent]:-Work only on the assigned task.}"
cat <<PROMPT
You are the "$agent" Codex subagent for this repository.
Responsibility:
$responsibility
File ownership:
$scope
Collaboration rules:
- You are not alone in this codebase; other agents may be editing their owned paths.
- Do not revert or overwrite changes made by others.
- Keep edits inside your ownership unless this task explicitly grants a wider scope.
- If the task needs files outside your ownership, stop and report the exact paths and reason.
- Finish with a concise summary of changed files, tests run, and remaining risks.
Task:
$task
PROMPT
}
cmd_doctor() {
printf 'target cwd: %s\n' "$TARGET_CWD"
printf 'config: %s\n' "$CONFIG_FILE"
if git -C "$TARGET_CWD" rev-parse --show-toplevel >/dev/null 2>&1; then
printf 'git root: %s\n' "$(git -C "$TARGET_CWD" rev-parse --show-toplevel)"
else
printf 'git root: none; acpx will scope sessions to the exact target cwd\n'
fi
local cmd
for cmd in node npm npx codex; do
if command -v "$cmd" >/dev/null 2>&1; then
printf 'ok: %-5s %s\n' "$cmd" "$(command -v "$cmd")"
else
printf 'missing: %s\n' "$cmd"
fi
done
if [[ -n "${ACPX_BIN:-}" ]]; then
printf 'acpx: ACPX_BIN=%s\n' "$ACPX_BIN"
elif command -v acpx >/dev/null 2>&1; then
printf 'acpx: %s\n' "$(command -v acpx)"
else
printf 'acpx: not installed; will use npx -y acpx@latest\n'
fi
printf 'agents: %s\n' "${AGENTS[*]}"
}
cmd_ensure() {
local agent
while IFS= read -r agent; do
printf 'ensuring session: %s\n' "$agent" >&2
run_acpx sessions ensure --name "$agent"
done < <(agents_or_all "$@")
}
cmd_send() {
local no_wait=0
local prompt_file=""
while (($# > 0)); do
case "$1" in
--no-wait)
no_wait=1
shift
;;
--file)
(($# >= 2)) || fail "--file requires a path"
prompt_file="$2"
shift 2
;;
--)
shift
break
;;
-*)
fail "unknown send option: $1"
;;
*)
break
;;
esac
done
(($# >= 1)) || fail "send requires an agent"
local agent="$1"
shift
agent_exists "$agent" || fail "unknown agent '$agent' (known: ${AGENTS[*]})"
while (($# > 0)); do
case "$1" in
--no-wait)
no_wait=1
shift
;;
--file)
(($# >= 2)) || fail "--file requires a path"
prompt_file="$2"
shift 2
;;
--)
shift
break
;;
-*)
fail "unknown send option: $1"
;;
*)
break
;;
esac
done
local task=""
if [[ -n "$prompt_file" ]]; then
[[ -f "$prompt_file" ]] || fail "prompt file does not exist: $prompt_file"
task="$(< "$prompt_file")"
if (($# > 0)); then
task+=$'\n\n'
task+="$*"
fi
else
task="$*"
fi
[[ -n "${task//[[:space:]]/}" ]] || fail "send requires prompt text or --file"
local prompt
prompt="$(compose_prompt "$agent" "$task")"
local args=(-s "$agent")
if ((no_wait == 1)); then
args+=(--no-wait)
fi
run_acpx "${args[@]}" "$prompt"
}
cmd_parallel() {
local no_wait=1
local selected=()
while (($# > 0)); do
case "$1" in
--no-wait)
no_wait=1
shift
;;
--wait)
no_wait=0
shift
;;
--)
shift
break
;;
*)
if agent_exists "$1"; then
selected+=("$1")
shift
else
break
fi
;;
esac
done
if ((${#selected[@]} == 0)); then
selected=("${AGENTS[@]}")
fi
local task="$*"
[[ -n "${task//[[:space:]]/}" ]] || fail "parallel requires prompt text after --"
local agent
for agent in "${selected[@]}"; do
if ((no_wait == 1)); then
cmd_send --no-wait "$agent" "$task"
else
cmd_send "$agent" "$task"
fi
done
}
cmd_sessions() {
run_acpx sessions list
}
cmd_show() {
(($# == 1)) || fail "show requires exactly one agent"
agent_exists "$1" || fail "unknown agent '$1' (known: ${AGENTS[*]})"
run_acpx sessions show "$1"
}
cmd_history() {
(($# >= 1 && $# <= 2)) || fail "history requires an agent and optional limit"
local agent="$1"
local limit="${2:-20}"
agent_exists "$agent" || fail "unknown agent '$agent' (known: ${AGENTS[*]})"
run_acpx sessions history "$agent" --limit "$limit"
}
cmd_close() {
local agent
while IFS= read -r agent; do
printf 'closing session: %s\n' "$agent" >&2
run_acpx sessions close "$agent"
done < <(agents_or_all "$@")
}
TARGET_CWD="$(absolute_dir "$TARGET_CWD")"
COMMAND=""
while (($# > 0)); do
case "$1" in
--cwd)
(($# >= 2)) || fail "--cwd requires a directory"
TARGET_CWD="$(absolute_dir "$2")"
shift 2
;;
--config)
(($# >= 2)) || fail "--config requires a file"
CONFIG_FILE="$2"
shift 2
;;
-h|--help|help)
usage
exit 0
;;
doctor|ensure|send|parallel|sessions|show|history|close)
COMMAND="$1"
shift
break
;;
*)
fail "unknown command or option: $1"
;;
esac
done
load_config
case "$COMMAND" in
doctor) cmd_doctor "$@" ;;
ensure) cmd_ensure "$@" ;;
send) cmd_send "$@" ;;
parallel) cmd_parallel "$@" ;;
sessions) cmd_sessions "$@" ;;
show) cmd_show "$@" ;;
history) cmd_history "$@" ;;
close) cmd_close "$@" ;;
"")
usage
;;
*)
fail "unknown command: $COMMAND"
;;
esac