feat: integrate tea CLI for forge issue/label/comment operations (#666)
- Add lib/tea-helpers.sh with tea_file_issue, tea_relabel, tea_comment, tea_close — thin wrappers preserving secret scanning on write ops - Add tea 0.9.2 binary to docker/agents/Dockerfile - Configure tea login in docker/agents/entrypoint.sh from FORGE_TOKEN/FORGE_URL - Derive TEA_LOGIN in lib/env.sh (codeberg vs local forgejo) - Source tea-helpers.sh conditionally when tea binary is available - Migrate predictor formula from inline curl to tea CLI commands - Register tea-helpers.sh in smoke test function resolution Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5e66ba7d12
commit
44cbbbde62
7 changed files with 137 additions and 19 deletions
|
|
@ -18,4 +18,5 @@ sourced as needed.
|
|||
| `lib/build-graph.py` | Python tool: parses VISION.md, prerequisite-tree.md, AGENTS.md, formulas/*.toml, evidence/, 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]`. | file-action-issue.sh, phase-handler.sh |
|
||||
| `lib/file-action-issue.sh` | `file_action_issue()` — dedup check, secret scan, label lookup, and issue creation for formula-driven cron wrappers. Sets `FILED_ISSUE_NUM` on success. Returns 4 if secrets detected in body. | (available for future use) |
|
||||
| `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. `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). Sourced by env.sh when `tea` binary is available. | env.sh (conditional) |
|
||||
| `lib/agent-session.sh` | Shared tmux + Claude session helpers: `create_agent_session()`, `inject_formula()`, `agent_wait_for_claude_ready()`, `agent_inject_into_session()`, `agent_kill_session()`, `monitor_phase_loop()`, `read_phase()`, `write_compact_context()`. `create_agent_session(session, workdir, [phase_file])` optionally installs a PostToolUse hook (matcher `Bash\|Write`) that detects phase file writes in real-time — when Claude writes to the phase file, the hook writes a marker so `monitor_phase_loop` reacts on the next poll instead of waiting for mtime changes. Also installs a StopFailure hook (matcher `rate_limit\|server_error\|authentication_failed\|billing_error`) that writes `PHASE:failed` with an `api_error` reason to the phase file and touches the phase-changed marker, so the orchestrator discovers API errors within one poll cycle instead of waiting for idle timeout. Also installs a SessionStart hook (matcher `compact`) that re-injects phase protocol instructions after context compaction — callers write the context file via `write_compact_context(phase_file, content)`, and the hook (`on-compact-reinject.sh`) outputs the file content to stdout so Claude retains critical instructions. When `MATRIX_THREAD_ID` is exported, also installs a Stop hook (`on-stop-matrix.sh`) that streams each Claude response to the Matrix thread. When `phase_file` is set, passes it to the idle stop hook (`on-idle-stop.sh`) so the hook can **nudge Claude** (up to 2 times) if Claude returns to the prompt without writing to the phase file — the hook injects a tmux reminder asking Claude to signal PHASE:done or PHASE:awaiting_ci. The PreToolUse guard hook (`on-pretooluse-guard.sh`) receives the session name as a third argument — formula agents (`gardener-*`, `planner-*`, `predictor-*`, `supervisor-*`) are identified this way and allowed to access `FACTORY_ROOT` from worktrees (they need env.sh, AGENTS.md, formulas/, lib/). `monitor_phase_loop` sets `_MONITOR_LOOP_EXIT` to one of: `done`, `idle_timeout`, `idle_prompt` (Claude returned to `>` for 3 consecutive polls without writing any phase — callback invoked with `PHASE:failed`, session already dead), `crashed`, or `PHASE:escalate` / other `PHASE:*` string. **Unified escalation**: `PHASE:escalate` is the signal that a session needs human input (renamed from `PHASE:needs_human`). **Callers must handle `idle_prompt`** in both their callback and their post-loop exit handler — see [`docs/PHASE-PROTOCOL.md` idle_prompt](docs/PHASE-PROTOCOL.md#idle_prompt-exit-reason) for the full contract. | dev-agent.sh, action-agent.sh |
|
||||
|
|
|
|||
15
lib/env.sh
15
lib/env.sh
|
|
@ -65,6 +65,15 @@ export FORGE_API="${FORGE_API:-${FORGE_URL}/api/v1/repos/${FORGE_REPO}}"
|
|||
export FORGE_WEB="${FORGE_WEB:-${FORGE_URL}/${FORGE_REPO}}"
|
||||
export CODEBERG_API="${FORGE_API}" # backwards compat
|
||||
export CODEBERG_WEB="${FORGE_WEB}" # backwards compat
|
||||
# tea CLI login name: derived from FORGE_URL (codeberg vs local forgejo)
|
||||
if [ -z "${TEA_LOGIN:-}" ]; then
|
||||
case "${FORGE_URL}" in
|
||||
*codeberg.org*) TEA_LOGIN="codeberg" ;;
|
||||
*) TEA_LOGIN="forgejo" ;;
|
||||
esac
|
||||
fi
|
||||
export TEA_LOGIN
|
||||
|
||||
export PROJECT_NAME="${PROJECT_NAME:-${FORGE_REPO##*/}}"
|
||||
export PROJECT_REPO_ROOT="${PROJECT_REPO_ROOT:-/home/${USER}/${PROJECT_NAME}}"
|
||||
export PRIMARY_BRANCH="${PRIMARY_BRANCH:-master}"
|
||||
|
|
@ -218,3 +227,9 @@ matrix_send_ctx() {
|
|||
printf '%s' "$event_id"
|
||||
fi
|
||||
}
|
||||
|
||||
# Source tea helpers (available when tea binary is installed)
|
||||
if command -v tea &>/dev/null; then
|
||||
# shellcheck source=tea-helpers.sh
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/tea-helpers.sh"
|
||||
fi
|
||||
|
|
|
|||
78
lib/tea-helpers.sh
Normal file
78
lib/tea-helpers.sh
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env bash
|
||||
# tea-helpers.sh — Thin wrappers around tea CLI for forge issue operations
|
||||
#
|
||||
# Usage: source this file (after env.sh), then call tea_* functions.
|
||||
# Requires: tea binary in PATH, TEA_LOGIN and FORGE_REPO from env.sh,
|
||||
# scan_for_secrets from lib/secret-scan.sh
|
||||
#
|
||||
# tea_file_issue <title> <body> <labels...>
|
||||
# Sets FILED_ISSUE_NUM on success.
|
||||
# Returns: 0=created, 3=API/tea error, 4=secrets detected
|
||||
#
|
||||
# tea_relabel <issue_number> <labels...>
|
||||
# tea_comment <issue_number> <body>
|
||||
# tea_close <issue_number>
|
||||
|
||||
# Load secret scanner
|
||||
# shellcheck source=secret-scan.sh
|
||||
source "$(dirname "${BASH_SOURCE[0]}")/secret-scan.sh"
|
||||
|
||||
tea_file_issue() {
|
||||
local title="$1" body="$2"
|
||||
shift 2
|
||||
FILED_ISSUE_NUM=""
|
||||
|
||||
# Secret scan: reject issue bodies containing embedded secrets
|
||||
if ! scan_for_secrets "$body"; then
|
||||
echo "tea-helpers: BLOCKED — issue body contains potential secrets. Use env var references instead." >&2
|
||||
return 4
|
||||
fi
|
||||
|
||||
# Join remaining args as comma-separated label names
|
||||
local IFS=','
|
||||
local labels="$*"
|
||||
|
||||
local result
|
||||
result=$(tea issues create --login "$TEA_LOGIN" --repo "$FORGE_REPO" \
|
||||
--title "$title" --body "$body" --labels "$labels" \
|
||||
--output simple 2>&1) || {
|
||||
echo "tea-helpers: tea issues create failed: ${result}" >&2
|
||||
return 3
|
||||
}
|
||||
|
||||
# Parse issue number from tea output (e.g. "#42 Title")
|
||||
FILED_ISSUE_NUM=$(printf '%s' "$result" | grep -oE '#[0-9]+' | head -1 | tr -d '#')
|
||||
if [ -z "$FILED_ISSUE_NUM" ]; then
|
||||
# Fallback: extract any number
|
||||
FILED_ISSUE_NUM=$(printf '%s' "$result" | grep -oE '[0-9]+' | head -1)
|
||||
fi
|
||||
}
|
||||
|
||||
tea_relabel() {
|
||||
local issue_num="$1"
|
||||
shift
|
||||
|
||||
local IFS=','
|
||||
local labels="$*"
|
||||
|
||||
tea issues labels "$issue_num" --login "$TEA_LOGIN" --repo "$FORGE_REPO" \
|
||||
--labels "$labels"
|
||||
}
|
||||
|
||||
tea_comment() {
|
||||
local issue_num="$1" body="$2"
|
||||
|
||||
# Secret scan: reject comment bodies containing embedded secrets
|
||||
if ! scan_for_secrets "$body"; then
|
||||
echo "tea-helpers: BLOCKED — comment body contains potential secrets. Use env var references instead." >&2
|
||||
return 4
|
||||
fi
|
||||
|
||||
tea comment create "$issue_num" --login "$TEA_LOGIN" --repo "$FORGE_REPO" \
|
||||
--body "$body"
|
||||
}
|
||||
|
||||
tea_close() {
|
||||
local issue_num="$1"
|
||||
tea issues close "$issue_num" --login "$TEA_LOGIN" --repo "$FORGE_REPO"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue