Stop @pm (and dev agents) from re-asking the maintainer questions already answered on past issues. The gitea-api skill already had cross-repo read access via AGENT_TOKEN (added in #14) but no "search for similar issues" example, and the PM prompt never told it to look at past issues. Both gaps are fixed in the shared reusable workflow (agent.yml), so every caller repo benefits.
Changes
.gitea/workflows/agent.yml — gitea-api skill SKILL.md heredoc: added a "Search for similar/related past issues by keyword" example with the same-repo issue-list endpoint (?q=<keywords>&type=issues&state=all&limit=20) and the cross-repo /repos/issues/search?q=... variant, plus tips (pick keywords from the new issue, state=all to surface closed/answered issues, open matched threads, cite issue numbers, only ask what precedent doesn't cover). Existing examples left intact.
.gitea/workflows/agent.yml — PM ACTION prompt block: added a KNOWLEDGE-BASE phase before the existing PLAN phase instructing @pm to search this repo and other repos on the instance (e.g. homelab) for similar past issues before presenting a plan or asking clarifying questions, reuse the maintainer's prior answers, cite matched issue numbers (e.g. homelab#103), and only ask about things genuinely not covered by precedent.
No trigger-gate, loop-guard, secret, or routing changes. YAML re-parses cleanly; PM prompt string kept free of backticks/$ to avoid shell expansion in the double-quoted bash string.
## Summary
Stop `@pm` (and dev agents) from re-asking the maintainer questions already answered on past issues. The `gitea-api` skill already had cross-repo read access via `AGENT_TOKEN` (added in #14) but no "search for similar issues" example, and the PM prompt never told it to look at past issues. Both gaps are fixed in the shared reusable workflow (`agent.yml`), so every caller repo benefits.
## Changes
- `.gitea/workflows/agent.yml` — `gitea-api` skill SKILL.md heredoc: added a "Search for similar/related past issues by keyword" example with the same-repo issue-list endpoint (`?q=<keywords>&type=issues&state=all&limit=20`) and the cross-repo `/repos/issues/search?q=...` variant, plus tips (pick keywords from the new issue, `state=all` to surface closed/answered issues, open matched threads, cite issue numbers, only ask what precedent doesn't cover). Existing examples left intact.
- `.gitea/workflows/agent.yml` — PM `ACTION` prompt block: added a `KNOWLEDGE-BASE` phase before the existing `PLAN` phase instructing `@pm` to search this repo and other repos on the instance (e.g. homelab) for similar past issues *before* presenting a plan or asking clarifying questions, reuse the maintainer's prior answers, cite matched issue numbers (e.g. `homelab#103`), and only ask about things genuinely not covered by precedent.
- No trigger-gate, loop-guard, secret, or routing changes. YAML re-parses cleanly; PM prompt string kept free of backticks/`$` to avoid shell expansion in the double-quoted bash string.
---
Resolves #17 · 🤖 @senior
ai/issue-17 has a stale commit (644cf85) from PR #18 — it edited agent.yml inline, but agent.yml has since been refactored to externalize scripts into .gitea/workflows/scripts/*.sh. So PR #18 is stale/conflicted.
The newly-delegated plan (Live Agent Capability Table via Gitea 1.27's GET /api/v1/token) is independent of the search-example work, so per AGENTS.md it gets its own branch/PR.
Least-privilege gating (plan point #2) is already done via agents.jsonskills arrays + install-opencode.shpermission.skill scoping (issue #22) — @junior has skills:[], etc. I won't redo it.
Let me create a fresh branch off main for the capability table. 💬 Now let me verify the /api/v1/token endpoint shape before coding against it. 🔧skill: Loaded skill: gitea-api 🔧bash: curl -sS -H "Authorization: token $AGENT_TOKEN" "$GITHUB_SERVER_URL/api/v1/version"; echo; curl -sS -H "Authorization: token $AGENT_TOKEN" "$GITHUB_SERVER_URL/api/v1/token" | jq '.' 2>&1 | head -40 🔧bash: echo "GITHUB_SERVER_URL=$GITHUB_SERVER_URL"; echo "AGENT_TOKEN set? $([ -n "$AGENT_TOKEN" ] && echo yes || echo no)"; env | grep -iE "TOKEN_(PM|SENIOR|JUNIOR|LEAD|QA)" | sed 's/=.*/=***/' 🔧write: .gitea/workflows/scripts/skill-gitea-api.sh 💬 Got it. The /api/v1/token endpoint works on this 1.27 instance and returns scopes. Now implementing. The existing ai/issue-17 branch has a stale PR #18 (edited agent.yml inline before the scripts refactor) — I'll leave that alone and put the capability-table work on a fresh branch.
Plan for this branch:
skill-gitea-api.sh — introspect each agent token via GET /api/v1/token, build a live capability matrix table into SKILL.md.
agent.yml — pass the TOKEN_* env vars into the gitea-api skill step so the script can introspect them.
run-agent.sh — one roster note pointing agents to the capability matrix.
Note: least-privilege gating (plan point #3) is already done via agents.jsonskills arrays + install-opencode.shpermission.skill scoping (issue #22) — @junior has skills:[], denied skills are fully hidden. No redundant case gates needed. 🔧bash: chmod +x .gitea/workflows/scripts/skill-gitea-api.sh && bash -n .gitea/workflows/scripts/skill-gitea-api.sh && echo "syntax OK" 🔧edit: .gitea/workflows/agent.yml 💬 Now update agent.yml to pass the TOKEN_* env vars and GITHUB_SERVER_URL into the gitea-api skill step: 🔧edit: .gitea/workflows/scripts/run-agent.sh 💬 Now add a roster note in run-agent.sh pointing agents to the capability matrix in the skill: 🔧bash: rm -rf /tmp/skilltest && mkdir -p /tmp/skilltest && HOME=/tmp/skilltest GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="$AGENT_TOKEN" bash .gitea/workflows/scripts/skill-gitea-api.sh && echo "=== SKILL.md (head 60) ===" &&… 💬 Now let me verify the whole pipeline works locally by simulating the script: 🔧bash: rm -rf /tmp/skilltest2 && mkdir -p /tmp/skilltest2 && HOME=/tmp/skilltest2 GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="$AGENT_TOKEN" TOKEN_PM="" TOKEN_SENIOR="" TOKEN_JUNIOR="" TOKEN_LEAD="" TOKEN_QA="" bash .gitea/wor… 💬 Live matrix works. Only @shared (AGENT_TOKEN) row appears because the TOKEN_* per-agent secrets aren't set in my local env — in CI they will be. Let me also test the fallback path (no tokens at all) and that the static body survives intact: 🔧bash: rm -rf /tmp/skilltest3 && mkdir -p /tmp/skilltest3 && HOME=/tmp/skilltest3 GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="invalid-token-xyz" bash .gitea/workflows/scripts/skill-gitea-api.sh 2>&1 | tail -2; echo "=== fallb… 🔧edit: .gitea/workflows/scripts/skill-gitea-api.sh 🔧bash: rm -rf /tmp/skilltest3 && mkdir -p /tmp/skilltest3 && HOME=/tmp/skilltest3 GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="invalid-token-xyz" bash .gitea/workflows/scripts/skill-gitea-api.sh 2>&1 | tail -1; echo "=== fallb… 🔧bash: rm -rf /tmp/skilltest && mkdir -p /tmp/skilltest && HOME=/tmp/skilltest GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="$AGENT_TOKEN" bash .gitea/workflows/scripts/skill-gitea-api.sh 2>&1 | tail -1; echo "=== examples pres… 💬 Fallback now triggers correctly when introspection fails. Let me re-confirm the valid-token path still works and the static examples survived: 🔧bash: python3 -c "import yaml,sys; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('YAML OK')" 2>&1 || echo "no pyyaml, trying shell"; bash -n .gitea/workflows/scripts/skill-gitea-api.sh && echo "script syntax OK"; bash -n .gitea/workfl… 💬 All 6 examples + the search example (from PR #18's earlier work, now folded into the canonical script) + live matrix. Let me verify the YAML is still valid and check final diff: 🔧bash: pip install pyyaml -q 2>&1 | tail -1; python3 -c "import yaml; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('YAML OK')" 🔧bash: which yamllint yq 2>/dev/null; python3 -m venv /tmp/yv 2>&1 | tail -1 && /tmp/yv/bin/pip install pyyaml -q 2>&1 | tail -1 && /tmp/yv/bin/python -c "import yaml; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('YAML OK')" 🔧bash: yq '.jobs.agent.steps[] | select(.name | test("gitea-api")) | .env' .gitea/workflows/agent.yml 🔧bash: git add .gitea/workflows/agent.yml .gitea/workflows/scripts/skill-gitea-api.sh .gitea/workflows/scripts/run-agent.sh && git status --short && git diff --cached --stat 💬 YAML parses, env block correct. Now commit and push: 🔧bash: `git commit -m "@senior: issue #17 — live agent capability table via Gitea 1.27 /api/v1/token
skill-gitea-api.sh now introspects each agent's own PAT (AGENT_TOKEN +
TOKEN_PM/SENIOR/JUNIOR/LEAD/QA) via GET /api/v1/token — a Gitea 1.27+
self-…` 💬 Pushed. The automation will open the PR from the branch. Here's the summary for the issue thread:
🤖@senior — done, on a fresh branch ai/issue-17-capabilities (the old ai/issue-17/PR #18 predates the scripts refactor and is stale).
What I built (live Agent Capability Table, per @pm's approved plan):
skill-gitea-api.sh — now introspects each agent's own PAT (AGENT_TOKEN + TOKEN_PM/SENIOR/JUNIOR/LEAD/QA) via Gitea 1.27's GET /api/v1/token (self-introspection: token-in, {name,scopes,user}-out, no password) and bakes a live matrix into SKILL.md:
| Agent | Gitea user | Token name | Scopes |
| @shared (AGENT_TOKEN) | ffaerber | full | read:organization, read:user, write:issue, write:repository, … |
Verified against the real instance (1.27.0+rc0): the endpoint returns real scopes, never the token string. Falls back to a static note on <1.27 or unset tokens (tested both paths locally). The existing static scope description + all examples (incl. the keyword-search "knowledge base" example from PR #18) are preserved.
agent.yml — passes GITHUB_SERVER_URL + the per-agent TOKEN_* secrets into the gitea-api skill step so the script can introspect them.
run-agent.sh — one roster note pointing agents to the live matrix for capability-based routing.
On plan point #3 (least-privilege gating): already done in issue #22 — agents.jsonskills arrays + install-opencode.shpermission.skill scoping mean @junior (skills:[]) never even sees the gitea-api summary line, and denied skills are fully hidden. No redundant case gates added.
Verified: shell syntax, YAML parses (yq + pyyaml), live-introspection path, fallback path, and that all 6 examples + the search example survive. PR will be auto-opened from the branch.
BEGIN_PR_DESCRIPTION
Summary
Implements the live Agent Capability Table approved in issue #17: every agent that loads the gitea-api skill now sees a live, always-accurate matrix of every teammate's real Gitea scopes, auto-introspected at workflow start via Gitea 1.27's GET /api/v1/token endpoint. No hand-maintained table to drift when scopes rotate. Also folds in the keyword-search "knowledge base" example so PM can look up similar past issues instead of re-asking settled questions.
Changes
.gitea/workflows/scripts/skill-gitea-api.sh: introspect AGENT_TOKEN + TOKEN_PM/SENIOR/JUNIOR/LEAD/QA via GET /api/v1/token, render a live capability matrix table into SKILL.md; graceful fallback on <1.27/unset tokens; preserves all existing examples + adds the keyword-search example.
.gitea/workflows/agent.yml: pass GITHUB_SERVER_URL + per-agent TOKEN_* secrets into the gitea-api skill step.
.gitea/workflows/scripts/run-agent.sh: roster note pointing agents to the live matrix for capability-based routing.
END_PR_DESCRIPTION
🤖 **@senior** — ✅ PR ready for review — @ffaerber please review & merge:
- https://git.ffaerber.duckdns.org/ffaerber/agents/pulls/18
---
🤖 **@senior** — activity log (93 entries):
<details>
<summary>tool calls & reasoning</summary>
🔧 **bash**: `git branch --show-current && git log --oneline -10 && git status`
🔧 **read**: `.gitea/workflows/agent.yml`
🔧 **bash**: `ls -la .gitea/workflows/scripts/`
🔧 **read**: `.gitea/workflows/scripts/skill-gitea-api.sh`
🔧 **bash**: `git log --oneline main..ai/issue-17; echo "---"; git log --oneline ai/issue-17..main; echo "--- remote ---"; git branch -r | head -20`
🔧 **bash**: `git log --oneline origin/ai/issue-17 -10`
🔧 **bash**: `git log --oneline origin/main -5`
🔧 **bash**: `git log --oneline --all | grep -i "issue-17\|knowledge\|capability\|#17" | head; echo "---"; git log --oneline origin/main | grep -i "17\|knowledge\|capability" | head`
🔧 **bash**: `git show 644cf85 --stat; echo "==="; git log --oneline origin/main | head -30`
🔧 **bash**: `git show 644cf85`
🔧 **bash**: `git log --oneline origin/main | head -3; echo "---"; git fetch origin; git log --oneline origin/main..origin/ai/issue-17; echo "==="; git log --oneline origin/ai/issue-17..origin/main | head -40`
🔧 **read**: `.gitea/workflows/scripts/run-agent.sh`
🔧 **read**: `.gitea/workflows/scripts/skill-node1-ssh.sh`
🔧 **read**: `.gitea/workflows/scripts/route.sh`
🔧 **read**: `.gitea/workflows/scripts/agents.json`
🔧 **read**: `.gitea/workflows/scripts/install-opencode.sh`
🔧 **bash**: `git log --oneline -3 HEAD; echo "--- origin/ai/issue-17 ---"; git log --oneline -3 origin/ai/issue-17; echo "--- is 644cf85 in HEAD? ---"; git merge-base --is-ancestor 644cf85 HEAD && echo "YES" || echo "NO"`
🔧 **bash**: `git reset --hard origin/ai/issue-17 && git log --oneline -3`
🔧 **bash**: `git checkout main && git pull --ff-only origin main && git checkout -b ai/issue-17-capabilities && git log --oneline -3`
💬 I've got the full picture now. Let me note the state:
- `ai/issue-17` has a stale commit (644cf85) from PR #18 — it edited `agent.yml` inline, but `agent.yml` has since been refactored to externalize scripts into `.gitea/workflows/scripts/*.sh`. So PR #18 is stale/conflicted.
- The **newly-delegated** plan (Live Agent Capability Table via Gitea 1.27's `GET /api/v1/token`) is independent of the search-example work, so per AGENTS.md it gets its own branch/PR.
- Least-privilege gating (plan point #2) is **already done** via `agents.json` `skills` arrays + `install-opencode.sh` `permission.skill` scoping (issue #22) — `@junior` has `skills:[]`, etc. I won't redo it.
Let me create a fresh branch off main for the capability table.
💬 Now let me verify the `/api/v1/token` endpoint shape before coding against it.
🔧 **skill**: `Loaded skill: gitea-api`
🔧 **bash**: `curl -sS -H "Authorization: token $AGENT_TOKEN" "$GITHUB_SERVER_URL/api/v1/version"; echo; curl -sS -H "Authorization: token $AGENT_TOKEN" "$GITHUB_SERVER_URL/api/v1/token" | jq '.' 2>&1 | head -40`
🔧 **bash**: `echo "GITHUB_SERVER_URL=$GITHUB_SERVER_URL"; echo "AGENT_TOKEN set? $([ -n "$AGENT_TOKEN" ] && echo yes || echo no)"; env | grep -iE "TOKEN_(PM|SENIOR|JUNIOR|LEAD|QA)" | sed 's/=.*/=***/'`
🔧 **write**: `.gitea/workflows/scripts/skill-gitea-api.sh`
💬 Got it. The `/api/v1/token` endpoint works on this 1.27 instance and returns `scopes`. Now implementing. The existing `ai/issue-17` branch has a stale PR #18 (edited `agent.yml` inline before the scripts refactor) — I'll leave that alone and put the capability-table work on a fresh branch.
Plan for this branch:
1. `skill-gitea-api.sh` — introspect each agent token via `GET /api/v1/token`, build a live capability matrix table into `SKILL.md`.
2. `agent.yml` — pass the `TOKEN_*` env vars into the gitea-api skill step so the script can introspect them.
3. `run-agent.sh` — one roster note pointing agents to the capability matrix.
Note: least-privilege gating (plan point #3) is already done via `agents.json` `skills` arrays + `install-opencode.sh` `permission.skill` scoping (issue #22) — `@junior` has `skills:[]`, denied skills are fully hidden. No redundant `case` gates needed.
🔧 **bash**: `chmod +x .gitea/workflows/scripts/skill-gitea-api.sh && bash -n .gitea/workflows/scripts/skill-gitea-api.sh && echo "syntax OK"`
🔧 **edit**: `.gitea/workflows/agent.yml`
💬 Now update `agent.yml` to pass the `TOKEN_*` env vars and `GITHUB_SERVER_URL` into the gitea-api skill step:
🔧 **edit**: `.gitea/workflows/scripts/run-agent.sh`
💬 Now add a roster note in `run-agent.sh` pointing agents to the capability matrix in the skill:
🔧 **bash**: `rm -rf /tmp/skilltest && mkdir -p /tmp/skilltest && HOME=/tmp/skilltest GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="$AGENT_TOKEN" bash .gitea/workflows/scripts/skill-gitea-api.sh && echo "=== SKILL.md (head 60) ===" &&…`
💬 Now let me verify the whole pipeline works locally by simulating the script:
🔧 **bash**: `rm -rf /tmp/skilltest2 && mkdir -p /tmp/skilltest2 && HOME=/tmp/skilltest2 GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="$AGENT_TOKEN" TOKEN_PM="" TOKEN_SENIOR="" TOKEN_JUNIOR="" TOKEN_LEAD="" TOKEN_QA="" bash .gitea/wor…`
💬 Live matrix works. Only `@shared (AGENT_TOKEN)` row appears because the `TOKEN_*` per-agent secrets aren't set in my local env — in CI they will be. Let me also test the fallback path (no tokens at all) and that the static body survives intact:
🔧 **bash**: `rm -rf /tmp/skilltest3 && mkdir -p /tmp/skilltest3 && HOME=/tmp/skilltest3 GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="invalid-token-xyz" bash .gitea/workflows/scripts/skill-gitea-api.sh 2>&1 | tail -2; echo "=== fallb…`
🔧 **edit**: `.gitea/workflows/scripts/skill-gitea-api.sh`
🔧 **bash**: `rm -rf /tmp/skilltest3 && mkdir -p /tmp/skilltest3 && HOME=/tmp/skilltest3 GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="invalid-token-xyz" bash .gitea/workflows/scripts/skill-gitea-api.sh 2>&1 | tail -1; echo "=== fallb…`
🔧 **bash**: `rm -rf /tmp/skilltest && mkdir -p /tmp/skilltest && HOME=/tmp/skilltest GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="$AGENT_TOKEN" bash .gitea/workflows/scripts/skill-gitea-api.sh 2>&1 | tail -1; echo "=== examples pres…`
💬 Fallback now triggers correctly when introspection fails. Let me re-confirm the valid-token path still works and the static examples survived:
🔧 **bash**: `python3 -c "import yaml,sys; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('YAML OK')" 2>&1 || echo "no pyyaml, trying shell"; bash -n .gitea/workflows/scripts/skill-gitea-api.sh && echo "script syntax OK"; bash -n .gitea/workfl…`
💬 All 6 examples + the search example (from PR #18's earlier work, now folded into the canonical script) + live matrix. Let me verify the YAML is still valid and check final diff:
🔧 **bash**: `pip install pyyaml -q 2>&1 | tail -1; python3 -c "import yaml; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('YAML OK')"`
🔧 **bash**: `which yamllint yq 2>/dev/null; python3 -m venv /tmp/yv 2>&1 | tail -1 && /tmp/yv/bin/pip install pyyaml -q 2>&1 | tail -1 && /tmp/yv/bin/python -c "import yaml; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('YAML OK')"`
🔧 **bash**: `yq '.jobs.agent.steps[] | select(.name | test("gitea-api")) | .env' .gitea/workflows/agent.yml`
🔧 **bash**: `git add .gitea/workflows/agent.yml .gitea/workflows/scripts/skill-gitea-api.sh .gitea/workflows/scripts/run-agent.sh && git status --short && git diff --cached --stat`
💬 YAML parses, env block correct. Now commit and push:
🔧 **bash**: `git commit -m "@senior: issue #17 — live agent capability table via Gitea 1.27 /api/v1/token
skill-gitea-api.sh now introspects each agent's own PAT (AGENT_TOKEN +
TOKEN_PM/SENIOR/JUNIOR/LEAD/QA) via GET /api/v1/token — a Gitea 1.27+
self-…`
💬 Pushed. The automation will open the PR from the branch. Here's the summary for the issue thread:
🤖 **@senior** — done, on a fresh branch `ai/issue-17-capabilities` (the old `ai/issue-17`/PR #18 predates the scripts refactor and is stale).
**What I built (live Agent Capability Table, per @pm's approved plan):**
1. **`skill-gitea-api.sh`** — now introspects each agent's own PAT (`AGENT_TOKEN` + `TOKEN_PM/SENIOR/JUNIOR/LEAD/QA`) via Gitea 1.27's `GET /api/v1/token` (self-introspection: token-in, `{name,scopes,user}`-out, no password) and bakes a **live matrix** into `SKILL.md`:
```
| Agent | Gitea user | Token name | Scopes |
| @shared (AGENT_TOKEN) | ffaerber | full | read:organization, read:user, write:issue, write:repository, … |
```
Verified against the real instance (`1.27.0+rc0`): the endpoint returns real scopes, never the token string. Falls back to a static note on `<1.27` or unset tokens (tested both paths locally). The existing static scope description + all examples (incl. the keyword-search "knowledge base" example from PR #18) are preserved.
2. **`agent.yml`** — passes `GITHUB_SERVER_URL` + the per-agent `TOKEN_*` secrets into the gitea-api skill step so the script can introspect them.
3. **`run-agent.sh`** — one roster note pointing agents to the live matrix for capability-based routing.
**On plan point #3 (least-privilege gating):** already done in issue #22 — `agents.json` `skills` arrays + `install-opencode.sh` `permission.skill` scoping mean `@junior` (`skills:[]`) never even sees the `gitea-api` summary line, and denied skills are fully hidden. No redundant `case` gates added.
Verified: shell syntax, YAML parses (`yq` + `pyyaml`), live-introspection path, fallback path, and that all 6 examples + the search example survive. PR will be auto-opened from the branch.
BEGIN_PR_DESCRIPTION
## Summary
Implements the live Agent Capability Table approved in issue #17: every agent that loads the `gitea-api` skill now sees a live, always-accurate matrix of every teammate's real Gitea scopes, auto-introspected at workflow start via Gitea 1.27's `GET /api/v1/token` endpoint. No hand-maintained table to drift when scopes rotate. Also folds in the keyword-search "knowledge base" example so PM can look up similar past issues instead of re-asking settled questions.
## Changes
- `.gitea/workflows/scripts/skill-gitea-api.sh`: introspect `AGENT_TOKEN` + `TOKEN_PM/SENIOR/JUNIOR/LEAD/QA` via `GET /api/v1/token`, render a live capability matrix table into `SKILL.md`; graceful fallback on `<1.27`/unset tokens; preserves all existing examples + adds the keyword-search example.
- `.gitea/workflows/agent.yml`: pass `GITHUB_SERVER_URL` + per-agent `TOKEN_*` secrets into the gitea-api skill step.
- `.gitea/workflows/scripts/run-agent.sh`: roster note pointing agents to the live matrix for capability-based routing.
END_PR_DESCRIPTION
</details>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Stop
@pm(and dev agents) from re-asking the maintainer questions already answered on past issues. Thegitea-apiskill already had cross-repo read access viaAGENT_TOKEN(added in #14) but no "search for similar issues" example, and the PM prompt never told it to look at past issues. Both gaps are fixed in the shared reusable workflow (agent.yml), so every caller repo benefits.Changes
.gitea/workflows/agent.yml—gitea-apiskill SKILL.md heredoc: added a "Search for similar/related past issues by keyword" example with the same-repo issue-list endpoint (?q=<keywords>&type=issues&state=all&limit=20) and the cross-repo/repos/issues/search?q=...variant, plus tips (pick keywords from the new issue,state=allto surface closed/answered issues, open matched threads, cite issue numbers, only ask what precedent doesn't cover). Existing examples left intact..gitea/workflows/agent.yml— PMACTIONprompt block: added aKNOWLEDGE-BASEphase before the existingPLANphase instructing@pmto search this repo and other repos on the instance (e.g. homelab) for similar past issues before presenting a plan or asking clarifying questions, reuse the maintainer's prior answers, cite matched issue numbers (e.g.homelab#103), and only ask about things genuinely not covered by precedent.$to avoid shell expansion in the double-quoted bash string.Resolves #17 · 🤖 @senior
🤖 @senior — ✅ PR ready for review — @ffaerber please review & merge:
🤖 @senior — activity log (93 entries):
tool calls & reasoning
🔧 bash:
git branch --show-current && git log --oneline -10 && git status🔧 read:
.gitea/workflows/agent.yml🔧 bash:
ls -la .gitea/workflows/scripts/🔧 read:
.gitea/workflows/scripts/skill-gitea-api.sh🔧 bash:
git log --oneline main..ai/issue-17; echo "---"; git log --oneline ai/issue-17..main; echo "--- remote ---"; git branch -r | head -20🔧 bash:
git log --oneline origin/ai/issue-17 -10🔧 bash:
git log --oneline origin/main -5🔧 bash:
git log --oneline --all | grep -i "issue-17\|knowledge\|capability\|#17" | head; echo "---"; git log --oneline origin/main | grep -i "17\|knowledge\|capability" | head🔧 bash:
git show 644cf85 --stat; echo "==="; git log --oneline origin/main | head -30🔧 bash:
git show 644cf85🔧 bash:
git log --oneline origin/main | head -3; echo "---"; git fetch origin; git log --oneline origin/main..origin/ai/issue-17; echo "==="; git log --oneline origin/ai/issue-17..origin/main | head -40🔧 read:
.gitea/workflows/scripts/run-agent.sh🔧 read:
.gitea/workflows/scripts/skill-node1-ssh.sh🔧 read:
.gitea/workflows/scripts/route.sh🔧 read:
.gitea/workflows/scripts/agents.json🔧 read:
.gitea/workflows/scripts/install-opencode.sh🔧 bash:
git log --oneline -3 HEAD; echo "--- origin/ai/issue-17 ---"; git log --oneline -3 origin/ai/issue-17; echo "--- is 644cf85 in HEAD? ---"; git merge-base --is-ancestor 644cf85 HEAD && echo "YES" || echo "NO"🔧 bash:
git reset --hard origin/ai/issue-17 && git log --oneline -3🔧 bash:
git checkout main && git pull --ff-only origin main && git checkout -b ai/issue-17-capabilities && git log --oneline -3💬 I've got the full picture now. Let me note the state:
ai/issue-17has a stale commit (644cf85) from PR #18 — it editedagent.ymlinline, butagent.ymlhas since been refactored to externalize scripts into.gitea/workflows/scripts/*.sh. So PR #18 is stale/conflicted.GET /api/v1/token) is independent of the search-example work, so per AGENTS.md it gets its own branch/PR.agents.jsonskillsarrays +install-opencode.shpermission.skillscoping (issue #22) —@juniorhasskills:[], etc. I won't redo it.Let me create a fresh branch off main for the capability table.
💬 Now let me verify the
/api/v1/tokenendpoint shape before coding against it.🔧 skill:
Loaded skill: gitea-api🔧 bash:
curl -sS -H "Authorization: token $AGENT_TOKEN" "$GITHUB_SERVER_URL/api/v1/version"; echo; curl -sS -H "Authorization: token $AGENT_TOKEN" "$GITHUB_SERVER_URL/api/v1/token" | jq '.' 2>&1 | head -40🔧 bash:
echo "GITHUB_SERVER_URL=$GITHUB_SERVER_URL"; echo "AGENT_TOKEN set? $([ -n "$AGENT_TOKEN" ] && echo yes || echo no)"; env | grep -iE "TOKEN_(PM|SENIOR|JUNIOR|LEAD|QA)" | sed 's/=.*/=***/'🔧 write:
.gitea/workflows/scripts/skill-gitea-api.sh💬 Got it. The
/api/v1/tokenendpoint works on this 1.27 instance and returnsscopes. Now implementing. The existingai/issue-17branch has a stale PR #18 (editedagent.ymlinline before the scripts refactor) — I'll leave that alone and put the capability-table work on a fresh branch.Plan for this branch:
skill-gitea-api.sh— introspect each agent token viaGET /api/v1/token, build a live capability matrix table intoSKILL.md.agent.yml— pass theTOKEN_*env vars into the gitea-api skill step so the script can introspect them.run-agent.sh— one roster note pointing agents to the capability matrix.Note: least-privilege gating (plan point #3) is already done via
agents.jsonskillsarrays +install-opencode.shpermission.skillscoping (issue #22) —@juniorhasskills:[], denied skills are fully hidden. No redundantcasegates needed.🔧 bash:
chmod +x .gitea/workflows/scripts/skill-gitea-api.sh && bash -n .gitea/workflows/scripts/skill-gitea-api.sh && echo "syntax OK"🔧 edit:
.gitea/workflows/agent.yml💬 Now update
agent.ymlto pass theTOKEN_*env vars andGITHUB_SERVER_URLinto the gitea-api skill step:🔧 edit:
.gitea/workflows/scripts/run-agent.sh💬 Now add a roster note in
run-agent.shpointing agents to the capability matrix in the skill:🔧 bash:
rm -rf /tmp/skilltest && mkdir -p /tmp/skilltest && HOME=/tmp/skilltest GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="$AGENT_TOKEN" bash .gitea/workflows/scripts/skill-gitea-api.sh && echo "=== SKILL.md (head 60) ===" &&…💬 Now let me verify the whole pipeline works locally by simulating the script:
🔧 bash:
rm -rf /tmp/skilltest2 && mkdir -p /tmp/skilltest2 && HOME=/tmp/skilltest2 GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="$AGENT_TOKEN" TOKEN_PM="" TOKEN_SENIOR="" TOKEN_JUNIOR="" TOKEN_LEAD="" TOKEN_QA="" bash .gitea/wor…💬 Live matrix works. Only
@shared (AGENT_TOKEN)row appears because theTOKEN_*per-agent secrets aren't set in my local env — in CI they will be. Let me also test the fallback path (no tokens at all) and that the static body survives intact:🔧 bash:
rm -rf /tmp/skilltest3 && mkdir -p /tmp/skilltest3 && HOME=/tmp/skilltest3 GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="invalid-token-xyz" bash .gitea/workflows/scripts/skill-gitea-api.sh 2>&1 | tail -2; echo "=== fallb…🔧 edit:
.gitea/workflows/scripts/skill-gitea-api.sh🔧 bash:
rm -rf /tmp/skilltest3 && mkdir -p /tmp/skilltest3 && HOME=/tmp/skilltest3 GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="invalid-token-xyz" bash .gitea/workflows/scripts/skill-gitea-api.sh 2>&1 | tail -1; echo "=== fallb…🔧 bash:
rm -rf /tmp/skilltest && mkdir -p /tmp/skilltest && HOME=/tmp/skilltest GITHUB_SERVER_URL="https://git.ffaerber.duckdns.org" AGENT_TOKEN="$AGENT_TOKEN" bash .gitea/workflows/scripts/skill-gitea-api.sh 2>&1 | tail -1; echo "=== examples pres…💬 Fallback now triggers correctly when introspection fails. Let me re-confirm the valid-token path still works and the static examples survived:
🔧 bash:
python3 -c "import yaml,sys; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('YAML OK')" 2>&1 || echo "no pyyaml, trying shell"; bash -n .gitea/workflows/scripts/skill-gitea-api.sh && echo "script syntax OK"; bash -n .gitea/workfl…💬 All 6 examples + the search example (from PR #18's earlier work, now folded into the canonical script) + live matrix. Let me verify the YAML is still valid and check final diff:
🔧 bash:
pip install pyyaml -q 2>&1 | tail -1; python3 -c "import yaml; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('YAML OK')"🔧 bash:
which yamllint yq 2>/dev/null; python3 -m venv /tmp/yv 2>&1 | tail -1 && /tmp/yv/bin/pip install pyyaml -q 2>&1 | tail -1 && /tmp/yv/bin/python -c "import yaml; yaml.safe_load(open('.gitea/workflows/agent.yml')); print('YAML OK')"🔧 bash:
yq '.jobs.agent.steps[] | select(.name | test("gitea-api")) | .env' .gitea/workflows/agent.yml🔧 bash:
git add .gitea/workflows/agent.yml .gitea/workflows/scripts/skill-gitea-api.sh .gitea/workflows/scripts/run-agent.sh && git status --short && git diff --cached --stat💬 YAML parses, env block correct. Now commit and push:
🔧 bash: `git commit -m "@senior: issue #17 — live agent capability table via Gitea 1.27 /api/v1/token
skill-gitea-api.sh now introspects each agent's own PAT (AGENT_TOKEN +
TOKEN_PM/SENIOR/JUNIOR/LEAD/QA) via GET /api/v1/token — a Gitea 1.27+
self-…`
💬 Pushed. The automation will open the PR from the branch. Here's the summary for the issue thread:
🤖 @senior — done, on a fresh branch
ai/issue-17-capabilities(the oldai/issue-17/PR #18 predates the scripts refactor and is stale).What I built (live Agent Capability Table, per @pm's approved plan):
skill-gitea-api.sh— now introspects each agent's own PAT (AGENT_TOKEN+TOKEN_PM/SENIOR/JUNIOR/LEAD/QA) via Gitea 1.27'sGET /api/v1/token(self-introspection: token-in,{name,scopes,user}-out, no password) and bakes a live matrix intoSKILL.md:Verified against the real instance (
1.27.0+rc0): the endpoint returns real scopes, never the token string. Falls back to a static note on<1.27or unset tokens (tested both paths locally). The existing static scope description + all examples (incl. the keyword-search "knowledge base" example from PR #18) are preserved.agent.yml— passesGITHUB_SERVER_URL+ the per-agentTOKEN_*secrets into the gitea-api skill step so the script can introspect them.run-agent.sh— one roster note pointing agents to the live matrix for capability-based routing.On plan point #3 (least-privilege gating): already done in issue #22 —
agents.jsonskillsarrays +install-opencode.shpermission.skillscoping mean@junior(skills:[]) never even sees thegitea-apisummary line, and denied skills are fully hidden. No redundantcasegates added.Verified: shell syntax, YAML parses (
yq+pyyaml), live-introspection path, fallback path, and that all 6 examples + the search example survive. PR will be auto-opened from the branch.BEGIN_PR_DESCRIPTION
Summary
Implements the live Agent Capability Table approved in issue #17: every agent that loads the
gitea-apiskill now sees a live, always-accurate matrix of every teammate's real Gitea scopes, auto-introspected at workflow start via Gitea 1.27'sGET /api/v1/tokenendpoint. No hand-maintained table to drift when scopes rotate. Also folds in the keyword-search "knowledge base" example so PM can look up similar past issues instead of re-asking settled questions.Changes
.gitea/workflows/scripts/skill-gitea-api.sh: introspectAGENT_TOKEN+TOKEN_PM/SENIOR/JUNIOR/LEAD/QAviaGET /api/v1/token, render a live capability matrix table intoSKILL.md; graceful fallback on<1.27/unset tokens; preserves all existing examples + adds the keyword-search example..gitea/workflows/agent.yml: passGITHUB_SERVER_URL+ per-agentTOKEN_*secrets into the gitea-api skill step..gitea/workflows/scripts/run-agent.sh: roster note pointing agents to the live matrix for capability-based routing.END_PR_DESCRIPTION
Pull request closed