picoclaw/cmd/piconomous/internal/cron/add.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

64 lines
1.7 KiB
Go

package cron
import (
"fmt"
"github.com/spf13/cobra"
"github.com/sipeed/piconomous/pkg/cron"
)
func newAddCommand(storePath func() string) *cobra.Command {
var (
name string
message string
every int64
cronExp string
deliver bool
channel string
to string
)
cmd := &cobra.Command{
Use: "add",
Short: "Add a new scheduled job",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, _ []string) error {
if every <= 0 && cronExp == "" {
return fmt.Errorf("either --every or --cron must be specified")
}
var schedule cron.CronSchedule
if every > 0 {
everyMS := every * 1000
schedule = cron.CronSchedule{Kind: "every", EveryMS: &everyMS}
} else {
schedule = cron.CronSchedule{Kind: "cron", Expr: cronExp}
}
cs := cron.NewCronService(storePath(), nil)
job, err := cs.AddJob(name, schedule, message, deliver, channel, to)
if err != nil {
return fmt.Errorf("error adding job: %w", err)
}
fmt.Printf("✓ Added job '%s' (%s)\n", job.Name, job.ID)
return nil
},
}
cmd.Flags().StringVarP(&name, "name", "n", "", "Job name")
cmd.Flags().StringVarP(&message, "message", "m", "", "Message for agent")
cmd.Flags().Int64VarP(&every, "every", "e", 0, "Run every N seconds")
cmd.Flags().StringVarP(&cronExp, "cron", "c", "", "Cron expression (e.g. '0 9 * * *')")
cmd.Flags().BoolVarP(&deliver, "deliver", "d", false, "Deliver response to channel")
cmd.Flags().StringVar(&to, "to", "", "Recipient for delivery")
cmd.Flags().StringVar(&channel, "channel", "", "Channel for delivery")
_ = cmd.MarkFlagRequired("name")
_ = cmd.MarkFlagRequired("message")
cmd.MarkFlagsMutuallyExclusive("every", "cron")
return cmd
}