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
This commit is contained in:
@@ -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 <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 prune [--force] Remove orphaned sessions (keeps base + pm-agent)
|
||||
kugetsu destroy <issue-ref> [-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 "$@"
|
||||
;;
|
||||
|
||||
Reference in New Issue
Block a user