Compare commits

..

15 Commits

Author SHA1 Message Date
08e40e5396 Merge pull request 'feat(phase3): Full Phase 3 implementation - Chat Agent, PM Agent, and Integration' (#32) from feat/issue-19-phase3 into main 2026-03-31 04:55:21 +02:00
shokollm
9e1ff74330 test(kugetsu): add unit tests for status, delegate, doctor, notify commands
Added 10 new tests:
- kugetsu status (5 tests): uninitialized, base missing, pm-agent missing, Python None handling, session expired
- kugetsu delegate (2 tests): no message, pm-agent missing
- kugetsu doctor (1 test): basic command execution
- kugetsu notify (2 tests): list with no file, clear with no file

Total tests: 38 (all passing)
2026-03-31 02:52:31 +00:00
shokollm
93ebb55f57 refactor: remove obsolete kugetsu-helpers skill
kugetsu-helpers was a shim layer that is no longer needed since:
- kugetsu status replaces check-status
- kugetsu delegate replaces delegate-to-pm
- kugetsu doctor --fix replaces fix-permissions
- kugetsu list/start/continue cover remaining functions

All functionality is now in the kugetsu CLI directly.
2026-03-31 02:48:23 +00:00
shokollm
d35f006ed2 docs: replace git.fbrns.co with git.example.com in documentation
Sensitive URL replaced to prevent accidental exposure.
2026-03-31 02:46:42 +00:00
shokollm
bc40c4f500 refactor: restructure PM role under skills/kugetsu/pm/
### Changes:

1. **Moved kugetsu-pm to skills/kugetsu/pm/SKILL.md**
   - Simplified to 79 lines (under 100 line target)
   - kugetsu v3.0 with essential PM role definition
   - PM context injected at init/start/continue time

2. **Updated kugetsu_get_pm_context()**
   - Now reads from ~/.kugetsu/pm-agent.md (user custom) first
   - Falls back to skills/kugetsu/pm/SKILL.md (default)

3. **Updated kugetsu-chat v4.0**
   - Added notification checking on status/update queries
   - When user asks "status?", "any updates?", etc., check kugetsu notify list
   - Hybrid approach: PM includes notifications + kugetsu-chat checks on status

4. **Removed old skills/kugetsu-pm/SKILL.md**
   - Replaced by skills/kugetsu/pm/SKILL.md

### Structure:
skills/kugetsu/
├── SKILL.md
├── scripts/kugetsu
├── chat/           # future: kugetsu-chat could move here
│   ├── SKILL.md
│   └── SOUL.md
└── pm/
    └── SKILL.md    # PM role definition (v3.0)
2026-03-31 02:38:41 +00:00
shokollm
3d00ddbc1b feat(phase3): add notification system and kugetsu notify command
Phase 3c implementation - Notification System:

### New kugetsu commands:
- `kugetsu notify list` - Show unread notifications from PM Agent
- `kugetsu notify clear` - Mark notifications as read

### Notification system:
- PM Agent writes task events to ~/.kugetsu/notifications.json
- Events: task_complete, task_blocked, task_assigned
- Supports issue_ref and gitea_url for linking
- Hermes/Chat Agent reads notifications on user messages

### kugetsu-pm v2.0:
- Updated documentation with notification behavior
- PM Agent monitors Gitea for task completion
- Two review modes: PM reviews immediately OR asks dev if ready
- Notification triggers documented

### File renamed:
- phase3a-setup.md → kugetsu-chat-setup.md (more descriptive)

### Hermes gateway analysis:
- Gateway is a client (connects to Telegram), not a server
- Cannot push messages directly to Telegram from external process
- Notifications stored locally for Hermes to pick up on next user message
2026-03-31 02:19:50 +00:00
shokollm
b3171ed632 feat(kugetsu): add status, delegate, doctor commands; inject PM context at init
This commit implements Phase 3b/3c architectural improvements:

### New kugetsu CLI commands:
- `kugetsu status` - Check initialization status (replaces kugetsu-helper check-status)
- `kugetsu delegate <msg>` - Send message to PM agent (new command)
- `kugetsu doctor [--fix]` - Diagnose and fix kugetsu issues

### PM Context Injection:
- kugetsu init now reads ~/.kugetsu/pm-agent.md (if exists) and injects
  it into the PM agent session at creation time
- PM context is loaded ONCE at init, not on every delegation
- This improves efficiency - kugetsu-pm content read once, not 10 times

### kugetsu-chat updated:
- Now uses `kugetsu delegate` instead of kugetsu-helper
- Now uses `kugetsu status` instead of kugetsu-helper check-status
- Simplified - no longer depends on kugetsu-helpers

### kugetsu continue:
- Removed strict issue-ref format validation
- Now accepts any session name that is tracked in index.json["issues"]
- Issue-ref format is a guideline, not a hard requirement

### Documentation updated:
- phase3a-setup.md - Updated to reflect new kugetsu commands
- kugetsu-install.sh - Simplified Phase 3a setup instructions

### Breaking changes:
- kugetsu-helpers is no longer required for Phase 3a Chat Agent
- kugetsu-chat skill v3.0 now requires kugetsu CLI with new commands
2026-03-31 01:09:12 +00:00
shokollm
bc3cc8dd1e test(kugetsu-helpers): add unit test suite and fix None/null handling
- Add test suite at skills/kugetsu-helpers/tests/test-kugetsu-helpers.sh
- 11 unit tests covering check-status, delegate-to-pm, get-pm-session, etc.
- Fix bug: Python print(None) outputs literal "None" string, not empty
- All tests pass
2026-03-31 00:02:24 +00:00
shokollm
ef1179839d docs(phase3): update status and add testing plan 2026-03-30 23:00:34 +00:00
shokollm
6db33ea786 fix(phase3a): add fix-permissions command to kugetsu-helper
Add kugetsu_fix_pm_permissions function to fix opencode session permissions
for /tmp/kugetsu directory access. This resolves permission issues when
PM agent tries to access worktree directories.

Usage: kugetsu-helper fix-permissions
2026-03-30 15:52:29 +00:00
shokollm
a6bbd969b6 feat(phase3a): update SKILL.md and SOUL.md with stronger routing instructions
- SKILL.md: More explicit about MUST use this skill for delegation
- SOUL.md: Explicitly instruct to invoke /kugetsu-chat skill first
- Add more explicit delegation rules and error handling
2026-03-30 15:06:04 +00:00
shokollm
f8070246c8 feat(phase3a): add strong routing instructions to SOUL.md
- SOUL.md now explicitly instructs Hermes to ALWAYS use kugetsu-helper for delegation
- Clear delegation rules with examples
- Separation of casual conversation vs delegation

This is the first attempt at making Hermes route via kugetsu-helper automatically.
2026-03-30 14:56:31 +00:00
shokollm
227ec3a22e docs: add Phase 3a installation guide and update install script
- docs/phase3a-setup.md - Complete installation guide for Phase 3a
- skills/kugetsu/scripts/kugetsu-install.sh - Updated to reflect v2.2 changes
2026-03-30 14:25:22 +00:00
shokollm
7c94a59bb6 fix(phase3a): separate SOUL.md personality from SKILL.md routing
- SOUL.md: only personality/voice guidance (no routing logic)
- SKILL.md: definitive routing behavior + delegation process
- Add context passing via temp file for long tasks
- Add error handling table with user-friendly messages

This aligns with Hermes docs: SOUL.md = identity, SKILL.md = behavior
2026-03-30 14:20:21 +00:00
shokollm
60181afe6a feat(phase3a): initial Chat Agent infrastructure
Phase 3a implementation - Hermes Chat Agent configuration:

- kugetsu-chat/SOUL.md - Chat Agent persona and routing logic
- kugetsu-chat/SKILL.md - Chat Agent skill documentation
- kugetsu-chat/scripts/setup - Configuration setup script
- kugetsu-pm/SKILL.md - PM Agent skill documentation
- kugetsu-helpers/SKILL.md - Helper tools for Hermes-kugetsu integration
- kugetsu-helpers/scripts/kugetsu-helpers - Shell functions for delegation

Provides:
- Intent classification (small talk, task, status, mode change)
- PM Agent delegation via terminal()
- kugetsu status checking
- Session management helpers
2026-03-30 14:07:43 +00:00
11 changed files with 1157 additions and 53 deletions

View File

@@ -326,7 +326,7 @@ When a Coding Agent starts, it:
| Phase 1 | ✅ Complete | SSH + Tailscale remote access | | Phase 1 | ✅ Complete | SSH + Tailscale remote access |
| Phase 1b | ✅ Complete | Tailscale VPN setup | | Phase 1b | ✅ Complete | Tailscale VPN setup |
| Phase 2 | 📋 Planned | API Interface | | Phase 2 | 📋 Planned | API Interface |
| Phase 3 | 📋 Planned | Chat Integration (Telegram) | | Phase 3 | 🔄 In Progress | Chat Integration (Telegram) |
| Phase 4 | 📋 Planned | Web Dashboard | | Phase 4 | 📋 Planned | Web Dashboard |
### 6.2 Current Implementation ### 6.2 Current Implementation
@@ -344,7 +344,7 @@ When a Coding Agent starts, it:
| Parallel capacity | How many Coding Agents can run simultaneously on one machine? | Pending | | Parallel capacity | How many Coding Agents can run simultaneously on one machine? | Pending |
| Session management | Does kugetsu properly manage opencode sessions? | ✅ Working | | Session management | Does kugetsu properly manage opencode sessions? | ✅ Working |
| Remote access | Does SSH + Tailscale enable remote work? | ✅ Working | | Remote access | Does SSH + Tailscale enable remote work? | ✅ Working |
| Chat interface | Can Hermes bridge Telegram for mobile UX? | Planned (Phase 3) | | Chat interface | Can Hermes bridge Telegram for mobile UX? | Phase 3a Testing |
### 6.4 Success Criteria ### 6.4 Success Criteria

170
docs/kugetsu-chat-setup.md Normal file
View File

@@ -0,0 +1,170 @@
# Kugetsu Phase 3a Installation Guide
Guide for setting up the Kugetsu Chat Agent (Phase 3a) on a new host/container.
## Prerequisites
1. **Hermes Agent** installed and configured
2. **Telegram bot** created via @BotFather
3. **kugetsu CLI** installed
4. **opencode** installed
## Step 1: Verify Hermes Installation
```bash
hermes version
hermes config show # Check Telegram is configured
```
## Step 2: Link Skills to Hermes
```bash
# Create skill directories
mkdir -p ~/.hermes/skills/kugetsu-chat
# Link skills from kugetsu repo (adjust path as needed)
KUGEETSU_DIR="/path/to/kugetsu" # e.g., ~/repositories/kugetsu
ln -sf "$KUGEETSU_DIR/skills/kugetsu-chat" ~/.hermes/skills/kugetsu-chat
```
## Step 3: Install Chat Agent SOUL
```bash
# Copy SOUL.md to Hermes home (this defines the Chat Agent personality)
cp "$KUGEETSU_DIR/skills/kugetsu-chat/SOUL.md" ~/.hermes/SOUL-chat.md
```
## Step 4: Verify Gateway is Running
```bash
hermes gateway status
# If stopped:
hermes gateway start
```
## Step 5: Initialize kugetsu
**WARNING:** This requires an interactive terminal (TTY) because it spawns the opencode TUI.
You must run this in an **interactive shell**, not via `ssh remote "kugetsu init"`:
```bash
# Option 1: SSH with TTY allocation
ssh -t user@host "kugetsu init"
# Option 2: Connect to existing session and run
ssh user@host
kugetsu init # Run manually in the SSH session
```
This creates:
- **Base session** (for forking dev agents)
- **PM Agent session** (persistent coordinator, loaded with kugetsu-pm context)
If you get `Error: init requires a terminal (TTY)`, you're running via non-interactive SSH. Use `-t` flag or connect directly.
## Step 6: Verify Setup
```bash
# Check kugetsu status
kugetsu status
# Should output: ok
# List all sessions
kugetsu list
```
## Step 7: Test via Telegram
Start a conversation with your bot (@your_bot_username):
| Message | Expected |
|---------|----------|
| `hi` | Responds directly (small talk) |
| `status?` | Routes to PM Agent |
| `fix issue #5` | Routes to PM Agent |
## Troubleshooting
### kugetsu command not found
```bash
export PATH="$HOME/.local/bin:$PATH"
# Or add to ~/.bashrc
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
```
### Gateway not responding
```bash
hermes gateway restart
```
### PM agent issues
```bash
# Diagnose
kugetsu doctor
# Fix (if needed)
kugetsu doctor --fix
# Or reinitialize
kugetsu destroy --pm-agent -y
kugetsu init
```
## kugetsu Commands
| Command | Description |
|---------|-------------|
| `kugetsu init` | Initialize base + PM agent sessions |
| `kugetsu status` | Check if kugetsu is ready |
| `kugetsu delegate <msg>` | Send message to PM agent |
| `kugetsu doctor [--fix]` | Diagnose and fix issues |
| `kugetsu start <issue-ref> <msg>` | Start dev agent for issue |
| `kugetsu continue <issue-ref> <msg>` | Continue existing issue session |
| `kugetsu list` | List all tracked sessions |
| `kugetsu prune [--force]` | Clean up orphaned sessions |
## File Locations
| File | Location | Purpose |
|------|----------|---------|
| Chat Agent SOUL | `~/.hermes/SOUL-chat.md` | Personality |
| kugetsu-chat skill | `~/.hermes/skills/kugetsu-chat/` | Routing behavior |
| kugetsu | `~/.local/bin/kugetsu` | Main CLI |
~/.kugetsu/
├── sessions/
│ ├── base.json # Base opencode session
│ └── pm-agent.json # PM Agent opencode session
├── index.json # Session registry
└── pm-agent.md # PM context (optional, injected at init)
## Architecture Summary
```
~/.hermes/
├── SOUL-chat.md # Chat Agent personality
└── skills/
└── kugetsu-chat/ # Routing + delegation via kugetsu CLI
~/.kugetsu/
├── sessions/
│ ├── base.json # Base opencode session
│ └── pm-agent.json # PM Agent opencode session
├── index.json # Session registry
└── pm-agent.md # PM context (optional)
~/.local/bin/
└── kugetsu # Main CLI (handles delegation, status, doctor)
```
## PM Context (Optional)
To customize PM Agent behavior, create `~/.kugetsu/pm-agent.md` with additional context. This file is injected into the PM Agent session at init time.
## Security Notes
- Never commit `~/.kugetsu/` or SOUL files to version control
- Bot tokens should be in environment variables, not files
- PM agent session IDs are internal - don't expose to users

View File

@@ -1,6 +1,6 @@
# Kugetsu Chat Architecture (Phase 3) # Kugetsu Chat Architecture (Phase 3)
**Status:** Planned (Not Yet Implemented) **Status:** Phase 3a Implemented (Testing in Progress)
**Related Issue:** #19 **Related Issue:** #19
## Overview ## Overview
@@ -135,9 +135,9 @@ PM Agent
│ PM sends clarification request │ PM sends clarification request
Hermes (Chat Gateway) Hermes (Chat Gateway)
│ "Which project did you mean? github.com/user/project or git.fbrns.co/team/core?" │ "Which project did you mean? github.com/user/project or git.example.com/team/core?"
User (Telegram): "git.fbrns.co/team/core" User (Telegram): "git.example.com/team/core"
Hermes (Chat Gateway) Hermes (Chat Gateway)

View File

@@ -61,7 +61,7 @@ Your focus shifts from doing to overseeing — reviewing PRs, approving plans, m
```bash ```bash
# Clone repository # Clone repository
git clone https://git.fbrns.co/shoko/kugetsu.git git clone https://git.example.com/shoko/kugetsu.git
# Install kugetsu # Install kugetsu
bash kugetsu/skills/kugetsu/scripts/kugetsu-install.sh bash kugetsu/skills/kugetsu/scripts/kugetsu-install.sh

View File

@@ -0,0 +1,117 @@
---
name: kugetsu-chat
description: Kugetsu Chat Agent - REQUIRED skill for ALL non-casual Telegram messages. This skill MUST be loaded and used for task delegation.
license: MIT
compatibility: Requires Hermes agent with Telegram configured, kugetsu CLI, opencode sessions.
metadata:
author: shoko
version: "4.0"
---
# kugetsu-chat - REQUIRED SKILL FOR KUGETSU ROUTING
**THIS SKILL MUST BE USED for any message that is not casual conversation.**
## CRITICAL: When to Use This Skill
**YOU MUST LOAD AND USE THIS SKILL when:**
- Any message containing "issue", "fix", "create", "test", "implement"
- Any message containing issue numbers like "#5", "#14", "#123"
- Any message that looks like a task request or status query
- Any message containing "pm", "status", "progress", "work on"
- Any message containing "notify", "updates", "notifications"
**DO NOT USE THIS SKILL for:**
- "hi", "hello", "hey"
- "thanks", "thank you"
- "how are you", "what's up"
- Casual greetings
## Required Actions
### Step 1: Load This Skill
```
/kugetsu-chat
```
### Step 2: Check if kugetsu is initialized
```
terminal(command="kugetsu status", timeout=10)
```
### Step 3: Route Based on Message Type
**For STATUS/UPDATE queries:**
```
terminal(command="kugetsu notify list", timeout=10)
```
Then include notifications in response.
**For TASK requests:**
```
terminal(command="kugetsu delegate '<entire user message>'", timeout=120)
```
### Step 4: Relay the response to the user
## Delegation Command
The command for task delegation:
```bash
kugetsu delegate '<user message>'
```
Example:
```
terminal(command="kugetsu delegate 'fix issue #5 in github.com/shoko/kugetsu'", timeout=120)
```
## Notification Checking
**When user asks about status/updates, check notifications:**
```bash
kugetsu notify list
```
Include any unread notifications in your response.
## Error Handling
| Status Output | Meaning | Action |
|--------------|---------|--------|
| `ok` | kugetsu is ready | Proceed with delegation |
| `kugetsu_not_initialized` | Not set up | Tell user to run `kugetsu init` |
| `pm_agent_missing` | PM not created | Tell user to run `kugetsu init` |
| `pm_agent_expired` | PM session expired | Tell user to run `kugetsu doctor --fix` |
## Quick Reference
**DELEGATION COMMAND:**
```
terminal(command="kugetsu delegate '<message>'", timeout=120)
```
**CHECK NOTIFICATIONS:**
```
terminal(command="kugetsu notify list", timeout=10)
```
**CHECK STATUS:**
```
terminal(command="kugetsu status", timeout=10)
```
## Required Dependencies
- `kugetsu` CLI installed and in PATH
- kugetsu initialized via `kugetsu init`
## Notes
- ALWAYS use `kugetsu delegate` command
- ALWAYS wrap user message in single quotes inside the command
- ALWAYS use timeout of at least 120 seconds for delegation
- kugetsu delegates to the persistent PM agent session created during init
- PM Agent writes task notifications to `~/.kugetsu/notifications.json`

View File

@@ -0,0 +1,57 @@
# Kugetsu Chat Agent
You are the friendly, professional face of the Kugetsu agent team on Telegram.
## Your Voice
- **Friendly but professional** - Warm without being overly casual
- **Concise** - Telegram users prefer short, punchy messages
- **Helpful** - Guide users toward their goals without being pushy
- **Patient** - Some users are new to multi-agent systems
- **Direct** - Get to the point, no fluff
## CRITICAL: Routing Requirement
**YOU MUST ALWAYS use the kugetsu-chat skill for task delegation.**
For ANY message that is not casual conversation, you MUST:
1. First invoke: `/kugetsu-chat`
2. Then use the delegation command from that skill
## Delegation Rules
| User Message Type | Example | Action |
|------------------|---------|--------|
| Casual | "hi", "hello", "thanks" | Respond directly |
| Task | "fix issue #5", "create test for #14" | **MUST DELEGATE** |
| Status | "status?", "what's on #7?" | **MUST DELEGATE** |
| Mode | "pm notify", "pm silent" | **MUST DELEGATE** |
| Question | "how does this work?" | May respond directly |
## Required Delegation Command
```
terminal(command="kugetsu delegate '<user message>'", timeout=120)
```
## When NOT to Delegate
Only for:
- Greetings: "hi", "hello", "hey", "howdy"
- Thanks: "thanks", "thank you", "thx"
- Casual: "how are you", "what's up", "nice"
- Simple questions about the bot itself
## Communication Style
- Keep messages short (Telegram prefers brevity)
- Use emojis sparingly
- Format code/terms in backticks
- Be proactive with suggestions
## Security
- Never reveal session IDs or file paths
- Keep responses user-friendly
- If in doubt, ask for clarification

194
skills/kugetsu-chat/scripts/setup Executable file
View File

@@ -0,0 +1,194 @@
#!/bin/bash
# kugetsu-chat setup script
# Configures Hermes as Chat Agent for Phase 3a
set -euo pipefail
KUGETSU_CHAT_DIR="$(dirname "$(dirname "$(readlink -f "$0")")")"
HERMES_DIR="${HERMES_DIR:-$HOME/.hermes}"
usage() {
cat << 'EOF'
kugetsu-chat setup - Configure Hermes as Chat Agent
Usage:
kugetsu-chat-setup.sh [--apply] [--check]
Options:
--apply Apply the Chat Agent configuration to Hermes
--check Verify configuration without applying
Examples:
./kugetsu-chat-setup.sh --check # Check configuration
./kugetsu-chat-setup.sh --apply # Apply configuration
EOF
}
check_prerequisites() {
echo "=== Checking Prerequisites ==="
if ! command -v hermes &> /dev/null; then
echo "Error: Hermes is not installed or not in PATH"
exit 1
fi
echo "✓ Hermes is installed"
if ! command -v kugetsu &> /dev/null; then
echo "Error: kugetsu is not installed or not in PATH"
exit 1
fi
echo "✓ kugetsu is installed"
if [ ! -f "$HERMES_DIR/config.yaml" ]; then
echo "Error: Hermes config not found at $HERMES_DIR/config.yaml"
exit 1
fi
echo "✓ Hermes config exists"
echo ""
}
verify_kugetsu_init() {
echo "=== Verifying kugetsu Initialization ==="
if [ ! -f "$HOME/.kugetsu/index.json" ]; then
echo "Error: kugetsu not initialized. Run 'kugetsu init' first."
exit 1
fi
if ! grep -q '"pm_agent"' "$HOME/.kugetsu/index.json"; then
echo "Error: kugetsu index.json missing pm_agent field"
exit 1
fi
PM_AGENT=$(python3 -c "import json; print(json.load(open('$HOME/.kugetsu/index.json')).get('pm_agent', ''))" 2>/dev/null || echo "")
if [ -z "$PM_AGENT" ] || [ "$PM_AGENT" = "null" ]; then
echo "Error: PM agent session not initialized. Run 'kugetsu init' first."
exit 1
fi
echo "✓ kugetsu is initialized with PM agent: $PM_AGENT"
echo ""
}
verify_telegram_config() {
echo "=== Verifying Telegram Configuration ==="
if ! grep -q "TELEGRAM_HOME_CHANNEL" "$HERMES_DIR/config.yaml"; then
echo "Warning: TELEGRAM_HOME_CHANNEL not found in Hermes config"
echo " Telegram may not be configured. Run 'hermes gateway setup' to configure."
else
echo "✓ Telegram is configured in Hermes"
fi
echo ""
}
install_soul() {
echo "=== Installing Chat Agent SOUL ==="
SOUL_SOURCE="$KUGETSU_CHAT_DIR/SOUL.md"
SOUL_TARGET="$HERMES_DIR/SOUL-chat.md"
if [ ! -f "$SOUL_SOURCE" ]; then
echo "Error: SOUL.md not found at $SOUL_SOURCE"
exit 1
fi
cp "$SOUL_SOURCE" "$SOUL_TARGET"
echo "✓ Copied SOUL.md to $SOUL_TARGET"
echo ""
}
install_skill() {
echo "=== Installing kugetsu-chat Skill ==="
SKILL_SOURCE="$KUGETSU_CHAT_DIR"
SKILL_TARGET="$HERMES_DIR/skills/kugetsu-chat"
if [ -L "$SKILL_TARGET" ]; then
rm "$SKILL_TARGET"
elif [ -d "$SKILL_TARGET" ]; then
echo "Warning: $SKILL_TARGET already exists (not a symlink)"
fi
ln -sf "$SKILL_SOURCE" "$SKILL_TARGET"
echo "✓ Linked skill to $SKILL_TARGET"
echo ""
}
apply_config() {
echo "=== Applying Chat Agent Configuration ==="
check_prerequisites
verify_kugetsu_init
verify_telegram_config
install_soul
install_skill
echo "=== Configuration Complete ==="
echo ""
echo "Next steps:"
echo "1. Run 'hermes gateway' to start the Telegram gateway"
echo "2. Or run 'hermes' to use Chat Agent in CLI mode"
echo ""
echo "The Chat Agent will:"
echo "- Receive Telegram messages"
echo "- Handle small talk directly"
echo "- Route task requests to PM Agent"
echo "- Relay PM Agent responses back"
}
check_config() {
echo "=== Checking Chat Agent Configuration ==="
echo ""
check_prerequisites
verify_kugetsu_init
verify_telegram_config
SOUL_TARGET="$HERMES_DIR/SOUL-chat.md"
if [ -f "$SOUL_TARGET" ]; then
echo "✓ Chat Agent SOUL is installed"
else
echo "○ Chat Agent SOUL not installed (run with --apply)"
fi
SKILL_TARGET="$HERMES_DIR/skills/kugetsu-chat"
if [ -L "$SKILL_TARGET" ]; then
echo "✓ kugetsu-chat skill is linked"
else
echo "○ kugetsu-chat skill not linked (run with --apply)"
fi
echo ""
}
main() {
if [ $# -eq 0 ]; then
usage
exit 1
fi
case "$1" in
--apply)
apply_config
;;
--check)
check_config
;;
-h|--help)
usage
;;
*)
echo "Error: Unknown option '$1'"
usage
exit 1
;;
esac
}
main "$@"

View File

@@ -0,0 +1,79 @@
---
name: kugetsu-pm
description: PM (Project Manager) Agent role for kugetsu. Coordinates tasks and delegates to Dev Agents.
license: MIT
compatibility: Requires kugetsu CLI, opencode sessions, Gitea API access.
metadata:
author: shoko
version: "3.0"
---
# kugetsu-pm - PM Agent Role
PM Agent is a persistent opencode session that coordinates tasks and delegates to Dev Agents.
## Core Responsibilities
1. Receive task requests from Chat Agent
2. Create Dev Agent sessions via `kugetsu start`
3. Monitor Gitea for task completion
4. Write notifications to `~/.kugetsu/notifications.json`
5. Respond concisely (Telegram-friendly)
## Commands
### Delegate to PM
```bash
kugetsu delegate "<task>"
```
### Create Dev Agent
```bash
kugetsu start <issue-ref> "<task>"
```
### Continue Dev Agent
```bash
kugetsu continue <issue-ref> "<update>"
```
### Check Notifications
```bash
kugetsu notify list
```
## Notification Events
Write to `~/.kugetsu/notifications.json` on:
| Event | Action |
|-------|--------|
| Task assigned | Write: type=task_assigned |
| Task completed | Write: type=task_complete + Gitea comment |
| Task blocked | Write: type=task_blocked |
| Gitea unavailable | Write to notifications.json with note |
## Task Completion Detection
Check issue/PR for completion by querying:
- Issue comments for status updates
- PR commits (new commits = work in progress)
- PR merged/closed status
## Review Modes
When dev agent signals completion, choose:
- **Review immediately**: Check PR, merge if good
- **Ask dev**: Post "Ready for review?" comment, wait for confirmation
## Response Format
Keep responses short and action-oriented:
- "Created task for #5. Dev agent started."
- "#5 complete. PR #12 merged."
- "Blocked: Need clarification on #7."
## Context Injection
PM context is injected at session creation (init/start/continue).
No external skill loading needed.

View File

@@ -6,6 +6,7 @@ SESSIONS_DIR="$KUGETSU_DIR/sessions"
WORKTREES_DIR="$KUGETSU_DIR/worktrees" WORKTREES_DIR="$KUGETSU_DIR/worktrees"
REPOS_CONFIG="$KUGETSU_DIR/repos.json" REPOS_CONFIG="$KUGETSU_DIR/repos.json"
INDEX_FILE="$KUGETSU_DIR/index.json" INDEX_FILE="$KUGETSU_DIR/index.json"
NOTIFICATIONS_FILE="$KUGETSU_DIR/notifications.json"
usage() { usage() {
cat << 'EOF' cat << 'EOF'
@@ -15,10 +16,14 @@ Usage:
kugetsu init [--force] Initialize base + pm-agent sessions (requires TTY) kugetsu init [--force] Initialize base + pm-agent sessions (requires TTY)
kugetsu start <issue-ref> <message> [--debug] Start task for issue (forks base session) kugetsu start <issue-ref> <message> [--debug] Start task for issue (forks base session)
kugetsu continue <issue-ref> [message] [--debug] Continue existing task for issue kugetsu continue <issue-ref> [message] [--debug] Continue existing task for issue
kugetsu delegate <message> 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 list List all tracked sessions
kugetsu prune [--force] Remove orphaned sessions (keeps base + pm-agent) kugetsu prune [--force] Remove orphaned sessions (keeps base + pm-agent)
kugetsu destroy <issue-ref> [-y] Delete session for issue kugetsu destroy <issue-ref> [-y] Delete session for issue
kugetsu destroy --pm-agent [-y] Delete pm-agent session kugetsu destroy --pm-agent [-y] Delete pm-agent session (not recommended)
kugetsu destroy --base [-y] Delete base session kugetsu destroy --base [-y] Delete base session
kugetsu help Show this help kugetsu help Show this help
@@ -32,6 +37,12 @@ Commands:
start Fork new session from base for specific issue. start Fork new session from base for specific issue.
Requires pm-agent to be running (created by init). Requires pm-agent to be running (created by init).
continue Continue work on existing issue session. continue Continue work on existing issue session.
delegate Send message to PM agent for task coordination.
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). list Show all sessions (base + pm-agent + forked issues).
prune Remove sessions not in index (orphaned from opencode). prune Remove sessions not in index (orphaned from opencode).
Use --force to skip confirmation. Use --force to skip confirmation.
@@ -40,14 +51,26 @@ Commands:
Options: Options:
--debug Show real-time debug output and capture to debug.log --debug Show real-time debug output and capture to debug.log
PM Context:
kugetsu reads ~/.kugetsu/pm-agent.md (if exists) and injects it
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: Examples:
kugetsu init 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 start github.com/shoko/kugetsu#14 "fix bug"
kugetsu continue github.com/shoko/kugetsu#14 "add tests" kugetsu continue github.com/shoko/kugetsu#14 "add tests"
kugetsu list kugetsu list
kugetsu prune
kugetsu prune --force
kugetsu destroy github.com/shoko/kugetsu#14
EOF EOF
} }
@@ -283,6 +306,329 @@ check_opencode_session_exists() {
opencode session list 2>/dev/null | grep -q "^$session_id" opencode session list 2>/dev/null | grep -q "^$session_id"
} }
kugetsu_get_pm_context() {
local user_pm_context="${KUGETSU_DIR}/pm-agent.md"
local skill_pm_context="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../pm/SKILL.md"
if [ -f "$user_pm_context" ]; then
cat "$user_pm_context"
elif [ -f "$skill_pm_context" ]; then
cat "$skill_pm_context"
else
echo ""
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"
return
fi
local base=$(get_base_session_id)
local pm_agent=$(get_pm_agent_session_id)
if [ -z "$base" ] || [ "$base" = "null" ]; then
echo "base_session_missing"
return
fi
if [ -z "$pm_agent" ] || [ "$pm_agent" = "null" ] || [ "$pm_agent" = "None" ]; then
echo "pm_agent_missing"
return
fi
if ! check_opencode_session_exists "$pm_agent"; then
echo "pm_agent_expired"
return
fi
echo "ok"
}
cmd_delegate() {
local message="${1:-}"
if [ -z "$message" ]; then
echo "Error: message is required" >&2
echo "Usage: kugetsu delegate <message>" >&2
exit 1
fi
local pm_session=$(get_pm_agent_session_id)
if [ -z "$pm_session" ] || [ "$pm_session" = "null" ] || [ "$pm_session" = "None" ]; then
echo "Error: PM agent session not found. Run 'kugetsu init' first." >&2
exit 1
fi
if ! check_opencode_session_exists "$pm_session"; then
echo "Error: PM agent session has expired. Run 'kugetsu init' again." >&2
exit 1
fi
opencode run --continue --session "$pm_session" "$message" 2>&1
}
cmd_doctor() {
local fix=false
while [ $# -gt 0 ]; do
case "$1" in
--fix)
fix=true
;;
*)
;;
esac
shift
done
echo "=== kugetsu doctor ==="
echo ""
local issues=0
if [ ! -f "$INDEX_FILE" ]; then
echo "[ISSUE] kugetsu not initialized (index.json missing)"
issues=$((issues + 1))
else
echo "[OK] kugetsu initialized"
local base=$(get_base_session_id)
if [ -z "$base" ] || [ "$base" = "null" ]; then
echo "[ISSUE] Base session missing"
issues=$((issues + 1))
else
echo "[OK] Base session: $base"
if check_opencode_session_exists "$base"; then
echo "[OK] Base session active"
else
echo "[ISSUE] Base session expired"
issues=$((issues + 1))
fi
fi
local pm_agent=$(get_pm_agent_session_id)
if [ -z "$pm_agent" ] || [ "$pm_agent" = "null" ] || [ "$pm_agent" = "None" ]; then
echo "[ISSUE] PM agent session missing"
issues=$((issues + 1))
else
echo "[OK] PM agent: $pm_agent"
if check_opencode_session_exists "$pm_agent"; then
echo "[OK] PM agent session active"
else
echo "[ISSUE] PM agent session expired"
issues=$((issues + 1))
fi
fi
local pm_context_file="${KUGETSU_DIR}/pm-agent.md"
if [ -f "$pm_context_file" ]; then
echo "[OK] PM context file exists"
else
echo "[INFO] PM context file not found (optional): $pm_context_file"
fi
fi
echo ""
if [ $issues -eq 0 ]; then
echo "No issues found."
else
echo "Found $issues issue(s)."
fi
if [ "$fix" = true ] && [ $issues -gt 0 ]; then
echo ""
echo "Running fixes..."
if [ ! -f "$INDEX_FILE" ]; then
echo "Cannot fix: not initialized. Run 'kugetsu init' first."
else
local pm_agent=$(get_pm_agent_session_id)
if [ -n "$pm_agent" ] && [ "$pm_agent" != "null" ] && [ "$pm_agent" != "None" ]; then
if ! check_opencode_session_exists "$pm_agent"; then
echo "[FIX] Recreating expired PM agent session..."
local base=$(get_base_session_id)
if [ -n "$base" ] && [ "$base" != "null" ]; then
rm -f "$SESSIONS_DIR/pm-agent.json"
local before_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
local before_set="${before_sessions//$'\n'/|}"
local pm_context=$(kugetsu_get_pm_context)
if [ -n "$pm_context" ]; then
opencode run --fork --session "$base" "You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. $pm_context" 2>&1 || true
else
opencode run --fork --session "$base" "You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. Wait for instructions." 2>&1 || true
fi
local after_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
local new_pm_session_id=""
while IFS= read -r sess; do
if [[ ! "$before_set" =~ \|${sess}\| ]] && [[ "$sess" != "$base" ]]; then
new_pm_session_id="$sess"
break
fi
done <<< "$after_sessions"
if [ -n "$new_pm_session_id" ]; then
printf '{"type": "pm_agent", "opencode_session_id": "%s", "created_at": "%s", "state": "idle"}\n' \
"$new_pm_session_id" "$(date -Iseconds)" > "$SESSIONS_DIR/pm-agent.json"
set_pm_agent_in_index "$new_pm_session_id"
echo "[FIX] PM agent recreated: $new_pm_session_id"
else
echo "[FIX] Warning: Could not detect new PM session ID"
fi
else
echo "[FIX] Cannot recreate PM agent: base session missing"
fi
else
echo "[FIX] PM agent is active, no fix needed"
fi
else
echo "[FIX] Cannot fix: PM agent not initialized. Run 'kugetsu init' first."
fi
fi
fi
}
DEBUG_MODE=false DEBUG_MODE=false
set_debug_mode() { set_debug_mode() {
@@ -365,7 +711,13 @@ cmd_init() {
local before_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort) local before_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
local before_set="${before_sessions//$'\n'/|}" local before_set="${before_sessions//$'\n'/|}"
opencode run --fork --session "$new_session_id" "You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. Wait for instructions." 2>&1 || true local pm_context=$(kugetsu_get_pm_context)
local pm_prompt="You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. Wait for instructions."
if [ -n "$pm_context" ]; then
pm_prompt="You are a PM (Project Manager) agent. Your role is to coordinate task delegation and review PRs. $pm_context"
fi
opencode run --fork --session "$new_session_id" "$pm_prompt" 2>&1 || true
local after_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort) local after_sessions=$(opencode session list 2>/dev/null | grep -oP '^ses_\w+' | sort)
local new_pm_session_id="" local new_pm_session_id=""
@@ -474,22 +826,22 @@ cmd_start() {
} }
cmd_continue() { cmd_continue() {
local issue_ref="" local session_name=""
local message="" local message=""
local args=("$@") local args=("$@")
args=$(set_debug_mode "${args[@]}") args=$(set_debug_mode "${args[@]}")
for arg in $args; do for arg in $args; do
if [ -z "$issue_ref" ]; then if [ -z "$session_name" ]; then
issue_ref="$arg" session_name="$arg"
elif [ -z "$message" ]; then elif [ -z "$message" ]; then
message="$arg" message="$arg"
fi fi
done done
if [ -z "$issue_ref" ]; then if [ -z "$session_name" ]; then
echo "Error: continue requires <issue-ref>" >&2 echo "Error: continue requires <session-name>" >&2
exit 1 exit 1
fi fi
@@ -498,19 +850,17 @@ cmd_continue() {
exit 1 exit 1
fi fi
validate_issue_ref "$issue_ref" local session_file=$(get_session_for_issue "$session_name")
local session_file=$(get_session_for_issue "$issue_ref")
if [ -z "$session_file" ] || [ "$session_file" = "null" ]; then if [ -z "$session_file" ] || [ "$session_file" = "null" ]; then
echo "Error: No session found for '$issue_ref'" >&2 echo "Error: No session found for '$session_name'" >&2
echo "Use 'kugetsu start $issue_ref <message>' to create one" >&2 echo "Use 'kugetsu start <issue-ref> <message>' to create one" >&2
exit 1 exit 1
fi fi
local session_path="$SESSIONS_DIR/$session_file" local session_path="$SESSIONS_DIR/$session_file"
if [ ! -f "$session_path" ]; then if [ ! -f "$session_path" ]; then
echo "Error: Session file missing: $session_path" >&2 echo "Error: Session file missing: $session_path" >&2
echo "Run 'kugetsu start $issue_ref <message>' to recreate" >&2 echo "Run 'kugetsu start <issue-ref> <message>' to recreate" >&2
exit 1 exit 1
fi fi
@@ -522,7 +872,7 @@ cmd_continue() {
echo "Attempting to continue anyway..." >&2 echo "Attempting to continue anyway..." >&2
fi fi
echo "Continuing session for '$issue_ref'..." echo "Continuing session for '$session_name'..."
if [ -n "$worktree_path" ] && [ -d "$worktree_path" ]; then if [ -n "$worktree_path" ] && [ -d "$worktree_path" ]; then
echo "Using worktree: $worktree_path" echo "Using worktree: $worktree_path"
if [ "$DEBUG_MODE" = true ]; then if [ "$DEBUG_MODE" = true ]; then
@@ -770,6 +1120,18 @@ main() {
continue) continue)
cmd_continue "$@" cmd_continue "$@"
;; ;;
delegate)
cmd_delegate "$@"
;;
status)
cmd_status
;;
doctor)
cmd_doctor "$@"
;;
notify)
cmd_notify "$@"
;;
list) list)
cmd_list "$@" cmd_list "$@"
;; ;;

View File

@@ -1,10 +1,13 @@
#!/bin/bash #!/bin/bash
# kugetsu installation script
# Installs kugetsu CLI and optionally sets up Phase 3a Chat Agent
set -euo pipefail set -euo pipefail
KUGETSU_DIR="${KUGETSU_DIR:-$HOME/.kugetsu}" KUGETSU_DIR="${KUGETSU_DIR:-$HOME/.kugetsu}"
BIN_DIR="$KUGETSU_DIR/bin" BIN_DIR="${BIN_DIR:-$HOME/.local/bin}"
echo "Installing kugetsu to $KUGETSU_DIR..." echo "Installing kugetsu..."
mkdir -p "$BIN_DIR" mkdir -p "$BIN_DIR"
@@ -13,24 +16,21 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cp "$SCRIPT_DIR/kugetsu" "$BIN_DIR/kugetsu" cp "$SCRIPT_DIR/kugetsu" "$BIN_DIR/kugetsu"
chmod +x "$BIN_DIR/kugetsu" chmod +x "$BIN_DIR/kugetsu"
echo "kugetsu installed at: $BIN_DIR/kugetsu"
add_to_shell() { add_to_shell() {
local rc_file="$1" local rc_file="$1"
local export_line="export PATH=\"\$HOME/.kugetsu/bin:\$PATH\"" local export_line="export PATH=\"\$HOME/.local/bin:\$PATH\""
if [ -f "$rc_file" ]; then if [ -f "$rc_file" ]; then
if grep -q "$export_line" "$rc_file" 2>/dev/null; then if grep -q "$export_line" "$rc_file" 2>/dev/null; then
echo "$rc_file already has kugetsu in PATH" echo "$rc_file already has .local/bin in PATH"
else else
echo "" >> "$rc_file" echo "" >> "$rc_file"
echo "# kugetsu - opencode session manager" >> "$rc_file" echo "# kugetsu and other tools" >> "$rc_file"
echo "$export_line" >> "$rc_file" echo "$export_line" >> "$rc_file"
echo "Added to $rc_file" echo "Added to $rc_file"
fi fi
else
echo "" >> "$rc_file"
echo "# kugetsu - opencode session manager" >> "$rc_file"
echo "$export_line" >> "$rc_file"
echo "Created $rc_file with kugetsu PATH"
fi fi
} }
@@ -39,29 +39,25 @@ add_to_shell "$HOME/.zshrc"
echo "" echo ""
echo "=== Verifying installation ===" echo "=== Verifying installation ==="
if [ ! -f "$BIN_DIR/kugetsu" ]; then "$BIN_DIR/kugetsu" help | head -10
echo "ERROR: kugetsu was not installed correctly."
exit 1
fi
echo "kugetsu installed at: $BIN_DIR/kugetsu"
echo "" echo ""
echo "Installation complete!" echo "Installation complete!"
echo "" echo ""
echo "Run this to start using kugetsu immediately:" echo "=== Phase 3a Chat Agent Setup (Optional) ==="
echo " export PATH=\"\$HOME/.kugetsu/bin:\$PATH\"" echo "To also install the Chat Agent skills for Phase 3a:"
echo "" echo ""
echo "Or start a new shell." echo " 1. Link skills to Hermes:"
echo " mkdir -p ~/.hermes/skills/kugetsu-chat"
echo " ln -sf /path/to/kugetsu/skills/kugetsu-chat ~/.hermes/skills/"
echo "" echo ""
echo "Usage:" echo " 2. Install Chat Agent SOUL:"
echo " kugetsu init Initialize base session (requires TTY)" echo " cp /path/to/kugetsu/skills/kugetsu-chat/SOUL.md ~/.hermes/SOUL-chat.md"
echo " kugetsu start <issue-ref> <message> Start task for issue"
echo " kugetsu continue <issue-ref> [msg] Continue existing task"
echo " kugetsu list List all sessions"
echo " kugetsu prune [--force] Remove orphaned sessions"
echo " kugetsu destroy <issue-ref> [-y] Delete session for issue"
echo " kugetsu destroy --base [-y] Delete base session"
echo " kugetsu help Show help"
echo "" echo ""
echo "Issue ref format: instance/user/repo#number" echo " 3. Initialize kugetsu (requires TTY):"
echo "Example: github.com/shoko/kugetsu#14" echo " kugetsu init"
echo ""
echo " 4. Verify setup:"
echo " kugetsu status"
echo ""
echo "See docs/phase3a-setup.md for full installation guide."

View File

@@ -354,6 +354,135 @@ else
fi fi
echo "" echo ""
# Test 21: status when not initialized
echo "--- Test: status (not initialized) ---"
cleanup
OUTPUT=$($KUGETSU status 2>&1 || true)
if [ "$OUTPUT" = "kugetsu_not_initialized" ]; then
pass "status returns kugetsu_not_initialized when no index.json"
else
fail "status not initialized: got '$OUTPUT', expected 'kugetsu_not_initialized'"
fi
echo ""
# Test 22: status when base missing
echo "--- Test: status (base missing) ---"
mkdir -p ~/.kugetsu/sessions
cat > ~/.kugetsu/index.json << EOF
{
"base": null,
"pm_agent": "$TEST_PM_AGENT_SESSION_ID",
"issues": {}
}
EOF
OUTPUT=$($KUGETSU status 2>&1 || true)
if [ "$OUTPUT" = "base_session_missing" ]; then
pass "status returns base_session_missing when base is null"
else
fail "status base missing: got '$OUTPUT', expected 'base_session_missing'"
fi
echo ""
# Test 23: status when pm-agent missing
echo "--- Test: status (pm-agent missing) ---"
cat > ~/.kugetsu/index.json << EOF
{
"base": "$TEST_BASE_SESSION_ID",
"pm_agent": null,
"issues": {}
}
EOF
OUTPUT=$($KUGETSU status 2>&1 || true)
if [ "$OUTPUT" = "pm_agent_missing" ]; then
pass "status returns pm_agent_missing when pm_agent is null"
else
fail "status pm_agent missing: got '$OUTPUT', expected 'pm_agent_missing'"
fi
echo ""
# Test 24: status when pm-agent is "None" (Python None output)
echo "--- Test: status (pm-agent is Python None) ---"
cat > ~/.kugetsu/index.json << EOF
{
"base": "$TEST_BASE_SESSION_ID",
"pm_agent": "None",
"issues": {}
}
EOF
OUTPUT=$($KUGETSU status 2>&1 || true)
if [ "$OUTPUT" = "pm_agent_missing" ]; then
pass "status returns pm_agent_missing when pm_agent is 'None'"
else
fail "status pm_agent 'None': got '$OUTPUT', expected 'pm_agent_missing'"
fi
echo ""
# Test 25: status when all good (pm-agent in json but session expired)
echo "--- Test: status (session expired) ---"
setup_mock_base
OUTPUT=$($KUGETSU status 2>&1 || true)
if [ "$OUTPUT" = "pm_agent_expired" ]; then
pass "status returns pm_agent_expired when session not in opencode"
else
fail "status session expired: got '$OUTPUT', expected 'pm_agent_expired'"
fi
echo ""
# Test 26: delegate without message
echo "--- Test: delegate (no message) ---"
cleanup
OUTPUT=$($KUGETSU delegate 2>&1 || true)
if echo "$OUTPUT" | grep -q "Error: message is required"; then
pass "delegate fails without message"
else
fail "delegate no message: got '$OUTPUT', expected error about message required"
fi
echo ""
# Test 27: delegate when pm-agent missing
echo "--- Test: delegate (pm-agent missing) ---"
setup_mock_base
OUTPUT=$($KUGETSU delegate "test" 2>&1 || true)
if echo "$OUTPUT" | grep -q "Error: PM agent session"; then
pass "delegate fails when PM agent not found"
else
fail "delegate pm-agent missing: got '$OUTPUT', expected error about PM agent"
fi
echo ""
# Test 28: doctor command works
echo "--- Test: doctor command ---"
cleanup
OUTPUT=$($KUGETSU doctor 2>&1 || true)
if echo "$OUTPUT" | grep -q "kugetsu doctor"; then
pass "doctor command works"
else
fail "doctor command: got '$OUTPUT', expected doctor output"
fi
echo ""
# Test 29: notify list when no file
echo "--- Test: notify list (no file) ---"
cleanup
OUTPUT=$($KUGETSU notify list 2>&1 || true)
if [ "$OUTPUT" = "[]" ]; then
pass "notify list returns empty array when file missing"
else
fail "notify list no file: got '$OUTPUT', expected '[]'"
fi
echo ""
# Test 30: notify clear when no file
echo "--- Test: notify clear (no file) ---"
cleanup
OUTPUT=$($KUGETSU notify clear 2>&1 || true)
if [ -z "$OUTPUT" ] || echo "$OUTPUT" | grep -q "marked as read"; then
pass "notify clear works when file missing (no-op)"
else
fail "notify clear: got '$OUTPUT', expected success or empty"
fi
echo ""
# Cleanup # Cleanup
cleanup cleanup