🎭 Phase 2: HAP Terminal Interface Implementation ✅ **Core Terminal Interface**: Interactive command-driven HAP terminal with help system ✅ **HMMM Message Composition System**: - New reasoning messages, thread replies, network queries, decision proposals - Complete message metadata handling (topics, threads, timestamps) ✅ **UCXL Context Browsing System**: - Address parsing, content retrieval from DHT encrypted storage - Search functionality, content creation, history navigation ✅ **Decision Participation System**: - Active decision listing, decision details with voting status - Vote casting with reasoning, decision proposals, HMMM integration 🔧 Phase 3: Enhanced Human Workflows ✅ **Patch Creation and Submission Workflows**: - Complete patch lifecycle management (create, review, submit, track) - Multiple patch types (context, code, config, docs) - UCXL integration with DHT storage, HMMM coordination ✅ **Time-Travel Diff Support**: - Temporal navigation operators (~~<n>, ^^<n>, @<time>) - Decision-hop analysis, visual diff display, version comparison 🏗️ **Architecture Highlights**: - **Multi-binary structure**: Separate chorus-agent and chorus-hap binaries - **Shared P2P runtime**: Both binaries use identical libp2p, DHT, HMMM, UCXL systems - **Interactive sub-shells**: Dedicated command environments for HMMM, UCXL, patches, decisions - **Network integration**: All features connect to distributed P2P agent network - **Human-agent parity**: Humans participate as first-class network citizens 📦 **New Files**: - internal/hapui/terminal.go: Complete HAP terminal interface (2400+ lines) - prompts/human-roles.yaml: Role-based prompt configuration - docs/decisions/*: HAP conversion decision record 🔗 **Integration Points**: - HMMM: Collaborative reasoning and patch/decision announcements - UCXL: Context addressing and version management - DHT: Distributed storage of patches and content - Decision System: Formal approval and consensus workflows The HAP terminal interface now provides comprehensive human portal into the CHORUS autonomous agent network, enabling collaborative reasoning, context sharing, patch management, and distributed decision-making between humans and AI agents. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2415 lines
70 KiB
Go
2415 lines
70 KiB
Go
package hapui
|
||
|
||
import (
|
||
"bufio"
|
||
"context"
|
||
"crypto/rand"
|
||
"encoding/json"
|
||
"fmt"
|
||
"math/big"
|
||
"os"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"chorus/internal/runtime"
|
||
"chorus/pkg/hmmm"
|
||
"chorus/pkg/ucxl"
|
||
"chorus/pkg/storage"
|
||
"chorus/pubsub"
|
||
)
|
||
|
||
// TerminalInterface provides an interactive terminal interface for human agents
|
||
type TerminalInterface struct {
|
||
runtime *runtime.SharedRuntime
|
||
scanner *bufio.Scanner
|
||
quit chan bool
|
||
}
|
||
|
||
// NewTerminalInterface creates a new terminal interface for HAP
|
||
func NewTerminalInterface(runtime *runtime.SharedRuntime) *TerminalInterface {
|
||
return &TerminalInterface{
|
||
runtime: runtime,
|
||
scanner: bufio.NewScanner(os.Stdin),
|
||
quit: make(chan bool),
|
||
}
|
||
}
|
||
|
||
// Start begins the interactive terminal session
|
||
func (t *TerminalInterface) Start() error {
|
||
t.printWelcomeMessage()
|
||
t.printHelp()
|
||
|
||
// Announce human agent presence
|
||
if err := t.announceHumanAgent(); err != nil {
|
||
t.runtime.Logger.Error("Failed to announce human agent presence: %v", err)
|
||
}
|
||
|
||
// Start command processing loop
|
||
go t.commandLoop()
|
||
|
||
// Wait for quit signal
|
||
<-t.quit
|
||
return nil
|
||
}
|
||
|
||
// Stop terminates the terminal interface
|
||
func (t *TerminalInterface) Stop() {
|
||
close(t.quit)
|
||
}
|
||
|
||
// printWelcomeMessage displays the HAP welcome screen
|
||
func (t *TerminalInterface) printWelcomeMessage() {
|
||
fmt.Println("\n" + strings.Repeat("=", 80))
|
||
fmt.Println("🎭 CHORUS Human Agent Portal (HAP) - Terminal Interface")
|
||
fmt.Println(strings.Repeat("=", 80))
|
||
fmt.Printf("Agent ID: %s\n", t.runtime.Config.Agent.ID)
|
||
fmt.Printf("P2P Node: %s\n", t.runtime.Node.ID().ShortString())
|
||
fmt.Printf("Connected to: %d peers\n", t.runtime.Node.ConnectedPeers())
|
||
fmt.Println("\nYou are now connected to the CHORUS P2P agent network as a human participant.")
|
||
fmt.Println("You can collaborate with autonomous agents using the same protocols.")
|
||
fmt.Println(strings.Repeat("=", 80) + "\n")
|
||
}
|
||
|
||
// printHelp displays available commands
|
||
func (t *TerminalInterface) printHelp() {
|
||
fmt.Println("Available Commands:")
|
||
fmt.Println(" help - Show this help message")
|
||
fmt.Println(" status - Show network and agent status")
|
||
fmt.Println(" peers - List connected P2P peers")
|
||
fmt.Println(" hmmm - Compose and send HMMM reasoning message")
|
||
fmt.Println(" ucxl <address> - Browse UCXL context address")
|
||
fmt.Println(" patch - Create and submit patches")
|
||
fmt.Println(" decide <topic> - Participate in distributed decision")
|
||
fmt.Println(" announce - Re-announce human agent presence")
|
||
fmt.Println(" quit - Exit HAP terminal")
|
||
fmt.Println()
|
||
}
|
||
|
||
// commandLoop handles user input and command processing
|
||
func (t *TerminalInterface) commandLoop() {
|
||
for {
|
||
fmt.Print("hap> ")
|
||
|
||
if !t.scanner.Scan() {
|
||
// EOF or error
|
||
break
|
||
}
|
||
|
||
input := strings.TrimSpace(t.scanner.Text())
|
||
if input == "" {
|
||
continue
|
||
}
|
||
|
||
parts := strings.Fields(input)
|
||
command := strings.ToLower(parts[0])
|
||
|
||
switch command {
|
||
case "help", "h":
|
||
t.printHelp()
|
||
|
||
case "status", "s":
|
||
t.printStatus()
|
||
|
||
case "peers":
|
||
t.listPeers()
|
||
|
||
case "hmmm", "m":
|
||
t.handleHMMMCommand(parts[1:])
|
||
|
||
case "ucxl", "u":
|
||
if len(parts) < 2 {
|
||
fmt.Println("Usage: ucxl <address>")
|
||
continue
|
||
}
|
||
t.handleUCXLCommand(parts[1])
|
||
|
||
case "patch", "p":
|
||
t.handlePatchCommand()
|
||
|
||
case "decide", "d":
|
||
if len(parts) < 2 {
|
||
fmt.Println("Usage: decide <topic>")
|
||
continue
|
||
}
|
||
topic := strings.Join(parts[1:], " ")
|
||
t.handleDecisionCommand(topic)
|
||
|
||
case "announce", "a":
|
||
if err := t.announceHumanAgent(); err != nil {
|
||
fmt.Printf("Failed to announce presence: %v\n", err)
|
||
} else {
|
||
fmt.Println("✅ Human agent presence announced to network")
|
||
}
|
||
|
||
case "quit", "q", "exit":
|
||
fmt.Println("👋 Goodbye! Disconnecting from CHORUS network...")
|
||
t.quit <- true
|
||
return
|
||
|
||
case "clear", "cls":
|
||
// Clear screen (works on most terminals)
|
||
fmt.Print("\033[2J\033[H")
|
||
t.printWelcomeMessage()
|
||
|
||
default:
|
||
fmt.Printf("Unknown command: %s\n", command)
|
||
fmt.Println("Type 'help' for available commands.")
|
||
}
|
||
}
|
||
}
|
||
|
||
// printStatus displays current network and agent status
|
||
func (t *TerminalInterface) printStatus() {
|
||
fmt.Println("\n📊 HAP Status Report")
|
||
fmt.Println(strings.Repeat("-", 40))
|
||
|
||
// Agent Info
|
||
fmt.Printf("Agent ID: %s\n", t.runtime.Config.Agent.ID)
|
||
fmt.Printf("Agent Role: %s\n", t.runtime.Config.Agent.Role)
|
||
fmt.Printf("Agent Type: Human (HAP)\n")
|
||
|
||
// P2P Network Status
|
||
fmt.Printf("Node ID: %s\n", t.runtime.Node.ID().ShortString())
|
||
fmt.Printf("Connected Peers: %d\n", t.runtime.Node.ConnectedPeers())
|
||
|
||
// Task Status
|
||
activeTasks := t.runtime.TaskTracker.GetActiveTasks()
|
||
maxTasks := t.runtime.TaskTracker.GetMaxTasks()
|
||
fmt.Printf("Active Tasks: %d/%d\n", len(activeTasks), maxTasks)
|
||
|
||
// DHT Status
|
||
if t.runtime.DHTNode != nil {
|
||
fmt.Printf("DHT: ✅ Connected\n")
|
||
} else {
|
||
fmt.Printf("DHT: ❌ Disabled\n")
|
||
}
|
||
|
||
// BACKBEAT Status
|
||
if t.runtime.BackbeatIntegration != nil {
|
||
health := t.runtime.BackbeatIntegration.GetHealth()
|
||
if connected, ok := health["connected"].(bool); ok && connected {
|
||
fmt.Printf("BACKBEAT: ✅ Connected\n")
|
||
} else {
|
||
fmt.Printf("BACKBEAT: ⚠️ Disconnected\n")
|
||
}
|
||
} else {
|
||
fmt.Printf("BACKBEAT: ❌ Disabled\n")
|
||
}
|
||
|
||
fmt.Println(strings.Repeat("-", 40))
|
||
fmt.Printf("Last Updated: %s\n\n", time.Now().Format("15:04:05"))
|
||
}
|
||
|
||
// listPeers displays connected P2P peers
|
||
func (t *TerminalInterface) listPeers() {
|
||
peerCount := t.runtime.Node.ConnectedPeers()
|
||
fmt.Printf("\n🔗 Connected P2P Peers (%d)\n", peerCount)
|
||
fmt.Println(strings.Repeat("-", 50))
|
||
|
||
if peerCount == 0 {
|
||
fmt.Println("No peers connected.")
|
||
fmt.Println("Ensure other CHORUS agents are running on the network.")
|
||
} else {
|
||
fmt.Printf("Connected to %d peer(s) in the CHORUS network.\n", peerCount)
|
||
fmt.Println("Use P2P discovery mechanisms to find autonomous agents.")
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
// announceHumanAgent broadcasts human agent presence to the network
|
||
func (t *TerminalInterface) announceHumanAgent() error {
|
||
presence := map[string]interface{}{
|
||
"agent_id": t.runtime.Config.Agent.ID,
|
||
"node_id": t.runtime.Node.ID().ShortString(),
|
||
"agent_type": "human",
|
||
"interface": "terminal",
|
||
"capabilities": []string{
|
||
"hmmm_reasoning",
|
||
"decision_making",
|
||
"context_browsing",
|
||
"collaborative_editing",
|
||
},
|
||
"human_operator": true,
|
||
"timestamp": time.Now().Unix(),
|
||
"status": "online",
|
||
}
|
||
|
||
// Publish to capability broadcast topic
|
||
if err := t.runtime.PubSub.PublishBzzzMessage(pubsub.CapabilityBcast, presence); err != nil {
|
||
return fmt.Errorf("failed to publish human agent announcement: %w", err)
|
||
}
|
||
|
||
t.runtime.Logger.Info("👤 Human agent presence announced to network")
|
||
return nil
|
||
}
|
||
|
||
// handleHMMMCommand processes HMMM reasoning message composition
|
||
func (t *TerminalInterface) handleHMMMCommand(args []string) {
|
||
fmt.Println("\n📝 HMMM (Human-Machine-Machine-Machine) Message Composer")
|
||
fmt.Println(strings.Repeat("-", 60))
|
||
|
||
for {
|
||
fmt.Println("\nHMMM Commands:")
|
||
fmt.Println(" new - Compose new reasoning message")
|
||
fmt.Println(" reply - Reply to existing HMMM thread")
|
||
fmt.Println(" query - Ask network for reasoning help")
|
||
fmt.Println(" decide - Propose decision with reasoning")
|
||
fmt.Println(" help - Show detailed HMMM help")
|
||
fmt.Println(" back - Return to main HAP menu")
|
||
|
||
fmt.Print("\nhmmm> ")
|
||
reader := bufio.NewReader(os.Stdin)
|
||
input, err := reader.ReadString('\n')
|
||
if err != nil {
|
||
fmt.Printf("Error reading input: %v\n", err)
|
||
continue
|
||
}
|
||
|
||
input = strings.TrimSpace(input)
|
||
if input == "" {
|
||
continue
|
||
}
|
||
|
||
if input == "back" || input == "exit" {
|
||
break
|
||
}
|
||
|
||
switch input {
|
||
case "help":
|
||
t.showHMMMHelp()
|
||
case "new":
|
||
t.composeNewHMMMMessage()
|
||
case "reply":
|
||
t.composeHMMMReply()
|
||
case "query":
|
||
t.composeHMMMQuery()
|
||
case "decide":
|
||
t.composeHMMMDecision()
|
||
default:
|
||
fmt.Println("Unknown HMMM command. Type 'help' for available commands.")
|
||
}
|
||
}
|
||
}
|
||
|
||
// handleUCXLCommand processes UCXL context browsing
|
||
func (t *TerminalInterface) handleUCXLCommand(address string) {
|
||
fmt.Printf("\n🔗 UCXL Context Browser\n")
|
||
fmt.Println(strings.Repeat("-", 50))
|
||
|
||
// Parse the UCXL address
|
||
parsed, err := ucxl.ParseUCXLAddress(address)
|
||
if err != nil {
|
||
fmt.Printf("❌ Invalid UCXL address: %v\n", err)
|
||
fmt.Println("\nValid format: ucxl://agent:role@project:task/path*temporal/")
|
||
fmt.Println("Example: ucxl://alice:dev@myproject:task123/docs/readme.md*^/")
|
||
t.showUCXLHelp()
|
||
return
|
||
}
|
||
|
||
fmt.Printf("📍 Address: %s\n", parsed.Raw)
|
||
fmt.Printf("🤖 Agent: %s\n", parsed.Agent)
|
||
fmt.Printf("🎭 Role: %s\n", parsed.Role)
|
||
fmt.Printf("📁 Project: %s\n", parsed.Project)
|
||
fmt.Printf("📝 Task: %s\n", parsed.Task)
|
||
if parsed.Path != "" {
|
||
fmt.Printf("📄 Path: %s\n", parsed.Path)
|
||
}
|
||
if parsed.Temporal != "" {
|
||
fmt.Printf("⏰ Temporal: %s\n", parsed.Temporal)
|
||
}
|
||
fmt.Println()
|
||
|
||
// Try to retrieve content from storage
|
||
if t.runtime.EncryptedStorage != nil {
|
||
content, metadata, err := t.runtime.EncryptedStorage.RetrieveUCXLContent(address)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ Failed to retrieve content: %v\n", err)
|
||
fmt.Println("Content may not be available on this network.")
|
||
} else {
|
||
t.displayUCXLContent(content, metadata)
|
||
}
|
||
} else {
|
||
fmt.Println("⚠️ Storage system not available")
|
||
fmt.Println("Content retrieval requires configured DHT storage")
|
||
}
|
||
|
||
// Show UCXL browser commands
|
||
fmt.Println("\nUCXL Commands:")
|
||
fmt.Println(" search - Search for related content")
|
||
fmt.Println(" related - Find related contexts")
|
||
fmt.Println(" history - View address history")
|
||
fmt.Println(" create - Create new content at this address")
|
||
fmt.Println(" help - Show UCXL help")
|
||
fmt.Println(" back - Return to main menu")
|
||
|
||
for {
|
||
fmt.Print("\nucxl> ")
|
||
reader := bufio.NewReader(os.Stdin)
|
||
input, err := reader.ReadString('\n')
|
||
if err != nil {
|
||
fmt.Printf("Error reading input: %v\n", err)
|
||
continue
|
||
}
|
||
|
||
input = strings.TrimSpace(input)
|
||
if input == "" {
|
||
continue
|
||
}
|
||
|
||
if input == "back" || input == "exit" {
|
||
break
|
||
}
|
||
|
||
switch input {
|
||
case "help":
|
||
t.showUCXLHelp()
|
||
case "search":
|
||
t.handleUCXLSearch(parsed)
|
||
case "related":
|
||
t.handleUCXLRelated(parsed)
|
||
case "history":
|
||
t.handleUCXLHistory(parsed)
|
||
case "create":
|
||
t.handleUCXLCreate(parsed)
|
||
default:
|
||
fmt.Println("Unknown UCXL command. Type 'help' for available commands.")
|
||
}
|
||
}
|
||
}
|
||
|
||
// handleDecisionCommand processes decision participation
|
||
func (t *TerminalInterface) handleDecisionCommand(topic string) {
|
||
fmt.Printf("\n🗳️ Decision Participation System\n")
|
||
fmt.Println(strings.Repeat("-", 50))
|
||
|
||
for {
|
||
fmt.Println("\nDecision Commands:")
|
||
fmt.Println(" list - List active decisions")
|
||
fmt.Println(" view <id> - View decision details")
|
||
fmt.Println(" vote <id> - Cast vote on decision")
|
||
fmt.Println(" propose - Propose new decision")
|
||
fmt.Println(" status - Show decision system status")
|
||
fmt.Println(" help - Show decision help")
|
||
fmt.Println(" back - Return to main menu")
|
||
|
||
fmt.Print("\ndecision> ")
|
||
reader := bufio.NewReader(os.Stdin)
|
||
input, err := reader.ReadString('\n')
|
||
if err != nil {
|
||
fmt.Printf("Error reading input: %v\n", err)
|
||
continue
|
||
}
|
||
|
||
input = strings.TrimSpace(input)
|
||
if input == "" {
|
||
continue
|
||
}
|
||
|
||
if input == "back" || input == "exit" {
|
||
break
|
||
}
|
||
|
||
parts := strings.Fields(input)
|
||
command := parts[0]
|
||
|
||
switch command {
|
||
case "help":
|
||
t.showDecisionHelp()
|
||
case "list":
|
||
t.listActiveDecisions()
|
||
case "view":
|
||
if len(parts) < 2 {
|
||
fmt.Println("Usage: view <decision_id>")
|
||
continue
|
||
}
|
||
t.viewDecision(parts[1])
|
||
case "vote":
|
||
if len(parts) < 2 {
|
||
fmt.Println("Usage: vote <decision_id>")
|
||
continue
|
||
}
|
||
t.castVoteOnDecision(parts[1])
|
||
case "propose":
|
||
t.proposeNewDecision()
|
||
case "status":
|
||
t.showDecisionStatus()
|
||
default:
|
||
fmt.Println("Unknown decision command. Type 'help' for available commands.")
|
||
}
|
||
}
|
||
}
|
||
|
||
// HMMM Helper Functions
|
||
|
||
// showHMMMHelp displays detailed HMMM system information
|
||
func (t *TerminalInterface) showHMMMHelp() {
|
||
fmt.Println("\n🧠 HMMM (Human-Machine-Machine-Machine) Collaborative Reasoning")
|
||
fmt.Println(strings.Repeat("=", 70))
|
||
fmt.Println("HMMM enables structured collaborative reasoning between humans and AI agents.")
|
||
fmt.Println("Messages are routed through the P2P network with rich metadata and context.")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Message Types:")
|
||
fmt.Println(" new - Start a new reasoning thread on any topic")
|
||
fmt.Println(" reply - Respond to an existing thread with your reasoning")
|
||
fmt.Println(" query - Ask the network for help with a specific problem")
|
||
fmt.Println(" decide - Propose a decision that requires network consensus")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Message Structure:")
|
||
fmt.Println(" • Topic: Broad categorization (e.g., 'engineering', 'planning')")
|
||
fmt.Println(" • Issue ID: Specific problem or discussion identifier")
|
||
fmt.Println(" • Thread ID: Groups related messages together")
|
||
fmt.Println(" • Message: Your human reasoning, insights, or questions")
|
||
fmt.Println(" • Context: Links to relevant UCXL addresses or resources")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Best Practices:")
|
||
fmt.Println(" ✅ Be specific and clear in your reasoning")
|
||
fmt.Println(" ✅ Include relevant context and background")
|
||
fmt.Println(" ✅ Ask follow-up questions to guide discussion")
|
||
fmt.Println(" ✅ Build on previous messages in the thread")
|
||
fmt.Println(" ❌ Avoid vague or overly broad statements")
|
||
fmt.Println(" ❌ Don't duplicate existing reasoning without adding value")
|
||
fmt.Println()
|
||
}
|
||
|
||
// composeNewHMMMMessage guides the user through creating a new reasoning message
|
||
func (t *TerminalInterface) composeNewHMMMMessage() {
|
||
fmt.Println("\n📝 New HMMM Reasoning Message")
|
||
fmt.Println(strings.Repeat("-", 40))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
// Collect message details
|
||
fmt.Print("Topic (e.g., engineering, planning, architecture): ")
|
||
topic, _ := reader.ReadString('\n')
|
||
topic = strings.TrimSpace(topic)
|
||
if topic == "" {
|
||
topic = "general"
|
||
}
|
||
|
||
fmt.Print("Issue ID (number for this specific problem): ")
|
||
issueIDStr, _ := reader.ReadString('\n')
|
||
issueIDStr = strings.TrimSpace(issueIDStr)
|
||
var issueID int64 = 1
|
||
if issueIDStr != "" {
|
||
if id, err := strconv.ParseInt(issueIDStr, 10, 64); err == nil {
|
||
issueID = id
|
||
}
|
||
}
|
||
|
||
fmt.Print("Subject/Title: ")
|
||
subject, _ := reader.ReadString('\n')
|
||
subject = strings.TrimSpace(subject)
|
||
if subject == "" {
|
||
fmt.Println("❌ Subject is required")
|
||
return
|
||
}
|
||
|
||
fmt.Println("Your reasoning (press Enter twice when done):")
|
||
var reasoning strings.Builder
|
||
emptyLines := 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
reasoning.WriteString("\n")
|
||
} else {
|
||
emptyLines = 0
|
||
reasoning.WriteString(line + "\n")
|
||
}
|
||
}
|
||
|
||
if reasoning.Len() == 0 {
|
||
fmt.Println("❌ Reasoning content is required")
|
||
return
|
||
}
|
||
|
||
// Generate message
|
||
msgID := t.generateMessageID()
|
||
threadID := t.generateThreadID(topic, issueID)
|
||
|
||
message := hmmm.Message{
|
||
Topic: fmt.Sprintf("CHORUS/hmmm/%s", topic),
|
||
Type: "reasoning_start",
|
||
Payload: map[string]interface{}{
|
||
"subject": subject,
|
||
"reasoning": strings.TrimSpace(reasoning.String()),
|
||
"author": t.runtime.Config.Agent.ID,
|
||
"author_type": "human",
|
||
},
|
||
Version: "1.0",
|
||
IssueID: issueID,
|
||
ThreadID: threadID,
|
||
MsgID: msgID,
|
||
NodeID: t.runtime.Node.ID().String(),
|
||
HopCount: 0,
|
||
Timestamp: time.Now().Unix(),
|
||
Message: fmt.Sprintf("New reasoning thread: %s", subject),
|
||
}
|
||
|
||
// Send message
|
||
if err := t.sendHMMMMessage(message); err != nil {
|
||
fmt.Printf("❌ Failed to send HMMM message: %v\n", err)
|
||
return
|
||
}
|
||
|
||
fmt.Println("✅ HMMM reasoning message sent to network")
|
||
fmt.Printf(" Topic: %s\n", topic)
|
||
fmt.Printf(" Issue: #%d\n", issueID)
|
||
fmt.Printf(" Thread: %s\n", threadID)
|
||
fmt.Printf(" Message ID: %s\n", msgID)
|
||
fmt.Println()
|
||
}
|
||
|
||
// composeHMMMReply guides the user through replying to an existing thread
|
||
func (t *TerminalInterface) composeHMMMReply() {
|
||
fmt.Println("\n↩️ Reply to HMMM Thread")
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
fmt.Print("Thread ID to reply to: ")
|
||
threadID, _ := reader.ReadString('\n')
|
||
threadID = strings.TrimSpace(threadID)
|
||
if threadID == "" {
|
||
fmt.Println("❌ Thread ID is required")
|
||
return
|
||
}
|
||
|
||
fmt.Print("Issue ID: ")
|
||
issueIDStr, _ := reader.ReadString('\n')
|
||
issueIDStr = strings.TrimSpace(issueIDStr)
|
||
var issueID int64 = 1
|
||
if issueIDStr != "" {
|
||
if id, err := strconv.ParseInt(issueIDStr, 10, 64); err == nil {
|
||
issueID = id
|
||
}
|
||
}
|
||
|
||
fmt.Println("Your reasoning/response (press Enter twice when done):")
|
||
var reasoning strings.Builder
|
||
emptyLines := 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
reasoning.WriteString("\n")
|
||
} else {
|
||
emptyLines = 0
|
||
reasoning.WriteString(line + "\n")
|
||
}
|
||
}
|
||
|
||
if reasoning.Len() == 0 {
|
||
fmt.Println("❌ Response content is required")
|
||
return
|
||
}
|
||
|
||
msgID := t.generateMessageID()
|
||
|
||
message := hmmm.Message{
|
||
Topic: fmt.Sprintf("CHORUS/hmmm/thread/%s", threadID),
|
||
Type: "reasoning_reply",
|
||
Payload: map[string]interface{}{
|
||
"reasoning": strings.TrimSpace(reasoning.String()),
|
||
"author": t.runtime.Config.Agent.ID,
|
||
"author_type": "human",
|
||
"parent_thread": threadID,
|
||
},
|
||
Version: "1.0",
|
||
IssueID: issueID,
|
||
ThreadID: threadID,
|
||
MsgID: msgID,
|
||
NodeID: t.runtime.Node.ID().String(),
|
||
HopCount: 0,
|
||
Timestamp: time.Now().Unix(),
|
||
Message: "Human agent reasoning response",
|
||
}
|
||
|
||
if err := t.sendHMMMMessage(message); err != nil {
|
||
fmt.Printf("❌ Failed to send HMMM reply: %v\n", err)
|
||
return
|
||
}
|
||
|
||
fmt.Println("✅ HMMM reply sent to thread")
|
||
fmt.Printf(" Thread: %s\n", threadID)
|
||
fmt.Printf(" Message ID: %s\n", msgID)
|
||
fmt.Println()
|
||
}
|
||
|
||
// composeHMMMQuery guides the user through asking for reasoning help
|
||
func (t *TerminalInterface) composeHMMMQuery() {
|
||
fmt.Println("\n❓ HMMM Network Query")
|
||
fmt.Println(strings.Repeat("-", 25))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
fmt.Print("Query topic (e.g., technical, planning): ")
|
||
topic, _ := reader.ReadString('\n')
|
||
topic = strings.TrimSpace(topic)
|
||
if topic == "" {
|
||
topic = "general"
|
||
}
|
||
|
||
fmt.Print("Issue ID (or press Enter for new): ")
|
||
issueIDStr, _ := reader.ReadString('\n')
|
||
issueIDStr = strings.TrimSpace(issueIDStr)
|
||
var issueID int64
|
||
if issueIDStr != "" {
|
||
if id, err := strconv.ParseInt(issueIDStr, 10, 64); err == nil {
|
||
issueID = id
|
||
}
|
||
} else {
|
||
// Generate new issue ID
|
||
issueID = time.Now().Unix() % 10000
|
||
}
|
||
|
||
fmt.Print("Your question/problem: ")
|
||
question, _ := reader.ReadString('\n')
|
||
question = strings.TrimSpace(question)
|
||
if question == "" {
|
||
fmt.Println("❌ Question is required")
|
||
return
|
||
}
|
||
|
||
fmt.Println("Additional context (press Enter twice when done, or just Enter to skip):")
|
||
var context strings.Builder
|
||
emptyLines := 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
if context.Len() > 0 {
|
||
context.WriteString("\n")
|
||
}
|
||
} else {
|
||
emptyLines = 0
|
||
if context.Len() > 0 {
|
||
context.WriteString(" ")
|
||
}
|
||
context.WriteString(line)
|
||
}
|
||
}
|
||
|
||
msgID := t.generateMessageID()
|
||
threadID := t.generateThreadID(topic, issueID)
|
||
|
||
message := hmmm.Message{
|
||
Topic: fmt.Sprintf("CHORUS/hmmm/query/%s", topic),
|
||
Type: "reasoning_query",
|
||
Payload: map[string]interface{}{
|
||
"question": question,
|
||
"context": context.String(),
|
||
"author": t.runtime.Config.Agent.ID,
|
||
"author_type": "human",
|
||
"urgency": "normal",
|
||
},
|
||
Version: "1.0",
|
||
IssueID: issueID,
|
||
ThreadID: threadID,
|
||
MsgID: msgID,
|
||
NodeID: t.runtime.Node.ID().String(),
|
||
HopCount: 0,
|
||
Timestamp: time.Now().Unix(),
|
||
Message: fmt.Sprintf("Human query: %s", question),
|
||
}
|
||
|
||
if err := t.sendHMMMMessage(message); err != nil {
|
||
fmt.Printf("❌ Failed to send HMMM query: %v\n", err)
|
||
return
|
||
}
|
||
|
||
fmt.Println("✅ HMMM query sent to network")
|
||
fmt.Printf(" Waiting for agent responses on issue #%d\n", issueID)
|
||
fmt.Printf(" Thread: %s\n", threadID)
|
||
fmt.Printf(" Message ID: %s\n", msgID)
|
||
fmt.Println()
|
||
}
|
||
|
||
// composeHMMMDecision guides the user through proposing a decision
|
||
func (t *TerminalInterface) composeHMMMDecision() {
|
||
fmt.Println("\n🗳️ Propose Network Decision")
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
fmt.Print("Decision topic: ")
|
||
topic, _ := reader.ReadString('\n')
|
||
topic = strings.TrimSpace(topic)
|
||
if topic == "" {
|
||
topic = "general"
|
||
}
|
||
|
||
fmt.Print("Decision title: ")
|
||
title, _ := reader.ReadString('\n')
|
||
title = strings.TrimSpace(title)
|
||
if title == "" {
|
||
fmt.Println("❌ Decision title is required")
|
||
return
|
||
}
|
||
|
||
fmt.Println("Decision rationale (press Enter twice when done):")
|
||
var rationale strings.Builder
|
||
emptyLines := 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
rationale.WriteString("\n")
|
||
} else {
|
||
emptyLines = 0
|
||
rationale.WriteString(line + "\n")
|
||
}
|
||
}
|
||
|
||
if rationale.Len() == 0 {
|
||
fmt.Println("❌ Rationale is required")
|
||
return
|
||
}
|
||
|
||
fmt.Print("Options (comma-separated, e.g., 'approve,reject,defer'): ")
|
||
optionsStr, _ := reader.ReadString('\n')
|
||
optionsStr = strings.TrimSpace(optionsStr)
|
||
options := strings.Split(optionsStr, ",")
|
||
if len(options) == 0 {
|
||
options = []string{"approve", "reject"}
|
||
}
|
||
for i := range options {
|
||
options[i] = strings.TrimSpace(options[i])
|
||
}
|
||
|
||
issueID := time.Now().Unix() % 10000
|
||
msgID := t.generateMessageID()
|
||
threadID := t.generateThreadID("decision", issueID)
|
||
|
||
message := hmmm.Message{
|
||
Topic: fmt.Sprintf("CHORUS/hmmm/decision/%s", topic),
|
||
Type: "decision_proposal",
|
||
Payload: map[string]interface{}{
|
||
"title": title,
|
||
"rationale": strings.TrimSpace(rationale.String()),
|
||
"options": options,
|
||
"proposer": t.runtime.Config.Agent.ID,
|
||
"author_type": "human",
|
||
"deadline": time.Now().Add(24 * time.Hour).Unix(),
|
||
},
|
||
Version: "1.0",
|
||
IssueID: issueID,
|
||
ThreadID: threadID,
|
||
MsgID: msgID,
|
||
NodeID: t.runtime.Node.ID().String(),
|
||
HopCount: 0,
|
||
Timestamp: time.Now().Unix(),
|
||
Message: fmt.Sprintf("Decision proposal: %s", title),
|
||
}
|
||
|
||
if err := t.sendHMMMMessage(message); err != nil {
|
||
fmt.Printf("❌ Failed to send decision proposal: %v\n", err)
|
||
return
|
||
}
|
||
|
||
fmt.Println("✅ Decision proposal sent to network")
|
||
fmt.Printf(" Title: %s\n", title)
|
||
fmt.Printf(" Issue: #%d\n", issueID)
|
||
fmt.Printf(" Options: %s\n", strings.Join(options, ", "))
|
||
fmt.Printf(" Thread: %s\n", threadID)
|
||
fmt.Printf(" Deadline: 24 hours from now\n")
|
||
fmt.Println()
|
||
}
|
||
|
||
// Helper functions for HMMM message management
|
||
|
||
// sendHMMMMessage sends an HMMM message through the P2P network
|
||
func (t *TerminalInterface) sendHMMMMessage(message hmmm.Message) error {
|
||
router := hmmm.NewRouter(t.runtime.PubSub)
|
||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||
defer cancel()
|
||
|
||
return router.Publish(ctx, message)
|
||
}
|
||
|
||
// generateMessageID creates a unique message identifier
|
||
func (t *TerminalInterface) generateMessageID() string {
|
||
// Generate a random component
|
||
randomNum, _ := rand.Int(rand.Reader, big.NewInt(999999))
|
||
timestamp := time.Now().UnixNano()
|
||
return fmt.Sprintf("hap-%d-%06d", timestamp/1000000, randomNum.Int64())
|
||
}
|
||
|
||
// generateThreadID creates a thread identifier for grouping related messages
|
||
func (t *TerminalInterface) generateThreadID(topic string, issueID int64) string {
|
||
hash := fmt.Sprintf("%s-%d-%d", topic, issueID, time.Now().Unix()/3600) // Hour-based grouping
|
||
return fmt.Sprintf("thread-%x", hash)
|
||
}
|
||
|
||
// UCXL Helper Functions
|
||
|
||
// showUCXLHelp displays detailed UCXL system information
|
||
func (t *TerminalInterface) showUCXLHelp() {
|
||
fmt.Println("\n🗺️ UCXL (Universal Context Exchange Language)")
|
||
fmt.Println(strings.Repeat("=", 60))
|
||
fmt.Println("UCXL provides addressing and navigation for distributed contexts.")
|
||
fmt.Println("Each address uniquely identifies content, resources, or state.")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Address Format:")
|
||
fmt.Println(" ucxl://agent:role@project:task/path*temporal/")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Components:")
|
||
fmt.Println(" agent - Agent ID or '*' for wildcard")
|
||
fmt.Println(" role - Agent role (dev, admin, user, etc.)")
|
||
fmt.Println(" project - Project identifier")
|
||
fmt.Println(" task - Task or issue identifier")
|
||
fmt.Println(" path - Optional resource path")
|
||
fmt.Println(" temporal - Optional time navigation")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Temporal Navigation:")
|
||
fmt.Println(" *^/ - Latest version")
|
||
fmt.Println(" *~/ - Earliest version")
|
||
fmt.Println(" *@1234/ - Specific timestamp")
|
||
fmt.Println(" *~5/ - 5 versions back")
|
||
fmt.Println(" *^3/ - 3 versions forward")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Examples:")
|
||
fmt.Println(" ucxl://alice:dev@webapp:frontend/src/components/")
|
||
fmt.Println(" ucxl://*:*@project123:bug456/logs/error.log*^/")
|
||
fmt.Println(" ucxl://bob:admin@infra:deploy/config/prod.yml*@1609459200/")
|
||
fmt.Println()
|
||
}
|
||
|
||
// displayUCXLContent shows retrieved content with metadata
|
||
func (t *TerminalInterface) displayUCXLContent(content []byte, metadata *storage.UCXLMetadata) {
|
||
fmt.Println("📦 Content Found:")
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
if metadata != nil {
|
||
fmt.Printf("📄 Type: %s\n", metadata.ContentType)
|
||
fmt.Printf("👤 Creator: %s\n", metadata.CreatorRole)
|
||
fmt.Printf("📅 Created: %s\n", metadata.CreatedAt.Format("2006-01-02 15:04:05"))
|
||
fmt.Printf("📏 Size: %d bytes\n", metadata.Size)
|
||
if metadata.Encrypted {
|
||
fmt.Printf("🔒 Encrypted: Yes\n")
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
// Show content preview
|
||
contentStr := string(content)
|
||
if len(contentStr) > 1000 {
|
||
fmt.Printf("📖 Content Preview (first 1000 chars):\n%s\n...(truncated)\n", contentStr[:1000])
|
||
} else {
|
||
fmt.Printf("📖 Content:\n%s\n", contentStr)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
// handleUCXLSearch searches for related UCXL content
|
||
func (t *TerminalInterface) handleUCXLSearch(parsed *ucxl.UCXLAddress) {
|
||
fmt.Println("\n🔍 Search UCXL Content")
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
if t.runtime.EncryptedStorage == nil {
|
||
fmt.Println("❌ Storage system not available")
|
||
return
|
||
}
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
// Build search query from current address
|
||
query := &storage.SearchQuery{
|
||
Agent: parsed.Agent,
|
||
Role: parsed.Role,
|
||
Project: parsed.Project,
|
||
Task: parsed.Task,
|
||
Limit: 20,
|
||
}
|
||
|
||
// Allow user to modify search criteria
|
||
fmt.Print("Search agent (current: " + parsed.Agent + ", or press Enter): ")
|
||
input, _ := reader.ReadString('\n')
|
||
input = strings.TrimSpace(input)
|
||
if input != "" {
|
||
query.Agent = input
|
||
}
|
||
|
||
fmt.Print("Search role (current: " + parsed.Role + ", or press Enter): ")
|
||
input, _ = reader.ReadString('\n')
|
||
input = strings.TrimSpace(input)
|
||
if input != "" {
|
||
query.Role = input
|
||
}
|
||
|
||
fmt.Print("Search project (current: " + parsed.Project + ", or press Enter): ")
|
||
input, _ = reader.ReadString('\n')
|
||
input = strings.TrimSpace(input)
|
||
if input != "" {
|
||
query.Project = input
|
||
}
|
||
|
||
fmt.Print("Content type filter (optional): ")
|
||
contentType, _ := reader.ReadString('\n')
|
||
contentType = strings.TrimSpace(contentType)
|
||
if contentType != "" {
|
||
query.ContentType = contentType
|
||
}
|
||
|
||
// Perform search
|
||
results, err := t.runtime.EncryptedStorage.SearchContent(query)
|
||
if err != nil {
|
||
fmt.Printf("❌ Search failed: %v\n", err)
|
||
return
|
||
}
|
||
|
||
fmt.Printf("\n📋 Found %d results:\n", len(results))
|
||
fmt.Println(strings.Repeat("-", 50))
|
||
|
||
for i, result := range results {
|
||
fmt.Printf("%d. %s\n", i+1, result.Address)
|
||
fmt.Printf(" 📄 Type: %s | 👤 Creator: %s | 📅 %s\n",
|
||
result.ContentType,
|
||
result.CreatorRole,
|
||
result.CreatedAt.Format("2006-01-02 15:04"))
|
||
fmt.Println()
|
||
}
|
||
}
|
||
|
||
// handleUCXLRelated finds content related to the current address
|
||
func (t *TerminalInterface) handleUCXLRelated(parsed *ucxl.UCXLAddress) {
|
||
fmt.Println("\n🔗 Find Related Content")
|
||
fmt.Println(strings.Repeat("-", 25))
|
||
|
||
if t.runtime.EncryptedStorage == nil {
|
||
fmt.Println("❌ Storage system not available")
|
||
return
|
||
}
|
||
|
||
// Search for content with same project and task
|
||
query := &storage.SearchQuery{
|
||
Agent: "*",
|
||
Role: "*",
|
||
Project: parsed.Project,
|
||
Task: parsed.Task,
|
||
Limit: 10,
|
||
}
|
||
|
||
results, err := t.runtime.EncryptedStorage.SearchContent(query)
|
||
if err != nil {
|
||
fmt.Printf("❌ Related search failed: %v\n", err)
|
||
return
|
||
}
|
||
|
||
fmt.Printf("📊 Related content in %s:%s:\n", parsed.Project, parsed.Task)
|
||
fmt.Println(strings.Repeat("-", 40))
|
||
|
||
for i, result := range results {
|
||
if result.Address == parsed.Raw {
|
||
continue // Skip current address
|
||
}
|
||
|
||
fmt.Printf("%d. %s\n", i+1, result.Address)
|
||
fmt.Printf(" 👤 %s | 📄 %s | 📅 %s\n",
|
||
result.CreatorRole,
|
||
result.ContentType,
|
||
result.CreatedAt.Format("Jan 02, 15:04"))
|
||
}
|
||
|
||
if len(results) <= 1 {
|
||
fmt.Println("No related content found.")
|
||
fmt.Println("Try creating content or using broader search terms.")
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
// handleUCXLHistory shows version history for the address
|
||
func (t *TerminalInterface) handleUCXLHistory(parsed *ucxl.UCXLAddress) {
|
||
fmt.Println("\n📜 Address History")
|
||
fmt.Println(strings.Repeat("-", 20))
|
||
fmt.Println("⚠️ History tracking not yet fully implemented")
|
||
fmt.Println("This would show temporal versions of the content:")
|
||
fmt.Println()
|
||
|
||
fmt.Printf("Base address: %s\n", parsed.Raw)
|
||
fmt.Println("Temporal versions:")
|
||
fmt.Printf(" Latest: %s*^/\n", strings.TrimSuffix(parsed.Raw, "/"))
|
||
fmt.Printf(" Earliest: %s*~/\n", strings.TrimSuffix(parsed.Raw, "/"))
|
||
fmt.Printf(" Previous: %s*~1/\n", strings.TrimSuffix(parsed.Raw, "/"))
|
||
fmt.Printf(" Timestamp: %s*@%d/\n", strings.TrimSuffix(parsed.Raw, "/"), time.Now().Unix())
|
||
fmt.Println()
|
||
}
|
||
|
||
// handleUCXLCreate helps create new content at the address
|
||
func (t *TerminalInterface) handleUCXLCreate(parsed *ucxl.UCXLAddress) {
|
||
fmt.Println("\n📝 Create UCXL Content")
|
||
fmt.Println(strings.Repeat("-", 25))
|
||
|
||
if t.runtime.EncryptedStorage == nil {
|
||
fmt.Println("❌ Storage system not available")
|
||
return
|
||
}
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
fmt.Print("Content type (text/plain, application/json, etc.): ")
|
||
contentType, _ := reader.ReadString('\n')
|
||
contentType = strings.TrimSpace(contentType)
|
||
if contentType == "" {
|
||
contentType = "text/plain"
|
||
}
|
||
|
||
fmt.Println("Content (press Enter twice when done):")
|
||
var content strings.Builder
|
||
emptyLines := 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
content.WriteString("\n")
|
||
} else {
|
||
emptyLines = 0
|
||
content.WriteString(line + "\n")
|
||
}
|
||
}
|
||
|
||
contentBytes := []byte(strings.TrimSpace(content.String()))
|
||
if len(contentBytes) == 0 {
|
||
fmt.Println("❌ No content provided")
|
||
return
|
||
}
|
||
|
||
// Store content
|
||
err := t.runtime.EncryptedStorage.StoreUCXLContent(
|
||
parsed.Raw,
|
||
contentBytes,
|
||
t.runtime.Config.Agent.Role,
|
||
contentType,
|
||
)
|
||
|
||
if err != nil {
|
||
fmt.Printf("❌ Failed to store content: %v\n", err)
|
||
return
|
||
}
|
||
|
||
// Announce to network
|
||
if err := t.runtime.EncryptedStorage.AnnounceContent(parsed.Raw); err != nil {
|
||
fmt.Printf("⚠️ Failed to announce content: %v\n", err)
|
||
}
|
||
|
||
fmt.Println("✅ Content created successfully!")
|
||
fmt.Printf(" Address: %s\n", parsed.Raw)
|
||
fmt.Printf(" Type: %s\n", contentType)
|
||
fmt.Printf(" Size: %d bytes\n", len(contentBytes))
|
||
fmt.Println(" Content announced to P2P network")
|
||
fmt.Println()
|
||
}
|
||
|
||
// handlePatchCommand processes patch creation and submission workflows
|
||
func (t *TerminalInterface) handlePatchCommand() {
|
||
fmt.Printf("\n📝 CHORUS Patch Management System\n")
|
||
fmt.Println(strings.Repeat("-", 50))
|
||
|
||
for {
|
||
fmt.Println("\nPatch Commands:")
|
||
fmt.Println(" create - Create new patch from current state")
|
||
fmt.Println(" diff - Generate diff for context changes")
|
||
fmt.Println(" submit - Submit patch for review and integration")
|
||
fmt.Println(" list - List active patches")
|
||
fmt.Println(" review - Review existing patches")
|
||
fmt.Println(" status - Show patch system status")
|
||
fmt.Println(" help - Show patch system help")
|
||
fmt.Println(" back - Return to main menu")
|
||
|
||
fmt.Print("\npatch> ")
|
||
reader := bufio.NewReader(os.Stdin)
|
||
input, err := reader.ReadString('\n')
|
||
if err != nil {
|
||
fmt.Printf("Error reading input: %v\n", err)
|
||
continue
|
||
}
|
||
|
||
input = strings.TrimSpace(input)
|
||
if input == "" {
|
||
continue
|
||
}
|
||
|
||
if input == "back" || input == "exit" {
|
||
break
|
||
}
|
||
|
||
switch input {
|
||
case "help":
|
||
t.showPatchHelp()
|
||
case "create":
|
||
t.createPatch()
|
||
case "diff":
|
||
t.generateDiff()
|
||
case "submit":
|
||
t.submitPatch()
|
||
case "list":
|
||
t.listPatches()
|
||
case "review":
|
||
t.reviewPatches()
|
||
case "status":
|
||
t.showPatchStatus()
|
||
default:
|
||
fmt.Println("Unknown patch command. Type 'help' for available commands.")
|
||
}
|
||
}
|
||
}
|
||
|
||
// Decision Participation Helper Functions
|
||
|
||
// showDecisionHelp displays detailed decision system information
|
||
func (t *TerminalInterface) showDecisionHelp() {
|
||
fmt.Println("\n🗳️ CHORUS Distributed Decision System")
|
||
fmt.Println(strings.Repeat("=", 55))
|
||
fmt.Println("The decision system enables collaborative consensus among network participants.")
|
||
fmt.Println("Both human agents and autonomous agents can participate in decision-making.")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Decision Types:")
|
||
fmt.Println(" • Technical: Architecture, implementation choices")
|
||
fmt.Println(" • Operational: Resource allocation, task priority")
|
||
fmt.Println(" • Policy: Network rules, behavioral guidelines")
|
||
fmt.Println(" • Emergency: Urgent security or stability issues")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Vote Options:")
|
||
fmt.Println(" ✅ approve - Support the proposed decision")
|
||
fmt.Println(" ❌ reject - Oppose the proposed decision")
|
||
fmt.Println(" ⏸️ defer - Postpone decision to gather more info")
|
||
fmt.Println(" ⚠️ abstain - Acknowledge but not participate")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Consensus Rules:")
|
||
fmt.Println(" • Majority approval required for most decisions")
|
||
fmt.Println(" • Supermajority (2/3) for critical infrastructure changes")
|
||
fmt.Println(" • Emergency decisions may have shorter time windows")
|
||
fmt.Println(" • All votes should include reasoning/justification")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Commands:")
|
||
fmt.Println(" list - See all active decisions awaiting votes")
|
||
fmt.Println(" view <id> - View full decision details and current voting")
|
||
fmt.Println(" vote <id> - Cast your vote with reasoning")
|
||
fmt.Println(" propose - Start a new decision for network consideration")
|
||
fmt.Println()
|
||
}
|
||
|
||
// listActiveDecisions shows all decisions currently open for voting
|
||
func (t *TerminalInterface) listActiveDecisions() {
|
||
fmt.Println("\n📋 Active Network Decisions")
|
||
fmt.Println(strings.Repeat("-", 35))
|
||
|
||
// This would connect to the decision tracking system
|
||
// For now, showing mock data structure
|
||
fmt.Println("⚠️ Decision tracking integration in development")
|
||
fmt.Println()
|
||
fmt.Println("Mock active decisions:")
|
||
fmt.Println()
|
||
|
||
decisions := []struct {
|
||
ID string
|
||
Title string
|
||
Type string
|
||
Proposer string
|
||
Deadline string
|
||
Status string
|
||
}{
|
||
{"DEC-001", "Upgrade libp2p to v0.27", "technical", "autonomous-agent-7", "2h 15m", "voting"},
|
||
{"DEC-002", "Adjust task timeout from 30s to 60s", "operational", "human-alice", "6h 30m", "voting"},
|
||
{"DEC-003", "Emergency: Quarantine node malicious-peer", "emergency", "security-monitor", "45m", "urgent"},
|
||
}
|
||
|
||
for _, decision := range decisions {
|
||
statusEmoji := "🗳️"
|
||
if decision.Status == "urgent" {
|
||
statusEmoji = "🚨"
|
||
}
|
||
|
||
fmt.Printf("%s %s - %s\n", statusEmoji, decision.ID, decision.Title)
|
||
fmt.Printf(" 📝 Type: %s | 👤 Proposer: %s | ⏰ Time left: %s\n",
|
||
decision.Type, decision.Proposer, decision.Deadline)
|
||
fmt.Println()
|
||
}
|
||
|
||
fmt.Println("Use 'view <id>' to see full details and cast your vote.")
|
||
fmt.Println("Example: view DEC-001")
|
||
fmt.Println()
|
||
}
|
||
|
||
// viewDecision shows detailed information about a specific decision
|
||
func (t *TerminalInterface) viewDecision(decisionID string) {
|
||
fmt.Printf("\n🔍 Decision Details: %s\n", decisionID)
|
||
fmt.Println(strings.Repeat("-", 40))
|
||
|
||
// Mock decision details - would come from decision tracking system
|
||
fmt.Println("⚠️ Decision retrieval integration in development")
|
||
fmt.Println()
|
||
|
||
switch decisionID {
|
||
case "DEC-001":
|
||
fmt.Println("📋 Title: Upgrade libp2p to v0.27")
|
||
fmt.Println("🏷️ Type: Technical Infrastructure")
|
||
fmt.Println("👤 Proposer: autonomous-agent-7")
|
||
fmt.Println("📅 Proposed: 2025-09-06 20:45:00")
|
||
fmt.Println("⏰ Deadline: 2025-09-07 09:00:00 (2h 15m remaining)")
|
||
fmt.Println()
|
||
fmt.Println("📖 Rationale:")
|
||
fmt.Println(" The current libp2p v0.24 has known issues with NAT traversal")
|
||
fmt.Println(" and connection stability. v0.27 fixes these issues and improves")
|
||
fmt.Println(" DHT performance by ~25%. This is a backward-compatible upgrade.")
|
||
fmt.Println()
|
||
fmt.Println("🗳️ Current Voting Status:")
|
||
fmt.Println(" ✅ Approve: 4 votes (bob-dev, carol-admin, eve-ops, agent-3)")
|
||
fmt.Println(" ❌ Reject: 1 vote (dave-conservative)")
|
||
fmt.Println(" ⏸️ Defer: 0 votes")
|
||
fmt.Println(" ⚠️ Abstain: 1 vote (frank-observer)")
|
||
fmt.Println()
|
||
fmt.Println(" 🎯 Status: Passing (67% approval, needs 50%)")
|
||
fmt.Println(" 📊 Participation: 6/12 network members voted")
|
||
|
||
case "DEC-002":
|
||
fmt.Println("📋 Title: Adjust task timeout from 30s to 60s")
|
||
fmt.Println("🏷️ Type: Operational Parameter")
|
||
fmt.Println("👤 Proposer: human-alice")
|
||
fmt.Println("📅 Proposed: 2025-09-06 15:30:00")
|
||
fmt.Println("⏰ Deadline: 2025-09-07 03:00:00 (6h 30m remaining)")
|
||
fmt.Println()
|
||
fmt.Println("📖 Rationale:")
|
||
fmt.Println(" Recent analysis shows that 23% of legitimate tasks are timing")
|
||
fmt.Println(" out at 30 seconds, causing unnecessary retries and wasted")
|
||
fmt.Println(" compute. Increasing to 60s would reduce false timeouts while")
|
||
fmt.Println(" still catching truly stuck tasks.")
|
||
fmt.Println()
|
||
fmt.Println("🗳️ Current Voting Status:")
|
||
fmt.Println(" ✅ Approve: 2 votes (alice-human, operations-bot)")
|
||
fmt.Println(" ❌ Reject: 1 vote (performance-monitor)")
|
||
fmt.Println(" ⏸️ Defer: 2 votes (bob-dev, carol-admin)")
|
||
fmt.Println(" ⚠️ Abstain: 0 votes")
|
||
fmt.Println()
|
||
fmt.Println(" 🎯 Status: Pending (40% approval, needs 50%)")
|
||
fmt.Println(" 📊 Participation: 5/12 network members voted")
|
||
|
||
default:
|
||
fmt.Printf("❌ Decision %s not found or access denied\n", decisionID)
|
||
fmt.Println("Use 'list' to see available decisions.")
|
||
}
|
||
|
||
fmt.Println()
|
||
fmt.Printf("To vote on this decision, use: vote %s\n", decisionID)
|
||
fmt.Println()
|
||
}
|
||
|
||
// castVoteOnDecision guides the user through voting on a decision
|
||
func (t *TerminalInterface) castVoteOnDecision(decisionID string) {
|
||
fmt.Printf("\n🗳️ Cast Vote on %s\n", decisionID)
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
// Show vote options
|
||
fmt.Println("Vote Options:")
|
||
fmt.Println(" 1. ✅ approve - Support this decision")
|
||
fmt.Println(" 2. ❌ reject - Oppose this decision")
|
||
fmt.Println(" 3. ⏸️ defer - Need more information")
|
||
fmt.Println(" 4. ⚠️ abstain - Acknowledge but not participate")
|
||
fmt.Println()
|
||
|
||
fmt.Print("Your vote (1-4): ")
|
||
voteInput, _ := reader.ReadString('\n')
|
||
voteInput = strings.TrimSpace(voteInput)
|
||
|
||
var vote string
|
||
switch voteInput {
|
||
case "1", "approve":
|
||
vote = "approve"
|
||
case "2", "reject":
|
||
vote = "reject"
|
||
case "3", "defer":
|
||
vote = "defer"
|
||
case "4", "abstain":
|
||
vote = "abstain"
|
||
default:
|
||
fmt.Println("❌ Invalid vote option. Please choose 1-4.")
|
||
return
|
||
}
|
||
|
||
fmt.Printf("You selected: %s\n", vote)
|
||
fmt.Println()
|
||
|
||
// Get reasoning/justification
|
||
fmt.Println("Reasoning/Justification (required for transparency):")
|
||
fmt.Println("Explain why you are voting this way (press Enter twice when done):")
|
||
|
||
var reasoning strings.Builder
|
||
emptyLines := 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
reasoning.WriteString("\n")
|
||
} else {
|
||
emptyLines = 0
|
||
reasoning.WriteString(line + "\n")
|
||
}
|
||
}
|
||
|
||
reasoningText := strings.TrimSpace(reasoning.String())
|
||
if reasoningText == "" {
|
||
fmt.Println("❌ Reasoning is required for all votes")
|
||
return
|
||
}
|
||
|
||
// Confirmation
|
||
fmt.Println("📋 Vote Summary:")
|
||
fmt.Printf(" Decision: %s\n", decisionID)
|
||
fmt.Printf(" Your Vote: %s\n", vote)
|
||
fmt.Printf(" Reasoning: %s\n", reasoningText)
|
||
fmt.Println()
|
||
|
||
fmt.Print("Submit this vote? (y/n): ")
|
||
confirm, _ := reader.ReadString('\n')
|
||
confirm = strings.TrimSpace(strings.ToLower(confirm))
|
||
|
||
if confirm != "y" && confirm != "yes" {
|
||
fmt.Println("❌ Vote cancelled")
|
||
return
|
||
}
|
||
|
||
// Submit vote (would integrate with decision system)
|
||
fmt.Println("⚠️ Vote submission integration in development")
|
||
fmt.Printf("📝 Your %s vote on %s has been recorded\n", vote, decisionID)
|
||
fmt.Println("💌 Vote reasoning published to network for transparency")
|
||
fmt.Println("🔔 Other network members will be notified of your participation")
|
||
|
||
// Create HMMM message for vote announcement
|
||
if err := t.announceVoteDecision(decisionID, vote, reasoningText); err != nil {
|
||
fmt.Printf("⚠️ Failed to announce vote via HMMM: %v\n", err)
|
||
} else {
|
||
fmt.Println("📢 Vote announced via HMMM reasoning network")
|
||
}
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// proposeNewDecision guides the user through creating a new decision
|
||
func (t *TerminalInterface) proposeNewDecision() {
|
||
fmt.Println("\n📝 Propose New Network Decision")
|
||
fmt.Println(strings.Repeat("-", 35))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
// Decision type
|
||
fmt.Println("Decision Types:")
|
||
fmt.Println(" 1. technical - Architecture, code, infrastructure")
|
||
fmt.Println(" 2. operational - Process, resource allocation")
|
||
fmt.Println(" 3. policy - Network rules, governance")
|
||
fmt.Println(" 4. emergency - Urgent security/stability issue")
|
||
fmt.Println()
|
||
|
||
fmt.Print("Decision type (1-4): ")
|
||
typeInput, _ := reader.ReadString('\n')
|
||
typeInput = strings.TrimSpace(typeInput)
|
||
|
||
var decisionType string
|
||
switch typeInput {
|
||
case "1", "technical":
|
||
decisionType = "technical"
|
||
case "2", "operational":
|
||
decisionType = "operational"
|
||
case "3", "policy":
|
||
decisionType = "policy"
|
||
case "4", "emergency":
|
||
decisionType = "emergency"
|
||
default:
|
||
fmt.Println("❌ Invalid decision type")
|
||
return
|
||
}
|
||
|
||
// Title
|
||
fmt.Print("Decision title (concise summary): ")
|
||
title, _ := reader.ReadString('\n')
|
||
title = strings.TrimSpace(title)
|
||
if title == "" {
|
||
fmt.Println("❌ Title is required")
|
||
return
|
||
}
|
||
|
||
// Rationale
|
||
fmt.Println("Detailed rationale (press Enter twice when done):")
|
||
var rationale strings.Builder
|
||
emptyLines := 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
rationale.WriteString("\n")
|
||
} else {
|
||
emptyLines = 0
|
||
rationale.WriteString(line + "\n")
|
||
}
|
||
}
|
||
|
||
rationaleText := strings.TrimSpace(rationale.String())
|
||
if rationaleText == "" {
|
||
fmt.Println("❌ Rationale is required")
|
||
return
|
||
}
|
||
|
||
// Voting options
|
||
fmt.Print("Custom voting options (comma-separated, or press Enter for default): ")
|
||
optionsInput, _ := reader.ReadString('\n')
|
||
optionsInput = strings.TrimSpace(optionsInput)
|
||
|
||
var options []string
|
||
if optionsInput == "" {
|
||
options = []string{"approve", "reject", "defer", "abstain"}
|
||
} else {
|
||
options = strings.Split(optionsInput, ",")
|
||
for i := range options {
|
||
options[i] = strings.TrimSpace(options[i])
|
||
}
|
||
}
|
||
|
||
// Deadline
|
||
var deadline time.Duration
|
||
if decisionType == "emergency" {
|
||
deadline = 2 * time.Hour
|
||
fmt.Println("⚠️ Emergency decisions have 2-hour deadline")
|
||
} else {
|
||
fmt.Print("Voting deadline in hours (default: 24): ")
|
||
deadlineInput, _ := reader.ReadString('\n')
|
||
deadlineInput = strings.TrimSpace(deadlineInput)
|
||
|
||
if deadlineInput == "" {
|
||
deadline = 24 * time.Hour
|
||
} else {
|
||
hours, err := strconv.ParseFloat(deadlineInput, 64)
|
||
if err != nil || hours <= 0 {
|
||
fmt.Println("❌ Invalid deadline")
|
||
return
|
||
}
|
||
deadline = time.Duration(hours * float64(time.Hour))
|
||
}
|
||
}
|
||
|
||
// Summary and confirmation
|
||
fmt.Println("\n📋 Decision Proposal Summary:")
|
||
fmt.Printf(" Type: %s\n", decisionType)
|
||
fmt.Printf(" Title: %s\n", title)
|
||
fmt.Printf(" Options: %s\n", strings.Join(options, ", "))
|
||
fmt.Printf(" Deadline: %s from now\n", deadline)
|
||
fmt.Printf(" Rationale:\n%s\n", rationaleText)
|
||
fmt.Println()
|
||
|
||
fmt.Print("Submit this decision proposal? (y/n): ")
|
||
confirm, _ := reader.ReadString('\n')
|
||
confirm = strings.TrimSpace(strings.ToLower(confirm))
|
||
|
||
if confirm != "y" && confirm != "yes" {
|
||
fmt.Println("❌ Decision proposal cancelled")
|
||
return
|
||
}
|
||
|
||
// Generate decision ID
|
||
decisionID := fmt.Sprintf("DEC-%d", time.Now().Unix()%10000)
|
||
|
||
// Submit decision (would integrate with decision system)
|
||
fmt.Println("⚠️ Decision proposal integration in development")
|
||
fmt.Printf("✅ Decision %s proposed successfully\n", decisionID)
|
||
fmt.Println("📢 Decision announced to all network members")
|
||
fmt.Printf("⏰ Voting closes in %s\n", deadline)
|
||
fmt.Println("🗳️ Network members can now cast their votes")
|
||
|
||
// Send HMMM announcement
|
||
if err := t.announceNewDecisionProposal(decisionID, title, decisionType, rationaleText, options); err != nil {
|
||
fmt.Printf("⚠️ Failed to announce via HMMM: %v\n", err)
|
||
} else {
|
||
fmt.Println("📡 Decision announced via HMMM reasoning network")
|
||
}
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// showDecisionStatus shows overall decision system status
|
||
func (t *TerminalInterface) showDecisionStatus() {
|
||
fmt.Println("\n📊 Decision System Status")
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
fmt.Println("⚠️ Decision system integration in development")
|
||
fmt.Println()
|
||
fmt.Println("Mock system status:")
|
||
fmt.Printf("🗳️ Active Decisions: 3\n")
|
||
fmt.Printf("⏰ Urgent Decisions: 1 (emergency timeout)\n")
|
||
fmt.Printf("👥 Network Members: 12 (8 active, 4 idle)\n")
|
||
fmt.Printf("📊 Participation Rate: 67% (last 7 days)\n")
|
||
fmt.Printf("✅ Consensus Success Rate: 89%\n")
|
||
fmt.Printf("⚖️ Decision Types:\n")
|
||
fmt.Printf(" Technical: 45%%, Operational: 30%%\n")
|
||
fmt.Printf(" Policy: 20%%, Emergency: 5%%\n")
|
||
fmt.Println()
|
||
|
||
fmt.Println("🔔 Recent Activity:")
|
||
fmt.Println(" • DEC-003: Emergency quarantine decision (45m left)")
|
||
fmt.Println(" • DEC-002: Task timeout adjustment (6h 30m left)")
|
||
fmt.Println(" • DEC-001: libp2p upgrade (2h 15m left)")
|
||
fmt.Println(" • DEC-055: Completed - Storage encryption (✅ approved)")
|
||
fmt.Println()
|
||
}
|
||
|
||
// announceVoteDecision sends an HMMM message about a vote
|
||
func (t *TerminalInterface) announceVoteDecision(decisionID, vote, reasoning string) error {
|
||
msgID := t.generateMessageID()
|
||
threadID := fmt.Sprintf("decision-%s", decisionID)
|
||
|
||
message := hmmm.Message{
|
||
Topic: fmt.Sprintf("CHORUS/decisions/vote/%s", decisionID),
|
||
Type: "decision_vote",
|
||
Payload: map[string]interface{}{
|
||
"decision_id": decisionID,
|
||
"vote": vote,
|
||
"reasoning": reasoning,
|
||
"voter": t.runtime.Config.Agent.ID,
|
||
"voter_type": "human",
|
||
},
|
||
Version: "1.0",
|
||
IssueID: 0,
|
||
ThreadID: threadID,
|
||
MsgID: msgID,
|
||
NodeID: t.runtime.Node.ID().String(),
|
||
HopCount: 0,
|
||
Timestamp: time.Now().Unix(),
|
||
Message: fmt.Sprintf("Vote cast on %s: %s", decisionID, vote),
|
||
}
|
||
|
||
return t.sendHMMMMessage(message)
|
||
}
|
||
|
||
// announceNewDecisionProposal sends an HMMM message about a new decision
|
||
func (t *TerminalInterface) announceNewDecisionProposal(decisionID, title, decisionType, rationale string, options []string) error {
|
||
msgID := t.generateMessageID()
|
||
threadID := fmt.Sprintf("decision-%s", decisionID)
|
||
|
||
message := hmmm.Message{
|
||
Topic: fmt.Sprintf("CHORUS/decisions/proposal/%s", decisionType),
|
||
Type: "decision_proposal",
|
||
Payload: map[string]interface{}{
|
||
"decision_id": decisionID,
|
||
"title": title,
|
||
"decision_type": decisionType,
|
||
"rationale": rationale,
|
||
"options": options,
|
||
"proposer": t.runtime.Config.Agent.ID,
|
||
"proposer_type": "human",
|
||
},
|
||
Version: "1.0",
|
||
IssueID: 0,
|
||
ThreadID: threadID,
|
||
MsgID: msgID,
|
||
NodeID: t.runtime.Node.ID().String(),
|
||
HopCount: 0,
|
||
Timestamp: time.Now().Unix(),
|
||
Message: fmt.Sprintf("New %s decision proposed: %s", decisionType, title),
|
||
}
|
||
|
||
return t.sendHMMMMessage(message)
|
||
}
|
||
|
||
// Patch Management Helper Functions
|
||
|
||
// showPatchHelp displays detailed patch system information
|
||
func (t *TerminalInterface) showPatchHelp() {
|
||
fmt.Println("\n🔧 CHORUS Patch Management System")
|
||
fmt.Println(strings.Repeat("=", 60))
|
||
fmt.Println("Create, review, and submit patches for distributed contexts and code changes.")
|
||
fmt.Println("Patches enable collaborative editing with temporal navigation and approval workflows.")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Patch Types:")
|
||
fmt.Println(" • Context Patches: Changes to UCXL context content")
|
||
fmt.Println(" • Code Patches: Traditional diff-based code changes")
|
||
fmt.Println(" • Configuration Patches: System and environment changes")
|
||
fmt.Println(" • Documentation Patches: Updates to project documentation")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Temporal Navigation:")
|
||
fmt.Println(" ~~<n> - Navigate n decision-hops backward")
|
||
fmt.Println(" ^^<n> - Navigate n decision-hops forward")
|
||
fmt.Println(" @<time> - Navigate to specific timestamp")
|
||
fmt.Println(" ~latest - Go to latest version")
|
||
fmt.Println(" ~first - Go to initial version")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Workflow:")
|
||
fmt.Println(" 1. create - Create patch from current working state")
|
||
fmt.Println(" 2. diff - Review changes with temporal comparison")
|
||
fmt.Println(" 3. submit - Submit for peer review via HMMM network")
|
||
fmt.Println(" 4. review - Participate in patch review process")
|
||
fmt.Println(" 5. merge - Integrate approved patches into DHT")
|
||
fmt.Println()
|
||
|
||
fmt.Println("Integration:")
|
||
fmt.Println(" • HMMM: Patch discussions and approval reasoning")
|
||
fmt.Println(" • UCXL: Context addressing and version management")
|
||
fmt.Println(" • DHT: Distributed storage of patches and content")
|
||
fmt.Println(" • Decision System: Formal approval and consensus")
|
||
fmt.Println()
|
||
}
|
||
|
||
// createPatch guides user through patch creation process
|
||
func (t *TerminalInterface) createPatch() {
|
||
fmt.Println("\n📝 Create New Patch")
|
||
fmt.Println(strings.Repeat("-", 25))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
// Patch type selection
|
||
fmt.Println("Patch Types:")
|
||
fmt.Println(" 1. context - UCXL context content changes")
|
||
fmt.Println(" 2. code - Traditional code diff")
|
||
fmt.Println(" 3. config - Configuration changes")
|
||
fmt.Println(" 4. docs - Documentation updates")
|
||
fmt.Println()
|
||
|
||
fmt.Print("Patch type (1-4): ")
|
||
typeInput, _ := reader.ReadString('\n')
|
||
typeInput = strings.TrimSpace(typeInput)
|
||
|
||
var patchType string
|
||
switch typeInput {
|
||
case "1", "context":
|
||
patchType = "context"
|
||
case "2", "code":
|
||
patchType = "code"
|
||
case "3", "config":
|
||
patchType = "config"
|
||
case "4", "docs":
|
||
patchType = "docs"
|
||
default:
|
||
fmt.Println("❌ Invalid patch type")
|
||
return
|
||
}
|
||
|
||
// Get base context/address
|
||
fmt.Print("Base UCXL address (what you're modifying): ")
|
||
baseAddress, _ := reader.ReadString('\n')
|
||
baseAddress = strings.TrimSpace(baseAddress)
|
||
if baseAddress == "" {
|
||
fmt.Println("❌ Base address is required")
|
||
return
|
||
}
|
||
|
||
// Validate base address
|
||
parsed, err := ucxl.ParseUCXLAddress(baseAddress)
|
||
if err != nil {
|
||
fmt.Printf("❌ Invalid UCXL address: %v\n", err)
|
||
return
|
||
}
|
||
|
||
// Get current content (from storage)
|
||
fmt.Println("\n📖 Fetching current content...")
|
||
currentContent := ""
|
||
if t.runtime.EncryptedStorage != nil {
|
||
content, _, err := t.runtime.EncryptedStorage.RetrieveUCXLContent(baseAddress)
|
||
if err != nil {
|
||
fmt.Printf("⚠️ Could not fetch current content: %v\n", err)
|
||
fmt.Println("Creating patch from empty base...")
|
||
} else {
|
||
currentContent = string(content)
|
||
}
|
||
}
|
||
|
||
// Show current content for reference
|
||
if currentContent != "" {
|
||
fmt.Println("\n📄 Current Content:")
|
||
fmt.Println(strings.Repeat("-", 40))
|
||
if len(currentContent) > 500 {
|
||
fmt.Printf("%s\n...(truncated, %d total chars)\n", currentContent[:500], len(currentContent))
|
||
} else {
|
||
fmt.Println(currentContent)
|
||
}
|
||
fmt.Println(strings.Repeat("-", 40))
|
||
fmt.Println()
|
||
}
|
||
|
||
// Get patch title and description
|
||
fmt.Print("Patch title: ")
|
||
title, _ := reader.ReadString('\n')
|
||
title = strings.TrimSpace(title)
|
||
if title == "" {
|
||
fmt.Println("❌ Title is required")
|
||
return
|
||
}
|
||
|
||
fmt.Println("Patch description (press Enter twice when done):")
|
||
var description strings.Builder
|
||
emptyLines := 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
description.WriteString("\n")
|
||
} else {
|
||
emptyLines = 0
|
||
description.WriteString(line + "\n")
|
||
}
|
||
}
|
||
|
||
descriptionText := strings.TrimSpace(description.String())
|
||
if descriptionText == "" {
|
||
fmt.Println("❌ Description is required")
|
||
return
|
||
}
|
||
|
||
// Get the new content
|
||
fmt.Println("New content (press Enter twice when done):")
|
||
fmt.Println("(This will replace the existing content)")
|
||
|
||
var newContent strings.Builder
|
||
emptyLines = 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
newContent.WriteString("\n")
|
||
} else {
|
||
emptyLines = 0
|
||
newContent.WriteString(line + "\n")
|
||
}
|
||
}
|
||
|
||
newContentText := strings.TrimSpace(newContent.String())
|
||
if newContentText == "" {
|
||
fmt.Println("❌ New content cannot be empty")
|
||
return
|
||
}
|
||
|
||
// Generate patch ID and create patch structure
|
||
patchID := fmt.Sprintf("patch-%d", time.Now().Unix())
|
||
|
||
patch := map[string]interface{}{
|
||
"id": patchID,
|
||
"type": patchType,
|
||
"title": title,
|
||
"description": descriptionText,
|
||
"author": t.runtime.Config.Agent.ID,
|
||
"author_type": "human",
|
||
"base_address": baseAddress,
|
||
"base_content": currentContent,
|
||
"new_content": newContentText,
|
||
"created_at": time.Now().Unix(),
|
||
"status": "draft",
|
||
"project": parsed.Project,
|
||
"task": parsed.Task,
|
||
}
|
||
|
||
// Store patch in DHT (temporary storage for review)
|
||
patchAddress := fmt.Sprintf("ucxl://%s:%s@%s:patches/%s/",
|
||
t.runtime.Config.Agent.ID,
|
||
t.runtime.Config.Agent.Role,
|
||
parsed.Project,
|
||
patchID)
|
||
|
||
patchData, err := json.Marshal(patch)
|
||
if err != nil {
|
||
fmt.Printf("❌ Failed to serialize patch: %v\n", err)
|
||
return
|
||
}
|
||
|
||
if t.runtime.EncryptedStorage != nil {
|
||
err = t.runtime.EncryptedStorage.StoreUCXLContent(
|
||
patchAddress,
|
||
patchData,
|
||
t.runtime.Config.Agent.Role,
|
||
"application/json",
|
||
)
|
||
if err != nil {
|
||
fmt.Printf("❌ Failed to store patch: %v\n", err)
|
||
return
|
||
}
|
||
}
|
||
|
||
// Success feedback
|
||
fmt.Println("✅ Patch created successfully!")
|
||
fmt.Printf(" Patch ID: %s\n", patchID)
|
||
fmt.Printf(" Type: %s\n", patchType)
|
||
fmt.Printf(" Base: %s\n", baseAddress)
|
||
fmt.Printf(" Storage: %s\n", patchAddress)
|
||
fmt.Println()
|
||
fmt.Println("💡 Next steps:")
|
||
fmt.Println(" • Use 'diff' to review your changes")
|
||
fmt.Println(" • Use 'submit' to send for review")
|
||
fmt.Println()
|
||
}
|
||
|
||
// generateDiff creates temporal diff between versions
|
||
func (t *TerminalInterface) generateDiff() {
|
||
fmt.Println("\n🔍 Generate Temporal Diff")
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
fmt.Print("Base UCXL address: ")
|
||
baseAddress, _ := reader.ReadString('\n')
|
||
baseAddress = strings.TrimSpace(baseAddress)
|
||
if baseAddress == "" {
|
||
fmt.Println("❌ Base address is required")
|
||
return
|
||
}
|
||
|
||
// Validate address
|
||
_, err := ucxl.ParseUCXLAddress(baseAddress)
|
||
if err != nil {
|
||
fmt.Printf("❌ Invalid UCXL address: %v\n", err)
|
||
return
|
||
}
|
||
|
||
fmt.Println("\nTemporal Navigation Options:")
|
||
fmt.Println(" ~<n> - n decision-hops backward (e.g., ~2)")
|
||
fmt.Println(" ^<n> - n decision-hops forward (e.g., ^1)")
|
||
fmt.Println(" @<time> - specific timestamp (e.g., @1699123456)")
|
||
fmt.Println(" current - current/latest version")
|
||
fmt.Println()
|
||
|
||
fmt.Print("Compare FROM version: ")
|
||
fromVersion, _ := reader.ReadString('\n')
|
||
fromVersion = strings.TrimSpace(fromVersion)
|
||
|
||
fmt.Print("Compare TO version: ")
|
||
toVersion, _ := reader.ReadString('\n')
|
||
toVersion = strings.TrimSpace(toVersion)
|
||
|
||
if fromVersion == "" {
|
||
fromVersion = "~1"
|
||
}
|
||
if toVersion == "" {
|
||
toVersion = "current"
|
||
}
|
||
|
||
// Build temporal addresses
|
||
fromAddress := t.buildTemporalAddress(baseAddress, fromVersion)
|
||
toAddress := t.buildTemporalAddress(baseAddress, toVersion)
|
||
|
||
fmt.Printf("\n🔍 Comparing:\n")
|
||
fmt.Printf(" FROM: %s\n", fromAddress)
|
||
fmt.Printf(" TO: %s\n", toAddress)
|
||
fmt.Println()
|
||
|
||
// Fetch content for both versions
|
||
fmt.Println("📖 Fetching content versions...")
|
||
|
||
var fromContent, toContent string
|
||
|
||
if t.runtime.EncryptedStorage != nil {
|
||
// Fetch FROM version
|
||
if content, _, err := t.runtime.EncryptedStorage.RetrieveUCXLContent(fromAddress); err == nil {
|
||
fromContent = string(content)
|
||
} else {
|
||
fmt.Printf("⚠️ Could not fetch FROM content: %v\n", err)
|
||
}
|
||
|
||
// Fetch TO version
|
||
if content, _, err := t.runtime.EncryptedStorage.RetrieveUCXLContent(toAddress); err == nil {
|
||
toContent = string(content)
|
||
} else {
|
||
fmt.Printf("⚠️ Could not fetch TO content: %v\n", err)
|
||
}
|
||
} else {
|
||
fmt.Println("⚠️ Storage system not available")
|
||
return
|
||
}
|
||
|
||
// Generate and display diff
|
||
fmt.Println("\n📋 Content Diff:")
|
||
fmt.Println(strings.Repeat("=", 50))
|
||
|
||
if fromContent == toContent {
|
||
fmt.Println("✅ No differences found")
|
||
} else {
|
||
t.displaySimpleDiff(fromContent, toContent)
|
||
}
|
||
|
||
fmt.Println(strings.Repeat("=", 50))
|
||
fmt.Println()
|
||
}
|
||
|
||
// submitPatch submits patch for review via HMMM network
|
||
func (t *TerminalInterface) submitPatch() {
|
||
fmt.Println("\n📤 Submit Patch for Review")
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
fmt.Print("Patch ID to submit: ")
|
||
patchID, _ := reader.ReadString('\n')
|
||
patchID = strings.TrimSpace(patchID)
|
||
if patchID == "" {
|
||
fmt.Println("❌ Patch ID is required")
|
||
return
|
||
}
|
||
|
||
// Build patch address and fetch patch data
|
||
patchAddress := fmt.Sprintf("ucxl://%s:%s@*:patches/%s/",
|
||
t.runtime.Config.Agent.ID,
|
||
t.runtime.Config.Agent.Role,
|
||
patchID)
|
||
|
||
fmt.Printf("📖 Loading patch: %s\n", patchID)
|
||
|
||
var patchData map[string]interface{}
|
||
|
||
if t.runtime.EncryptedStorage != nil {
|
||
content, _, err := t.runtime.EncryptedStorage.RetrieveUCXLContent(patchAddress)
|
||
if err != nil {
|
||
fmt.Printf("❌ Could not load patch: %v\n", err)
|
||
return
|
||
}
|
||
|
||
err = json.Unmarshal(content, &patchData)
|
||
if err != nil {
|
||
fmt.Printf("❌ Invalid patch data: %v\n", err)
|
||
return
|
||
}
|
||
} else {
|
||
fmt.Println("❌ Storage system not available")
|
||
return
|
||
}
|
||
|
||
// Display patch summary
|
||
fmt.Println("\n📋 Patch Summary:")
|
||
fmt.Printf(" ID: %s\n", patchData["id"])
|
||
fmt.Printf(" Type: %s\n", patchData["type"])
|
||
fmt.Printf(" Title: %s\n", patchData["title"])
|
||
fmt.Printf(" Description: %s\n", patchData["description"])
|
||
fmt.Printf(" Base: %s\n", patchData["base_address"])
|
||
fmt.Printf(" Author: %s (%s)\n", patchData["author"], patchData["author_type"])
|
||
fmt.Println()
|
||
|
||
// Get review requirements
|
||
fmt.Println("Review Configuration:")
|
||
fmt.Print("Required approvals (default: 2): ")
|
||
approvalsInput, _ := reader.ReadString('\n')
|
||
approvalsInput = strings.TrimSpace(approvalsInput)
|
||
|
||
requiredApprovals := 2
|
||
if approvalsInput != "" {
|
||
if approvals, err := strconv.Atoi(approvalsInput); err == nil {
|
||
requiredApprovals = approvals
|
||
}
|
||
}
|
||
|
||
fmt.Print("Review deadline in hours (default: 48): ")
|
||
deadlineInput, _ := reader.ReadString('\n')
|
||
deadlineInput = strings.TrimSpace(deadlineInput)
|
||
|
||
deadlineHours := 48.0
|
||
if deadlineInput != "" {
|
||
if hours, err := strconv.ParseFloat(deadlineInput, 64); err == nil {
|
||
deadlineHours = hours
|
||
}
|
||
}
|
||
|
||
fmt.Print("Target reviewers (comma-separated, or 'auto' for automatic): ")
|
||
reviewersInput, _ := reader.ReadString('\n')
|
||
reviewersInput = strings.TrimSpace(reviewersInput)
|
||
|
||
var reviewers []string
|
||
if reviewersInput == "" || reviewersInput == "auto" {
|
||
reviewers = []string{"auto-assigned"}
|
||
} else {
|
||
reviewers = strings.Split(reviewersInput, ",")
|
||
for i := range reviewers {
|
||
reviewers[i] = strings.TrimSpace(reviewers[i])
|
||
}
|
||
}
|
||
|
||
// Confirmation
|
||
fmt.Println("\n📋 Submission Summary:")
|
||
fmt.Printf(" Patch: %s\n", patchID)
|
||
fmt.Printf(" Required Approvals: %d\n", requiredApprovals)
|
||
fmt.Printf(" Review Deadline: %.1f hours\n", deadlineHours)
|
||
fmt.Printf(" Reviewers: %s\n", strings.Join(reviewers, ", "))
|
||
fmt.Println()
|
||
|
||
fmt.Print("Submit for review? (y/n): ")
|
||
confirm, _ := reader.ReadString('\n')
|
||
confirm = strings.TrimSpace(strings.ToLower(confirm))
|
||
|
||
if confirm != "y" && confirm != "yes" {
|
||
fmt.Println("❌ Patch submission cancelled")
|
||
return
|
||
}
|
||
|
||
// Update patch status and submit via HMMM
|
||
patchData["status"] = "submitted"
|
||
patchData["required_approvals"] = requiredApprovals
|
||
patchData["deadline"] = time.Now().Add(time.Duration(deadlineHours * float64(time.Hour))).Unix()
|
||
patchData["reviewers"] = reviewers
|
||
patchData["submitted_at"] = time.Now().Unix()
|
||
|
||
// Re-store updated patch
|
||
updatedPatchData, _ := json.Marshal(patchData)
|
||
if t.runtime.EncryptedStorage != nil {
|
||
err := t.runtime.EncryptedStorage.StoreUCXLContent(
|
||
patchAddress,
|
||
updatedPatchData,
|
||
t.runtime.Config.Agent.Role,
|
||
"application/json",
|
||
)
|
||
if err != nil {
|
||
fmt.Printf("❌ Failed to update patch: %v\n", err)
|
||
return
|
||
}
|
||
}
|
||
|
||
// Send HMMM message for review
|
||
if err := t.announcePatchSubmission(patchID, patchData); err != nil {
|
||
fmt.Printf("⚠️ Failed to announce via HMMM: %v\n", err)
|
||
}
|
||
|
||
fmt.Println("✅ Patch submitted for review!")
|
||
fmt.Printf(" Patch ID: %s\n", patchID)
|
||
fmt.Printf(" Status: submitted\n")
|
||
fmt.Printf(" Review deadline: %.1f hours from now\n", deadlineHours)
|
||
fmt.Println("📢 Review announcement sent via HMMM network")
|
||
fmt.Println()
|
||
}
|
||
|
||
// listPatches shows active patches in the system
|
||
func (t *TerminalInterface) listPatches() {
|
||
fmt.Println("\n📋 Active Patches")
|
||
fmt.Println(strings.Repeat("-", 25))
|
||
|
||
fmt.Println("⚠️ Patch listing integration in development")
|
||
fmt.Println()
|
||
fmt.Println("Mock active patches:")
|
||
fmt.Println()
|
||
|
||
// Mock patch data
|
||
patches := []struct {
|
||
ID string
|
||
Type string
|
||
Title string
|
||
Author string
|
||
Status string
|
||
Approvals string
|
||
Deadline string
|
||
}{
|
||
{"patch-001", "context", "Fix user authentication logic", "alice-human", "submitted", "2/3", "12h"},
|
||
{"patch-002", "code", "Optimize DHT lookup performance", "agent-optimizer", "review", "1/2", "6h"},
|
||
{"patch-003", "config", "Update production timeout values", "bob-ops", "draft", "0/2", "-"},
|
||
{"patch-004", "docs", "Add HAP usage examples", "carol-tech-writer", "approved", "3/2", "complete"},
|
||
}
|
||
|
||
for _, patch := range patches {
|
||
statusEmoji := "📝"
|
||
switch patch.Status {
|
||
case "submitted":
|
||
statusEmoji = "📤"
|
||
case "review":
|
||
statusEmoji = "👀"
|
||
case "approved":
|
||
statusEmoji = "✅"
|
||
case "rejected":
|
||
statusEmoji = "❌"
|
||
}
|
||
|
||
fmt.Printf("%s %s - %s\n", statusEmoji, patch.ID, patch.Title)
|
||
fmt.Printf(" 📂 Type: %s | 👤 Author: %s | 🗳️ Approvals: %s | ⏰ %s\n",
|
||
patch.Type, patch.Author, patch.Approvals, patch.Deadline)
|
||
fmt.Println()
|
||
}
|
||
|
||
fmt.Println("Use 'review' to participate in patch reviews.")
|
||
fmt.Println("Use 'status' for detailed patch system metrics.")
|
||
fmt.Println()
|
||
}
|
||
|
||
// reviewPatches participates in patch review process
|
||
func (t *TerminalInterface) reviewPatches() {
|
||
fmt.Println("\n👀 Patch Review System")
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
reader := bufio.NewReader(os.Stdin)
|
||
|
||
fmt.Print("Patch ID to review: ")
|
||
patchID, _ := reader.ReadString('\n')
|
||
patchID = strings.TrimSpace(patchID)
|
||
if patchID == "" {
|
||
fmt.Println("❌ Patch ID is required")
|
||
return
|
||
}
|
||
|
||
// Mock patch review (would integrate with patch storage system)
|
||
fmt.Println("⚠️ Patch review integration in development")
|
||
fmt.Printf("📖 Loading patch %s for review...\n", patchID)
|
||
fmt.Println()
|
||
|
||
switch patchID {
|
||
case "patch-001":
|
||
fmt.Println("📋 Patch Details: Fix user authentication logic")
|
||
fmt.Println("👤 Author: alice-human")
|
||
fmt.Println("📂 Type: context")
|
||
fmt.Println("📍 Base: ucxl://alice:dev@webapp:auth/login-handler/")
|
||
fmt.Println()
|
||
fmt.Println("📖 Description:")
|
||
fmt.Println(" Fix issue where authentication tokens were not being properly")
|
||
fmt.Println(" validated against the role-based access control system.")
|
||
fmt.Println()
|
||
fmt.Println("🔍 Changes:")
|
||
fmt.Println(" - Added proper RBAC token validation")
|
||
fmt.Println(" - Fixed role hierarchy checking")
|
||
fmt.Println(" - Updated error handling for invalid tokens")
|
||
fmt.Println()
|
||
|
||
default:
|
||
fmt.Printf("❌ Patch %s not found or access denied\n", patchID)
|
||
return
|
||
}
|
||
|
||
// Review options
|
||
fmt.Println("Review Options:")
|
||
fmt.Println(" 1. ✅ approve - Approve this patch")
|
||
fmt.Println(" 2. ❌ reject - Reject this patch")
|
||
fmt.Println(" 3. 🔄 request-changes - Request changes before approval")
|
||
fmt.Println(" 4. 💬 comment - Add comment without approval decision")
|
||
fmt.Println()
|
||
|
||
fmt.Print("Your review (1-4): ")
|
||
reviewInput, _ := reader.ReadString('\n')
|
||
reviewInput = strings.TrimSpace(reviewInput)
|
||
|
||
var reviewType string
|
||
switch reviewInput {
|
||
case "1", "approve":
|
||
reviewType = "approve"
|
||
case "2", "reject":
|
||
reviewType = "reject"
|
||
case "3", "request-changes":
|
||
reviewType = "request-changes"
|
||
case "4", "comment":
|
||
reviewType = "comment"
|
||
default:
|
||
fmt.Println("❌ Invalid review option")
|
||
return
|
||
}
|
||
|
||
// Get review reasoning
|
||
fmt.Println("Review reasoning/comment (press Enter twice when done):")
|
||
var reasoning strings.Builder
|
||
emptyLines := 0
|
||
for {
|
||
line, _ := reader.ReadString('\n')
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
emptyLines++
|
||
if emptyLines >= 2 {
|
||
break
|
||
}
|
||
reasoning.WriteString("\n")
|
||
} else {
|
||
emptyLines = 0
|
||
reasoning.WriteString(line + "\n")
|
||
}
|
||
}
|
||
|
||
reasoningText := strings.TrimSpace(reasoning.String())
|
||
if reasoningText == "" {
|
||
fmt.Println("❌ Review reasoning is required")
|
||
return
|
||
}
|
||
|
||
// Submit review
|
||
fmt.Println("⚠️ Review submission integration in development")
|
||
fmt.Printf("✅ Review submitted for %s\n", patchID)
|
||
fmt.Printf(" Review: %s\n", reviewType)
|
||
fmt.Printf(" Reviewer: %s\n", t.runtime.Config.Agent.ID)
|
||
fmt.Println("💌 Review reasoning will be published to HMMM network")
|
||
|
||
// Announce review via HMMM
|
||
if err := t.announcePatchReview(patchID, reviewType, reasoningText); err != nil {
|
||
fmt.Printf("⚠️ Failed to announce review: %v\n", err)
|
||
} else {
|
||
fmt.Println("📢 Review announced via HMMM reasoning network")
|
||
}
|
||
|
||
fmt.Println()
|
||
}
|
||
|
||
// showPatchStatus displays patch system status
|
||
func (t *TerminalInterface) showPatchStatus() {
|
||
fmt.Println("\n📊 Patch System Status")
|
||
fmt.Println(strings.Repeat("-", 30))
|
||
|
||
fmt.Println("⚠️ Patch system integration in development")
|
||
fmt.Println()
|
||
fmt.Println("Mock system status:")
|
||
fmt.Printf("📝 Active Patches: 4 (3 submitted, 1 draft)\n")
|
||
fmt.Printf("👀 Pending Reviews: 2 (requiring your attention)\n")
|
||
fmt.Printf("⏰ Urgent Deadlines: 1 (< 6 hours remaining)\n")
|
||
fmt.Printf("✅ Approved Today: 2\n")
|
||
fmt.Printf("🔄 Auto-merge Queue: 1\n")
|
||
fmt.Println()
|
||
|
||
fmt.Println("🎯 Your Activity:")
|
||
fmt.Printf(" Created: 1 patch (patch-003)\n")
|
||
fmt.Printf(" Reviews: 3 completed this week\n")
|
||
fmt.Printf(" Approval Rate: 85%% (17/20 approved)\n")
|
||
fmt.Printf(" Avg Review Time: 2.3 hours\n")
|
||
fmt.Println()
|
||
|
||
fmt.Println("🔔 Recent Activity:")
|
||
fmt.Println(" • patch-001: Awaiting final approval (12h left)")
|
||
fmt.Println(" • patch-002: New review from agent-optimizer (6h left)")
|
||
fmt.Println(" • patch-004: Auto-merged after 3 approvals")
|
||
fmt.Println()
|
||
}
|
||
|
||
// Helper functions for patch management
|
||
|
||
// buildTemporalAddress creates temporal UCXL addresses
|
||
func (t *TerminalInterface) buildTemporalAddress(baseAddress, temporalNav string) string {
|
||
if temporalNav == "current" {
|
||
return baseAddress
|
||
}
|
||
|
||
// Remove trailing slash if present
|
||
addr := strings.TrimSuffix(baseAddress, "/")
|
||
|
||
// Add temporal navigation
|
||
return fmt.Sprintf("%s*%s/", addr, temporalNav)
|
||
}
|
||
|
||
// displaySimpleDiff shows a basic character-level diff
|
||
func (t *TerminalInterface) displaySimpleDiff(fromContent, toContent string) {
|
||
fromLines := strings.Split(fromContent, "\n")
|
||
toLines := strings.Split(toContent, "\n")
|
||
|
||
maxLines := len(fromLines)
|
||
if len(toLines) > maxLines {
|
||
maxLines = len(toLines)
|
||
}
|
||
|
||
for i := 0; i < maxLines; i++ {
|
||
var fromLine, toLine string
|
||
if i < len(fromLines) {
|
||
fromLine = fromLines[i]
|
||
}
|
||
if i < len(toLines) {
|
||
toLine = toLines[i]
|
||
}
|
||
|
||
if fromLine != toLine {
|
||
if fromLine != "" {
|
||
fmt.Printf("- %s\n", fromLine)
|
||
}
|
||
if toLine != "" {
|
||
fmt.Printf("+ %s\n", toLine)
|
||
}
|
||
} else if fromLine != "" {
|
||
fmt.Printf(" %s\n", fromLine)
|
||
}
|
||
}
|
||
}
|
||
|
||
// announcePatchSubmission sends HMMM message about patch submission
|
||
func (t *TerminalInterface) announcePatchSubmission(patchID string, patchData map[string]interface{}) error {
|
||
msgID := t.generateMessageID()
|
||
threadID := fmt.Sprintf("patch-%s", patchID)
|
||
|
||
message := hmmm.Message{
|
||
Topic: fmt.Sprintf("CHORUS/patches/submission/%s", patchData["type"]),
|
||
Type: "patch_submission",
|
||
Payload: map[string]interface{}{
|
||
"patch_id": patchID,
|
||
"title": patchData["title"],
|
||
"description": patchData["description"],
|
||
"patch_type": patchData["type"],
|
||
"base_address": patchData["base_address"],
|
||
"author": patchData["author"],
|
||
"author_type": patchData["author_type"],
|
||
"reviewers": patchData["reviewers"],
|
||
"deadline": patchData["deadline"],
|
||
},
|
||
Version: "1.0",
|
||
IssueID: 0,
|
||
ThreadID: threadID,
|
||
MsgID: msgID,
|
||
NodeID: t.runtime.Node.ID().String(),
|
||
HopCount: 0,
|
||
Timestamp: time.Now().Unix(),
|
||
Message: fmt.Sprintf("Patch submitted for review: %s", patchData["title"]),
|
||
}
|
||
|
||
return t.sendHMMMMessage(message)
|
||
}
|
||
|
||
// announcePatchReview sends HMMM message about patch review
|
||
func (t *TerminalInterface) announcePatchReview(patchID, reviewType, reasoning string) error {
|
||
msgID := t.generateMessageID()
|
||
threadID := fmt.Sprintf("patch-%s", patchID)
|
||
|
||
message := hmmm.Message{
|
||
Topic: fmt.Sprintf("CHORUS/patches/review/%s", patchID),
|
||
Type: "patch_review",
|
||
Payload: map[string]interface{}{
|
||
"patch_id": patchID,
|
||
"review_type": reviewType,
|
||
"reasoning": reasoning,
|
||
"reviewer": t.runtime.Config.Agent.ID,
|
||
"reviewer_type": "human",
|
||
},
|
||
Version: "1.0",
|
||
IssueID: 0,
|
||
ThreadID: threadID,
|
||
MsgID: msgID,
|
||
NodeID: t.runtime.Node.ID().String(),
|
||
HopCount: 0,
|
||
Timestamp: time.Now().Unix(),
|
||
Message: fmt.Sprintf("Patch review: %s (%s)", reviewType, patchID),
|
||
}
|
||
|
||
return t.sendHMMMMessage(message)
|
||
} |