Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

---

## [Unreleased] — Domain Coverage Expansion (P0–P4)
## [Unreleased] — Domain Coverage Expansion (P0–P5)

### Added

Expand Down Expand Up @@ -41,9 +41,14 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `goclaw chat replay <agent> --session=<key>` and `goclaw chat sessions resume <agent> --session=<key>` — discoverability wrappers over existing chat session contracts.
- `goclaw tools invoke <name> --args=<json|@file>` — alias for `--params` with file-backed JSON support.

**P5 — Residual command fillers**
- `goclaw teams attachments download <team-id> <attachment-id> --output <file>` — authenticated attachment download with required output path and no-overwrite default.
- `goclaw agents evolution skill apply <agent-id> <suggestion-id> [--skill-draft @file]` — explicit wrapper for approving `skill_add` suggestions through the server evolution approval route.
- `goclaw agents evolution update` now maps `--action=accept|reject` to the server-compatible `status=approved|rejected` payload.

### Notes
- All new commands honor the AI-first ergonomics contract: `--output=json` envelope, central error handler, `--yes` for destructive ops, `--quiet` for CI.
- P4/P5 backlog was re-swept against the current CLI surface; already-covered items were removed from residual scope before the next implementation pass.
- P4/P5 backlog was re-swept against the current CLI surface; already-covered items were removed from residual scope before implementation.
- Out of scope: OpenAI-compatible `/chat/completions` and `/v1/responses` endpoints (client APIs, not admin CLI surface).

---
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ echo "Analyze this log" | goclaw chat myagent
|---------|-------------|
| `auth` | Login, logout, device pairing, profile management |
| `profile` | List, create, switch, inspect, and delete CLI profiles |
| `agents` | CRUD, shares, delegation links, per-user instances |
| `agents` | CRUD, shares, delegation links, per-user instances, evolution |
| `chat` | Interactive or single-shot messaging with streaming |
| `sessions` | List, preview, delete, reset, label, compact |
| `codex-pool` | Unified Codex pool activity lookup for agents/providers |
Expand All @@ -63,7 +63,7 @@ echo "Analyze this log" | goclaw chat myagent
| `providers` | LLM provider CRUD, model listing, verification |
| `tools` | Custom + built-in tool management, invocation |
| `cron` | Scheduled jobs CRUD, trigger, run history |
| `teams` | Team management, task board, workspace |
| `teams` | Team management, task board, workspace, attachments |
| `channels` | Channel instances, contacts, pending messages |
| `traces` | LLM trace viewer, filters, export |
| `memory` | Memory documents, semantic search |
Expand Down Expand Up @@ -324,6 +324,12 @@ goclaw chat sessions resume myagent --session=sess-123 -m "Continue" --no-stream

# Invoke a custom tool with JSON args from file
goclaw tools invoke weather --args=@payload.json

# Download a team task attachment to an explicit file
goclaw teams attachments download team-123 attachment-456 --output ./artifact.bin

# Approve a skill_add evolution suggestion, optionally overriding the draft
goclaw agents evolution skill apply agent-123 suggestion-456 --skill-draft @./SKILL.md
```

## API Docs
Expand Down
86 changes: 83 additions & 3 deletions cmd/agents_evolution.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package cmd

import (
"encoding/json"
"fmt"
"net/url"

"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -85,26 +87,104 @@ Example:
if err != nil {
return err
}
status := map[string]string{
"accept": "approved",
"reject": "rejected",
}[action]
_, err = c.Patch(
fmt.Sprintf("/v1/agents/%s/evolution/suggestions/%s", args[0], args[1]),
map[string]any{"action": action},
fmt.Sprintf(
"/v1/agents/%s/evolution/suggestions/%s",
url.PathEscape(args[0]),
url.PathEscape(args[1]),
),
map[string]any{"status": status},
)
if err != nil {
return err
}
printer.Success(fmt.Sprintf("Suggestion %s: %sd", args[1], action))
printer.Success(fmt.Sprintf("Suggestion %s %s", args[1], status))
return nil
},
}

var agentsEvolutionSkillCmd = &cobra.Command{
Use: "skill",
Short: "Apply skill evolution suggestions",
}

var agentsEvolutionSkillApplyCmd = &cobra.Command{
Use: "apply <id> <suggestionID>",
Short: "Approve a skill_add evolution suggestion",
Long: `Approve a skill_add evolution suggestion for an agent.

PATCH /v1/agents/{id}/evolution/suggestions/{suggestionID}

Example:
goclaw agents evolution skill apply agent-1 sugg-42
goclaw agents evolution skill apply agent-1 sugg-42 --skill-draft @./SKILL.md`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
body := map[string]any{"status": "approved"}
if cmd.Flags().Changed("skill-draft") {
draft, _ := cmd.Flags().GetString("skill-draft")
content, err := readContent(draft)
if err != nil {
return err
}
body["skill_draft"] = content
}
c, err := newHTTP()
if err != nil {
return err
}
if err := requireSkillAddSuggestion(c, args[0], args[1]); err != nil {
return err
}
data, err := c.Patch(
fmt.Sprintf(
"/v1/agents/%s/evolution/suggestions/%s",
url.PathEscape(args[0]),
url.PathEscape(args[1]),
),
body,
)
if err != nil {
return err
}
printer.Print(unmarshalMap(data))
return nil
},
}

func requireSkillAddSuggestion(c interface {
Get(path string) (json.RawMessage, error)
}, agentID, suggestionID string) error {
data, err := c.Get("/v1/agents/" + url.PathEscape(agentID) + "/evolution/suggestions?status=pending&limit=500")
if err != nil {
return err
}
for _, suggestion := range unmarshalList(data) {
if str(suggestion, "id") == suggestionID {
if str(suggestion, "suggestion_type") != "skill_add" {
return fmt.Errorf("suggestion %s is %q, not skill_add", suggestionID, str(suggestion, "suggestion_type"))
}
return nil
}
}
return fmt.Errorf("suggestion %s not found in agent evolution suggestions", suggestionID)
}

func init() {
agentsEvolutionUpdateCmd.Flags().String("action", "", "Action: accept or reject")
_ = agentsEvolutionUpdateCmd.MarkFlagRequired("action")
agentsEvolutionSkillApplyCmd.Flags().String("skill-draft", "", "Skill draft content or @file")
agentsEvolutionSkillCmd.AddCommand(agentsEvolutionSkillApplyCmd)

agentsEvolutionCmd.AddCommand(
agentsEvolutionMetricsCmd,
agentsEvolutionSuggestionsCmd,
agentsEvolutionUpdateCmd,
agentsEvolutionSkillCmd,
)
agentsCmd.AddCommand(agentsEvolutionCmd)
}
Loading
Loading