picoclaw/cmd/piconomous/internal/cron/add_test.go
Claude b7b671f1c1
rename picoclaw -> piconomous + add fully autonomous mode
## Rename
- Module path: github.com/sipeed/picoclaw -> github.com/sipeed/piconomous
- All binary names: picoclaw* -> piconomous*
- Config/data paths: ~/.picoclaw -> ~/.piconomous
- Env vars: PICOCLAW_* -> PICONOMOUS_*
- Directory renames: cmd/picoclaw -> cmd/piconomous, cmd/picoclaw-launcher-tui -> cmd/piconomous-launcher-tui
- Asset renames, desktop file, docker services

## Fully Autonomous Mode (new feature)
Adds `autonomous` config section with:
- `enabled`: activates goal-driven autonomous operation
- `interval_minutes`: heartbeat frequency (default 5 min in autonomous mode)
- `allow_self_schedule`: agent can create cron jobs for itself without command_confirm
- `max_goals`: cap on concurrent active goals (default 20)

### New: Goal Management Tool (pkg/tools/goal.go)
- `goal` tool with actions: set/list/complete/update/drop
- Goals persist in GOALS.md (human-readable) + goals.json (machine-readable)
- Priority levels P1-P5, status lifecycle: active -> completed/dropped

### Enhanced Heartbeat Service
- SetAutonomous(bool) activates goal-driven prompt mode
- In autonomous mode: reads GOALS.md each cycle, builds a rich prompt that
  instructs the agent to run the full software dev lifecycle:
  write code, run tests, fix failures, commit progress, schedule next steps
- Creates default GOALS.md template on first run
- Autonomous interval overrides heartbeat interval (5 min default vs 30 min)

### Cron Tool
- In autonomous mode, agent can self-schedule on internal channels without
  requiring explicit command_confirm=true (GHSA-pv8c-p6jf-3fpp exception)

### Goal Tool Registration
- Registered in AgentLoop.registerSharedTools when autonomous.enabled=true
  or when tools.goal is explicitly enabled in config

https://claude.ai/code/session_0118WWK5KLRM7ZBUoC8EMgKS
2026-03-21 15:17:32 +00:00

57 lines
1.3 KiB
Go

package cron
import (
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewAddSubcommand(t *testing.T) {
fn := func() string { return "" }
cmd := newAddCommand(fn)
require.NotNil(t, cmd)
assert.Equal(t, "add", cmd.Use)
assert.Equal(t, "Add a new scheduled job", cmd.Short)
assert.True(t, cmd.HasFlags())
assert.NotNil(t, cmd.Flags().Lookup("every"))
assert.NotNil(t, cmd.Flags().Lookup("cron"))
assert.NotNil(t, cmd.Flags().Lookup("deliver"))
assert.NotNil(t, cmd.Flags().Lookup("to"))
assert.NotNil(t, cmd.Flags().Lookup("channel"))
nameFlag := cmd.Flags().Lookup("name")
require.NotNil(t, nameFlag)
messageFlag := cmd.Flags().Lookup("message")
require.NotNil(t, messageFlag)
val, found := nameFlag.Annotations[cobra.BashCompOneRequiredFlag]
require.True(t, found)
require.NotEmpty(t, val)
assert.Equal(t, "true", val[0])
val, found = messageFlag.Annotations[cobra.BashCompOneRequiredFlag]
require.True(t, found)
require.NotEmpty(t, val)
assert.Equal(t, "true", val[0])
}
func TestNewAddCommandEveryAndCronMutuallyExclusive(t *testing.T) {
cmd := newAddCommand(func() string { return "testing" })
cmd.SetArgs([]string{
"--name", "job",
"--message", "hello",
"--every", "10",
"--cron", "0 9 * * *",
})
err := cmd.Execute()
require.Error(t, err)
}