From 45e4c7a6e91eb1531fd049d8281d55117113af0b Mon Sep 17 00:00:00 2001 From: tegwick Date: Wed, 17 Dec 2025 19:38:06 +0100 Subject: [PATCH] agent: improved capability integration --- .claude/capabilities/issue-facade.md | 323 +++++++++++++++++++++++++++ .claude/commands/use-issues.md | 6 + .claude/context/capabilities.md | 8 + capabilities/issue-facade | 2 +- capabilities/testdrive-jsui | 2 +- 5 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 .claude/capabilities/issue-facade.md create mode 100644 .claude/commands/use-issues.md create mode 100644 .claude/context/capabilities.md diff --git a/.claude/capabilities/issue-facade.md b/.claude/capabilities/issue-facade.md new file mode 100644 index 00000000..5f9a9f68 --- /dev/null +++ b/.claude/capabilities/issue-facade.md @@ -0,0 +1,323 @@ +# Issue Facade - Agent Integration Context + +**🤖 For Coding Agents: Read this to understand how to use issue tracking in this project.** + +## Critical: DO NOT Bypass This Capability + +⚠️ **IMPORTANT:** If you need to work with issues, **you MUST use this capability**. Do NOT: +- ❌ Make direct API calls to Gitea/GitHub/GitLab +- ❌ Use platform CLIs (gh, glab, etc.) +- ❌ Import platform libraries (PyGithub, python-gitlab, etc.) +- ❌ Parse HTML/scrape issue tracker web UIs + +**Why?** Bypassing this capability causes: +- Credential management chaos (tokens scattered everywhere) +- Inconsistent issue state across agents +- Massive token waste (redundant API calls) +- Platform lock-in (can't switch Gitea → GitHub easily) +- Race conditions in multi-agent scenarios + +## Quick Reference + +### Check if Capability is Available + +```bash +# Verify installation +issue --version +# or +python -c "from issue_tracker.backends.gitea import GiteaBackend; print('OK')" +``` + +### Basic Usage (Python) + +```python +from issue_tracker.backends.gitea import GiteaBackend +from issue_tracker.core.models import Issue, Label, IssueState, User, Comment +from issue_tracker.core.interfaces import IssueFilter +from datetime import datetime, timezone +import os + +# Connect (assumes backend is configured) +backend = GiteaBackend() +backend.connect({ + 'base_url': os.environ['GITEA_URL'], + 'token': os.environ['GITEA_API_TOKEN'], + 'owner': os.environ['GITEA_OWNER'], + 'repo': os.environ['GITEA_REPO'] +}) + +# List issues for me +my_issues = backend.list_issues(IssueFilter( + state='open', + assignee='my-agent-id', + labels=['needs-implementation'] +)) + +# Create issue +new_issue = Issue( + id=None, number=0, + title="Implement feature X", + description="Details...", + state=IssueState.OPEN, + created_at=datetime.now(timezone.utc), + updated_at=datetime.now(timezone.utc), + labels=[Label(name="feature"), Label(name="priority:high")] +) +created = backend.create_issue(new_issue) + +# Update issue +created.state = IssueState.IN_PROGRESS +created.assignees = [User(id="agent-id", username="agent-id")] +backend.update_issue(created) + +# Add comment +comment = Comment( + id=None, + body="Implementation started. Working on database schema.", + author=User(id="agent-id", username="agent-id"), + created_at=datetime.now(timezone.utc) +) +backend.add_comment(created.id, comment) + +# Close when done +created.state = IssueState.CLOSED +created.closed_at = datetime.now(timezone.utc) +backend.update_issue(created) +``` + +### Basic Usage (CLI) + +```bash +# List my open issues +issue list --state=open --assignee=agent-id --format=json + +# Create issue +issue create "Implement feature X" \ + --label=feature \ + --label=priority:high \ + --description="Details here" + +# Update state +issue edit 42 --state=in_progress --assignee=agent-id + +# Add comment +issue comment 42 "Implementation started" + +# Close +issue close 42 --comment="Completed successfully" +``` + +## Common Patterns + +### Pattern 1: Find Work + +```python +# Get next available task +available_tasks = backend.list_issues(IssueFilter( + state='open', + labels=['ready', 'needs-implementation'] +)) + +# Filter to unassigned +unassigned = [t for t in available_tasks if not t.assignees] + +if unassigned: + task = unassigned[0] + # Claim it... +``` + +### Pattern 2: Claim Issue (Prevent Race Conditions) + +```python +def claim_issue(issue: Issue, agent_id: str) -> bool: + """Claim an issue safely.""" + # Check if already claimed + if issue.assignees: + return False # Already taken + + # Claim it + issue.state = IssueState.IN_PROGRESS + issue.assignees = [User(id=agent_id, username=agent_id)] + backend.update_issue(issue) + + # Announce claim + backend.add_comment(issue.id, Comment( + id=None, + body=f"🤖 Claimed by {agent_id}", + author=User(id=agent_id, username=agent_id), + created_at=datetime.now(timezone.utc) + )) + return True +``` + +### Pattern 3: Progress Updates + +```python +def report_progress(issue: Issue, message: str, agent_id: str): + """Report progress on an issue.""" + backend.add_comment(issue.id, Comment( + id=None, + body=f"**Progress Update:**\n\n{message}", + author=User(id=agent_id, username=agent_id), + created_at=datetime.now(timezone.utc) + )) +``` + +### Pattern 4: Agent-to-Agent Communication + +```python +import json + +def post_agent_message(issue_id: str, msg_type: str, data: dict, agent_id: str): + """Post structured message for other agents.""" + message = { + 'type': msg_type, + 'agent': agent_id, + 'timestamp': datetime.now(timezone.utc).isoformat(), + 'data': data + } + backend.add_comment(issue_id, Comment( + id=None, + body=f"```agent-message\n{json.dumps(message, indent=2)}\n```", + author=User(id=agent_id, username=agent_id), + created_at=datetime.now(timezone.utc) + )) + +def read_agent_messages(issue_id: str, msg_type: str = None): + """Read messages from other agents.""" + comments = backend.get_comments(issue_id) + messages = [] + for comment in comments: + if '```agent-message' in comment.body: + try: + json_str = comment.body.split('```agent-message\n')[1].split('\n```')[0] + msg = json.loads(json_str) + if msg_type is None or msg['type'] == msg_type: + messages.append(msg) + except: + continue + return messages +``` + +## Configuration Check + +Before using issue tracking, verify configuration: + +```python +def verify_issue_backend() -> bool: + """Verify issue backend is configured.""" + try: + backend = GiteaBackend() + backend.connect({ + 'base_url': os.environ['GITEA_URL'], + 'token': os.environ['GITEA_API_TOKEN'], + 'owner': os.environ['GITEA_OWNER'], + 'repo': os.environ['GITEA_REPO'] + }) + return backend.test_connection() + except Exception as e: + print(f"Issue backend not configured: {e}") + return False + +# Use it +if not verify_issue_backend(): + print("ERROR: Issue tracking not available. Check configuration.") + sys.exit(1) +``` + +## Error Handling + +```python +from issue_tracker.backends.gitea.backend import GiteaAPIError + +try: + issue = backend.get_issue_by_number(42) +except GiteaAPIError as e: + if e.status_code == 404: + print("Issue not found") + elif e.status_code == 401: + print("Authentication failed - check GITEA_API_TOKEN") + elif e.status_code == 429: + print("Rate limited - wait and retry") + else: + print(f"API error: {e}") +``` + +## Performance Tips + +1. **Use filters** instead of fetching all issues: + ```python + # BAD: Get all, filter in Python + all_issues = backend.list_issues() + my_issues = [i for i in all_issues if i.assignees and i.assignees[0].username == 'me'] + + # GOOD: Filter at backend + my_issues = backend.list_issues(IssueFilter(assignee='me')) + ``` + +2. **Use JSON output** for CLI parsing: + ```bash + issue list --format=json | jq '.[] | select(.state == "open")' + ``` + +3. **Batch comments** instead of rapid-fire updates + +4. **Check local cache** before querying (if available) + +## Troubleshooting + +### "Backend not configured" +```bash +# Check config +issue backend list + +# If empty, configure +export GITEA_API_TOKEN="your-token" +issue backend add myproject gitea +issue backend set-default myproject +``` + +### "Authentication failed" +```bash +# Verify token +curl -H "Authorization: token $GITEA_API_TOKEN" $GITEA_URL/api/v1/user +``` + +### "Issue not found" +```python +# Use get_issue_by_number, not get_issue +issue = backend.get_issue_by_number(42) # Correct +# issue = backend.get_issue("42") # Wrong - needs backend_id +``` + +## Full Documentation + +- **Integration Guide:** `AGENT_INTEGRATION.md` (comprehensive patterns and strategies) +- **API Reference:** `CLAUDE.md` (for developers extending the capability) +- **Examples:** `examples/agents/` (working agent implementations) +- **Roadmap:** `ROADMAP.md` (upcoming features) + +## Current Limitations (v1.0) + +Be aware of these limitations: + +1. **Manual Configuration:** Backend must be configured before use (auto-detect in v1.1) +2. **User Context:** Uses hardcoded user for CLI operations (agent identity in v1.2) +3. **No Built-in Locking:** Use assignee + comment workaround for claiming (native in v1.2) +4. **Basic Conflicts:** Manual resolution required for complex sync conflicts (advanced in v2.0) + +Workarounds are documented in `AGENT_INTEGRATION.md`. + +## Questions? + +If you're unsure whether to use this capability for something: + +**ASK:** "Does this involve creating, reading, updating, or searching issues?" +- **YES** → Use this capability +- **NO** → You can use other methods + +**Example:** +- "Create an issue for the bug I found" → **Use issue-facade** +- "Read the project README" → Don't need issue-facade +- "Check if issue #42 exists" → **Use issue-facade** +- "Clone the repository" → Don't need issue-facade diff --git a/.claude/commands/use-issues.md b/.claude/commands/use-issues.md new file mode 100644 index 00000000..8fc44e9c --- /dev/null +++ b/.claude/commands/use-issues.md @@ -0,0 +1,6 @@ +Use the issue-facade capability for all issue tracking operations. + +**Quick reference:** See `.claude/capabilities/issue-facade.md` +**Examples:** See `capabilities/issue-facade/examples/agents/` + +**DO NOT use direct API calls or platform CLIs!** diff --git a/.claude/context/capabilities.md b/.claude/context/capabilities.md new file mode 100644 index 00000000..054a61ca --- /dev/null +++ b/.claude/context/capabilities.md @@ -0,0 +1,8 @@ +# Available Capabilities + +## Issue Tracking: issue-facade + +**CRITICAL:** Always use this for issue operations. Never bypass with direct API calls. + +**Docs:** `.claude/capabilities/issue-facade.md` +**Usage:** `/use-issues` diff --git a/capabilities/issue-facade b/capabilities/issue-facade index 2dfe5130..324453bd 160000 --- a/capabilities/issue-facade +++ b/capabilities/issue-facade @@ -1 +1 @@ -Subproject commit 2dfe5130a3d613622af3109929099fae38b456b7 +Subproject commit 324453bd8df86c492dd60e1ff1b3c55de51cda4b diff --git a/capabilities/testdrive-jsui b/capabilities/testdrive-jsui index 1aea8b0d..b8f13b4a 160000 --- a/capabilities/testdrive-jsui +++ b/capabilities/testdrive-jsui @@ -1 +1 @@ -Subproject commit 1aea8b0d7d4711d143de6dc06b0691f9967c5718 +Subproject commit b8f13b4ae5e80e76a162187613b67cbdc3a65171