diff --git a/docs/phase3a-setup.md b/docs/kugetsu-chat-setup.md similarity index 100% rename from docs/phase3a-setup.md rename to docs/kugetsu-chat-setup.md diff --git a/skills/kugetsu-pm/SKILL.md b/skills/kugetsu-pm/SKILL.md index aea1012..65016f8 100644 --- a/skills/kugetsu-pm/SKILL.md +++ b/skills/kugetsu-pm/SKILL.md @@ -1,11 +1,11 @@ --- name: kugetsu-pm -description: PM (Project Manager) Agent skill for kugetsu. Handles task coordination, delegation, and Gitea integration. +description: PM (Project Manager) Agent skill for kugetsu. Handles task coordination, delegation, and notifications. license: MIT compatibility: Requires kugetsu CLI, opencode sessions, Gitea API access. metadata: author: shoko - version: "1.0" + version: "2.0" --- # kugetsu-pm - PM Agent Skill @@ -18,37 +18,44 @@ The PM Agent is a persistent opencode session managed by kugetsu. It: 1. **Receives** task requests from Chat Agent (via Hermes) 2. **Coordinates** task execution via Dev Agents -3. **Monitors** Gitea for issue updates -4. **Notifies** users of task completion (if in notify mode) +3. **Monitors** Gitea for issue/PR updates +4. **Notifies** users of task completion and status changes 5. **Maintains** context across interactions ## Architecture ``` -Chat Agent (Hermes/Telegram) - │ - ├── Routes task requests - │ - ▼ -PM Agent (opencode session via kugetsu) - │ - ├── Creates Dev Agent sessions via kugetsu - │ - ▼ -Dev Agents (opencode sessions via kugetsu) - │ - ├── Work on issues autonomously - │ - ▼ -Gitea (Issues, PRs, Comments) +User (Telegram) → Hermes → Chat Agent → PM Agent + │ + ├── kugetsu start → Dev Agent + │ └── Work on issue + │ + └── notify ← Gitea (optional) + └── ~/.kugetsu/notifications.json ``` -## PM Agent Modes +## Notification System -| Mode | Behavior | Command | -|------|----------|---------| -| **notify** (default) | Send completion notifications | "pm notify" | -| **silent** | Work quietly, no notifications | "pm silent" | +PM Agent notifies users via two channels: + +### 1. Local Notifications (Default) +- PM Agent writes to `~/.kugetsu/notifications.json` +- Hermes/Chat Agent reads this file when user sends a message +- User can view with `kugetsu notify list` + +### 2. Gitea Comments (When Available) +- If task is issue/PR-related, PM Agent posts to Gitea +- User receives notification via Gitea's native notification system +- If Gitea is unavailable, PM Agent logs to notifications.json with a note + +### Notification Triggers + +| Event | Action | +|-------|--------| +| Task assigned to Dev Agent | Write to notifications.json | +| Task completed (PR merged/closed) | Write to notifications.json + Gitea | +| Task blocked | Write to notifications.json | +| Dev agent needs review | Two options: review immediately OR ask dev if ready | ## Task Flow @@ -79,77 +86,73 @@ PM Agent decides: kugetsu start "" ``` -### 5. Monitor and Notify +### 5. Monitor for Completion -- PM monitors Gitea for PR status -- When complete, notifies user (if in notify mode) +PM Agent monitors Gitea for task completion by checking: +- Issue comments +- PR commits +- PR status (merged/open) -## Gitea Integration - -### Context Fetching - -PM Agent fetches from Gitea when: -- Initial task load (no context) -- Explicit request (agent decides) -- Insufficient context - -### Context Merge Strategy - -- **Default**: Append new context to existing -- **Threshold**: Summarize + replace at 40% of context window - -## Session Context - -PM Agent maintains: - -### Managed Repositories -```json -{ - "repos": [ - "github.com/shoko/kugetsu", - "gitlab.com/team/core" - ] -} +**Query pattern for completion:** +```bash +# Check if issue/PR has recent activity +curl -s "https://git.fbrns.co/api/v1/repos/{owner}/{repo}/issues/{number}/comments" +# Check for commits in PR branch ``` -### Active Tasks -```json -{ - "tasks": { - "issue-5": { - "status": "in_progress", - "dev_agent": "ses_xyz789", - "created_at": "2026-03-30T10:00:00Z" - } - } -} -``` +### 6. Review Decision -### Notification Preferences +When dev agent signals completion, PM Agent chooses: + +**Option A: Review immediately** +- PM Agent reviews the PR/changes +- If good, merges or approves +- If issues, posts review comments + +**Option B: Ask dev if ready** +- PM Agent posts comment: "Dev work complete. Please review and confirm if ready for merge." +- Waits for dev agent confirmation +- Then proceeds with review + +### 7. Notify on Completion + +After task completion: +1. Write to `~/.kugetsu/notifications.json` +2. Post to Gitea issue/PR comment (if available) +3. If Gitea fails, note in notifications.json + +**Notification format:** ```json { - "mode": "notify" + "type": "task_complete", + "message": "Issue #5 fixed. PR #12 created and merged.", + "issue_ref": "github.com/shoko/kugetsu#5", + "gitea_url": "https://git.fbrns.co/shoko/kugetsu/pulls/12", + "timestamp": "2026-03-31T10:00:00Z" } ``` ## Delegation Commands -### Create Dev Agent Session +### Send message to PM Agent +```bash +kugetsu delegate "" +``` +### Create Dev Agent Session ```bash kugetsu start "" ``` ### Continue Dev Agent Session - ```bash kugetsu continue "" ``` -### List Active Sessions - +### Check Notifications ```bash -kugetsu list +kugetsu notify list +kugetsu notify clear ``` ## Response Format @@ -165,6 +168,7 @@ PM Agent responses should be: "Created task for issue #5. Dev agent started." "Issue #5 is complete. PR created: [link]" "Task blocked: Need clarification on requirements." +"Dev work ready for review. Merging PR #12." ``` ## Error Handling @@ -172,7 +176,7 @@ PM Agent responses should be: ### Dev Agent Failure - Analyze failure reason - Retry or escalate to user -- Log to Gitea issue comment +- Log to notifications.json ### Session Not Found - Check kugetsu status: `kugetsu list` @@ -182,18 +186,7 @@ PM Agent responses should be: ### Gitea API Errors - Retry with backoff - Cache last known state -- Inform user if persistent - -## Skills - -### kugetsu (for session management) -- Session creation and continuation -- Worktree management - -### github (for Gitea API) -- Issue fetching -- PR creation -- Comment posting +- Log to notifications.json (user will be notified there) ## Implementation Notes @@ -204,15 +197,36 @@ The PM Agent session is stored in: ~/.kugetsu/index.json → "pm_agent" field ``` -### Accessing PM Agent +### PM Context File (Optional) -```bash -PM_SESSION=$(cat ~/.kugetsu/index.json | python3 -c "import sys,json; print(json.load(sys.stdin).get('pm_agent', ''))") -opencode run --continue --session "$PM_SESSION" "" +Customize PM Agent behavior by creating: +``` +~/.kugetsu/pm-agent.md +``` + +This file is injected into the PM Agent session at init time. + +### Notifications File + +Location: `~/.kugetsu/notifications.json` + +Format: +```json +[ + { + "type": "task_complete|task_blocked|task_assigned", + "message": "Human-readable message", + "issue_ref": "github.com/user/repo#5", + "gitea_url": "https://git.fbrns.co/user/repo/pulls/5", + "timestamp": "ISO8601", + "read": false + } +] ``` ## Related Documentation - [kugetsu-architecture.md](../../docs/kugetsu-architecture.md) - [kugetsu-chat.md](../../docs/kugetsu-chat.md) -- [hermes-setup.md](../../docs/hermes-setup.md) \ No newline at end of file +- [kugetsu-chat-setup.md](../../docs/kugetsu-chat-setup.md) +- [hermes-setup.md](../../docs/hermes-setup.md) diff --git a/skills/kugetsu/scripts/kugetsu b/skills/kugetsu/scripts/kugetsu index df5f157..74051b9 100755 --- a/skills/kugetsu/scripts/kugetsu +++ b/skills/kugetsu/scripts/kugetsu @@ -6,6 +6,7 @@ SESSIONS_DIR="$KUGETSU_DIR/sessions" WORKTREES_DIR="$KUGETSU_DIR/worktrees" REPOS_CONFIG="$KUGETSU_DIR/repos.json" INDEX_FILE="$KUGETSU_DIR/index.json" +NOTIFICATIONS_FILE="$KUGETSU_DIR/notifications.json" usage() { cat << 'EOF' @@ -18,6 +19,7 @@ Usage: kugetsu delegate Send message to PM agent kugetsu status Check kugetsu initialization status kugetsu doctor [--fix] Diagnose and fix kugetsu issues + kugetsu notify [list|clear] Show or clear notifications kugetsu list List all tracked sessions kugetsu prune [--force] Remove orphaned sessions (keeps base + pm-agent) kugetsu destroy [-y] Delete session for issue @@ -39,6 +41,8 @@ Commands: PM context is loaded once at init time. status Check if kugetsu is initialized and PM agent is active. doctor Diagnose kugetsu issues. Use --fix to attempt repairs. + notify Show or clear notifications from PM agent. + Use 'kugetsu notify list' to see unread notifications. list Show all sessions (base + pm-agent + forked issues). prune Remove sessions not in index (orphaned from opencode). Use --force to skip confirmation. @@ -52,12 +56,18 @@ PM Context: into the PM agent session at init time. This allows customizing PM behavior without recreating the session. +Notifications: + PM Agent writes task completion notifications to ~/.kugetsu/notifications.json + Use 'kugetsu notify list' to see unread notifications. + Examples: kugetsu init kugetsu status kugetsu delegate "work on issue #5" kugetsu doctor kugetsu doctor --fix + kugetsu notify list + kugetsu notify clear kugetsu start github.com/shoko/kugetsu#14 "fix bug" kugetsu continue github.com/shoko/kugetsu#14 "add tests" kugetsu list @@ -305,6 +315,143 @@ kugetsu_get_pm_context() { fi } +kugetsu_add_notification() { + local type="$1" + local message="$2" + local issue_ref="${3:-}" + local gitea_url="${4:-}" + + mkdir -p "$(dirname "$NOTIFICATIONS_FILE")" + + local notification=$(python3 << PYEOF +import json +import os +from datetime import datetime + +notification = { + "type": "$type", + "message": "$message", + "issue_ref": "$issue_ref" if "$issue_ref" else None, + "gitea_url": "$gitea_url" if "$gitea_url" else None, + "timestamp": datetime.now().isoformat(), + "read": False +} + +file_path = os.path.expanduser("$NOTIFICATIONS_FILE") +notifications = [] + +if os.path.exists(file_path): + try: + with open(file_path, 'r') as f: + notifications = json.load(f) + except: + notifications = [] + +notifications.append(notification) + +with open(file_path, 'w') as f: + json.dump(notifications, f, indent=2) + +print("Notification added") +PYEOF +) + echo "$notification" +} + +kugetsu_get_notifications() { + local limit="${1:-10}" + + if [ ! -f "$NOTIFICATIONS_FILE" ]; then + echo "[]" + return + fi + + python3 << PYEOF +import json +import os +from datetime import datetime + +file_path = os.path.expanduser("$NOTIFICATIONS_FILE") + +if not os.path.exists(file_path): + print("[]") + exit(0) + +try: + with open(file_path, 'r') as f: + notifications = json.load(f) + + unread = [n for n in notifications if not n.get("read", False)] + unread.sort(key=lambda x: x.get("timestamp", ""), reverse=True) + + for n in unread[:$limit]: + ts = n.get("timestamp", "unknown") + ntype = n.get("type", "info") + msg = n.get("message", "") + issue = n.get("issue_ref", "") + gitea = n.get("gitea_url", "") + + print(f"[{ts}] {ntype}: {msg}") + if issue: + print(f" Issue: {issue}") + if gitea: + print(f" Link: {gitea}") + print() + + if not unread: + print("No unread notifications.") + +except Exception as e: + print(f"Error reading notifications: {e}") +PYEOF +} + +kugetsu_clear_notifications() { + if [ ! -f "$NOTIFICATIONS_FILE" ]; then + return + fi + + python3 << PYEOF +import json +import os + +file_path = os.path.expanduser("$NOTIFICATIONS_FILE") + +if not os.path.exists(file_path): + exit(0) + +try: + with open(file_path, 'r') as f: + notifications = json.load(f) + + for n in notifications: + n["read"] = True + + with open(file_path, 'w') as f: + json.dump(notifications, f, indent=2) + + print("Notifications marked as read") +except Exception as e: + print(f"Error: {e}") +PYEOF +} + +cmd_notify() { + local action="${1:-}" + + case "$action" in + ""|"list"|"show") + kugetsu_get_notifications 10 + ;; + "clear") + kugetsu_clear_notifications + ;; + *) + echo "Usage: kugetsu notify [list|clear]" + ;; + esac +} + cmd_status() { if [ ! -f "$INDEX_FILE" ]; then echo "kugetsu_not_initialized" @@ -978,6 +1125,9 @@ main() { doctor) cmd_doctor "$@" ;; + notify) + cmd_notify "$@" + ;; list) cmd_list "$@" ;;