Skip to content

Commit 025f1e8

Browse files
author
SqlRush
committed
Add ScheduleCron manifest tool
1 parent 01d5747 commit 025f1e8

7 files changed

Lines changed: 610 additions & 3 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
| 文件工具 M5 初版 | 已完成文本版 `Read`、image Read 初版、notebook Read 初版、Read 大文本预算截断/落盘、`Write``Edit`,含读前写、mtime stale guard、`replace_all`、structured diff、`.claude/settings*.json` 写前校验、team-memory secret guard、Read 去重 |
3131
| M6 初始上下文层 | 已完成 CLAUDE.md/memdir 扫描、memory manifest、team-memory secret guard、compact threshold/prompt/runner/boundary plan、conversation auto-compact 接入、失败熔断、microcompact/cache 初版、persistent cached microcompact 初版、cache digest structural/rich-content metadata 覆盖、cache version/TTL/prune、in-memory micro cache prune、memory-cache write-through 到磁盘、atomic cache write、坏缓存默认 fail-open、session memory summary/frontmatter aliases/recall 初版、model-ranked recall session-id selection 和 invalid-selection fallback 初版、recall agent alternate/camel response keys/fenced-prose JSON extraction/scalar id parsing/nested/wrapped/collection-alias selection parsing、resume context model-assisted recall 接入、session memory rollup/prune compaction、rollup archive exclusion/merge、rune-safe rollup truncation、resume context + session memory recall、conversation recall 注入、deterministic/model-backed memory extraction 初版、extraction agent fenced-prose JSON/wrapped facts/provider-style response wrapper/alternate/structured field/nested source object/nested response/fact kind alias parsing、turn-end memory extraction 落盘、prompt history lock/buffered flush/field aliases、official subagent transcript layout、agent metadata sidecar/field aliases、sidechain runtime start/append/finish/cancel/fail 和 parent-chain append/finish 初版、sidechain manager orchestration 初版、sidechain manifest 聚合、sidechain state/list/resume/content-field aliases 初版、sidechain resume context builder、sidechain conversation/content-replacement reconstruction、transcript tail/window/metadata/index loaders、byte-budget transcript tail loader、agent-scoped content replacement metadata/record field-alias loading、session-scoped metadata reappend including AI-title/last-prompt/task-summary、transcript line offset index/window/byte-budget-window/parent-chain/resume/tail/byte-budget-tail loaders、extended transcript metadata entries/type/field aliases、transcript message/session UUID field aliases including top-level `messageUuid`/`messageId`/`id` record IDs and `role`/`entry_type`/`messageType`/`createdAt` timestamp aliases、transcript tombstone metadata delete/relink、transcript resume conversation builder、index 文本预览和 AI-title/last-prompt/task metadata 字段、流式 transcript 搜索、session list pagination/search/title、remote history token refresh、remote history 全量分页抓取/page-field/event-list/records/entries/eventList/sessionEvents/last-id/cursor/event-id/has-next alias/wrapped-data/links/paging/bare-array/keyed event map/connection/eventConnection/sessionEventsConnection wrapper response/edge-cursor fallback/max-pages 截断状态/before_id 续抓、remote event transcript materialization/fallback field fill/去重追加/duplicate parent guard、remote history 一步 sync 到 transcript |
3232
| M7 初始 TUI 层 | 已完成轻量 terminal frame renderer、PromptInput 状态机、history 导航、ctrl-p/ctrl-n history navigation、shift-enter 多行输入、多行 prompt 行内 ctrl-a/ctrl-e/ctrl-u/ctrl-k 和 wrap/render/cursor、共享 kill ring、ctrl-b/ctrl-f/ctrl-u/ctrl-k/ctrl-w 行编辑、alt-b/alt-f/alt-d/alt-backspace word 编辑、ctrl-left/ctrl-right/alt-left/alt-right word motion、ctrl-y yank 和 alt-y yank-pop 初版、reverse-search cursor/word 编辑/kill/yank/yank-pop 初版、ctrl-c interrupt/双击退出事件、ctrl-d delete-forward/空输入双击退出事件、ctrl-l 重绘事件、ctrl-o/ctrl-t 全局切换事件、ctrl-g/ctrl-s/ctrl-x chord chat 事件、reverse-search 状态/渲染/脚本断言/空结果/选择回填/cursor 断言、paste/image hint 输入和 OSC ST/base64 filename 兼容、text/image pasted-content 引用/metadata 脚本断言/提交展开/history entry restoration、SGR mouse 解析、alternate terminal navigation key sequences including modified Home/End/Delete/PageUp/PageDown、滚轮滚动、修饰键滚轮/左键、左键拖动选择、viewport 半页/顶部/底部可配置滚动、viewport 点击选择和 dialog action 点击、focus/blur 事件、resize 视口保持、keybinding resolver/config/chord pending/null-unbind/key/action camelCase alias、JSON config loader 和 focus/mouse/paste/image key name 覆盖、vim insert/normal/j/k/word/WORD/ge/gE/line-local ^/$/0/|/I/A/D/quote/bracket text-object/yank/register/paste/delete/count/replace/undo/find/till/repeat/matching-pair %/dot-repeat/G/gg/toggle/join/open-line/indent/substitute 动作、normal-mode arrow/backspace/delete 映射和 operator linewise/字符范围、REPL screen、permission/task dialog builder、dialog kind/id routing/runtime/status line、runtime 到 REPL screen 的 dialog/status 同步、runtime-aware interaction script runner、prompt text/cursor/expanded/vim mode/register/task state/dialog result/runtime mutation/task bulk-cancel/permission cancel/keybinding mutation/status negative/snapshot negative/screen size/event-sequence/event-count/no-event/dialog-result-count/no-dialog-result 脚本断言、viewport 脚本断言、named-key 脚本输入、script JSON/JSONL/wrapper loader、script file runner 和 runtime/task camel field aliases、stale dialog race guard、cancel active、permission id/all cancellation、queued permission promotion、active task dialog refresh、task lifecycle/bulk-cancel 初版、idempotent alternate screen lifecycle/reset/reassert interactive 初版、mouse/focus/bracketed-paste terminal mode lifecycle/reconciliation、ANSI snapshot 基础、snapshot corpus write/compare/script-file compare/missing-baseline/diff/batch/strict unexpected-baseline 状态、scripted interaction runner/assertions/multi-key/text/paste/image/pasted-content metadata 初版、status/dialog/message components、viewport/selection |
33-
| M10 Agents/tasks/worktree/remote | 已有 Task/TaskOutput/KillTask/SendMessage/TeamCreate/TeamDelete/TeamOutput/TeamSendMessage/TeamCoordinate/ResumeTask/Sleep/Brief 入口、sidechain metadata/lifecycle、task progress event、显式与 settings 默认 owned worktree 创建/清理、sparse/symlink settings 应用、`run:true` subagent nested tool loop、agent permission mode/allowlist 过滤,以及 session-scoped team manifest、team coordinator_task_id 元数据、TeamOutput coordinator status、TeamSendMessage target routing、coordinator briefing 和 structured handoff brief;完整多 agent 调度、远端协作和团队自动调度仍未完成 |
33+
| M10 Agents/tasks/worktree/remote | 已有 Task/TaskOutput/KillTask/SendMessage/TeamCreate/TeamDelete/TeamOutput/TeamSendMessage/TeamCoordinate/ResumeTask/Sleep/Brief/ScheduleCron 入口、sidechain metadata/lifecycle、task progress event、显式与 settings 默认 owned worktree 创建/清理、sparse/symlink settings 应用、`run:true` subagent nested tool loop、agent permission mode/allowlist 过滤,以及 session-scoped team/schedule manifest、team coordinator_task_id 元数据、TeamOutput coordinator status、TeamSendMessage target routing、coordinator briefing 和 structured handoff brief;完整多 agent 调度、远端协作和团队自动调度仍未完成 |
3434
| 全量测试 | 当前 `go test ./...` 通过 |
3535

3636
M10 补充:plugin command/agent 的 allowed tool frontmatter 解析现在只在顶层逗号或空白处分隔,保留括号、方括号和引号内的逗号/空白,避免 `Bash(git commit -m "x,y")` 这类 tool pattern 被误拆。
@@ -79,6 +79,8 @@ M10 补充:新增 `Sleep` 工具入口,支持 `duration_ms`/`seconds`/Go dur
7979

8080
M10 补充:新增 `Brief` 工具入口,可把 summary/title/status/details/next_steps/risks 规范为 structured handoff brief,并支持常见字段别名与单字符串列表项归一化;这为后续远端协作/UI brief surface 提供稳定 payload。完整 remote brief UI/调度接线仍未完成。
8181

82+
M10 补充:新增 `ScheduleCron` 工具入口和 session-scoped `schedules.json` manifest,可 create/list/delete cron schedule metadata,校验 5-field cron 或常见 `@daily` 类表达式,并可绑定 team_id/target/message。当前是调度状态地基,完整后台触发执行和 RemoteTrigger 仍未完成。
83+
8284
M7 补充:interaction script paste payload 现在接受 ClipboardItem 风格的 `items[].getAsString`/`get_as_string` 以及 `stringData`/`textData` 文本字段,DOM clipboard 录制脚本可直接恢复 pasted text。
8385

8486
M7 补充:scripted task runtime payload 和 task expectation 现在接受 `taskID``jobId``runId``label``displayName``phase``taskState``message``currentStep``percent`/`percentage`/`pct` 等相邻字段,并支持数字 task ID 与数字字符串 progress。
@@ -1436,7 +1438,7 @@ M7 补充:terminal input parser 和 configurable keybinding name parser 现在
14361438
- remote CCR agent、team/swarm/coordinator。
14371439
- Task*
14381440

1439-
当前状态:已有 Task/TaskOutput/KillTask/SendMessage/TeamCreate/TeamDelete/TeamOutput/TeamSendMessage/TeamCoordinate/ResumeTask/Sleep/Brief 入口、sidechain metadata/lifecycle、team manifest、team coordinator_task_id 元数据、TeamOutput coordinator status、TeamSendMessage target routing、TeamCoordinate coordinator briefing、structured handoff brief、task progress event、显式与 settings 默认 owned worktree 创建/清理、sparse/symlink settings 应用、`run:true` subagent nested tool loop、agent permission mode 应用、agent tool allowlist registry/permission pattern 过滤,以及 completed/failed/cancelled 终态 owned worktree 自动清理;完整多 agent 调度、远端协作和团队自动调度仍未完成。
1441+
当前状态:已有 Task/TaskOutput/KillTask/SendMessage/TeamCreate/TeamDelete/TeamOutput/TeamSendMessage/TeamCoordinate/ResumeTask/Sleep/Brief/ScheduleCron 入口、sidechain metadata/lifecycle、team manifest、schedule manifest、team coordinator_task_id 元数据、TeamOutput coordinator status、TeamSendMessage target routing、TeamCoordinate coordinator briefing、structured handoff brief、task progress event、显式与 settings 默认 owned worktree 创建/清理、sparse/symlink settings 应用、`run:true` subagent nested tool loop、agent permission mode 应用、agent tool allowlist registry/permission pattern 过滤,以及 completed/failed/cancelled 终态 owned worktree 自动清理;完整多 agent 调度、远端协作和团队自动调度仍未完成。
14401442

14411443
### M11: Bridge, LSP, Telemetry, Advanced Integrations
14421444

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,7 @@ test/parity/ # golden tests against TS/official behavior
729729
- 本轮补充:新增 `TeamCoordinate` 工具入口,可把 team description、成员 task status/summary 和用户 objective 组装为 deterministic briefing,发送给 running coordinator task;这提供了 coordinator 驱动团队协作的最小上下文注入能力。完整自动调度循环和远端协作仍未完成。
730730
- 本轮补充:新增 `Sleep` 工具入口,支持 `duration_ms`/`seconds`/Go duration 字符串,最大 60 秒,并使用 tool context cancellation 中断等待;这补齐了 proactive/gated 工具中的安全 wait 原语。完整 ScheduleCron/RemoteTrigger 仍未完成。
731731
- 本轮补充:新增 `Brief` 工具入口,可把 summary/title/status/details/next_steps/risks 规范为 structured handoff brief,并支持常见字段别名与单字符串列表项归一化;这为后续远端协作/UI brief surface 提供稳定 payload。完整 remote brief UI/调度接线仍未完成。
732+
- 本轮补充:新增 `ScheduleCron` 工具入口和 session-scoped `schedules.json` manifest,可 create/list/delete cron schedule metadata,校验 5-field cron 或常见 `@daily` 类表达式,并可绑定 team_id/target/message。当前是调度状态地基,完整后台触发执行和 RemoteTrigger 仍未完成。
732733

733734
### M11: Bridge 和高级集成
734735

internal/session/schedule.go

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
package session
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"sort"
9+
"strings"
10+
"time"
11+
12+
"ccgo/internal/contracts"
13+
"ccgo/internal/platform"
14+
)
15+
16+
type ScheduleState struct {
17+
ID string `json:"id"`
18+
SessionID contracts.ID `json:"sessionId,omitempty"`
19+
Description string `json:"description,omitempty"`
20+
Cron string `json:"cron,omitempty"`
21+
Message string `json:"message,omitempty"`
22+
TeamID string `json:"teamId,omitempty"`
23+
Target string `json:"target,omitempty"`
24+
Enabled bool `json:"enabled"`
25+
CreatedAt string `json:"createdAt,omitempty"`
26+
UpdatedAt string `json:"updatedAt,omitempty"`
27+
}
28+
29+
type ScheduleManifest struct {
30+
SessionID contracts.ID `json:"sessionId,omitempty"`
31+
Schedules []ScheduleState `json:"schedules,omitempty"`
32+
}
33+
34+
type ScheduleOptions struct {
35+
ID string
36+
Description string
37+
Cron string
38+
Message string
39+
TeamID string
40+
Target string
41+
Enabled bool
42+
Timestamp time.Time
43+
}
44+
45+
func ScheduleManifestPath(sessionPath string, sessionID contracts.ID) string {
46+
if sessionPath == "" || sessionID == "" {
47+
return ""
48+
}
49+
return filepath.Join(filepath.Dir(sessionPath), string(sessionID), "schedules.json")
50+
}
51+
52+
func LoadScheduleManifest(sessionPath string, sessionID contracts.ID) (ScheduleManifest, error) {
53+
path := ScheduleManifestPath(sessionPath, sessionID)
54+
if path == "" {
55+
return ScheduleManifest{}, os.ErrInvalid
56+
}
57+
data, err := os.ReadFile(path)
58+
if os.IsNotExist(err) {
59+
return ScheduleManifest{SessionID: sessionID}, nil
60+
}
61+
if err != nil {
62+
return ScheduleManifest{}, err
63+
}
64+
var manifest ScheduleManifest
65+
if err := json.Unmarshal(data, &manifest); err != nil {
66+
return ScheduleManifest{}, err
67+
}
68+
manifest.SessionID = sessionID
69+
for i := range manifest.Schedules {
70+
manifest.Schedules[i].SessionID = sessionID
71+
manifest.Schedules[i].ID = sanitizeSidechainID(manifest.Schedules[i].ID)
72+
manifest.Schedules[i].Description = strings.TrimSpace(manifest.Schedules[i].Description)
73+
manifest.Schedules[i].Cron = strings.TrimSpace(manifest.Schedules[i].Cron)
74+
manifest.Schedules[i].Message = strings.TrimSpace(manifest.Schedules[i].Message)
75+
manifest.Schedules[i].TeamID = sanitizeSidechainID(manifest.Schedules[i].TeamID)
76+
manifest.Schedules[i].Target = strings.TrimSpace(manifest.Schedules[i].Target)
77+
}
78+
sort.SliceStable(manifest.Schedules, func(i, j int) bool {
79+
return manifest.Schedules[i].ID < manifest.Schedules[j].ID
80+
})
81+
return manifest, nil
82+
}
83+
84+
func SaveScheduleManifest(sessionPath string, manifest ScheduleManifest) error {
85+
path := ScheduleManifestPath(sessionPath, manifest.SessionID)
86+
if path == "" {
87+
return os.ErrInvalid
88+
}
89+
data, err := json.MarshalIndent(manifest, "", " ")
90+
if err != nil {
91+
return err
92+
}
93+
data = append(data, '\n')
94+
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
95+
return err
96+
}
97+
return platform.AtomicWriteFile(path, data, 0o644)
98+
}
99+
100+
func UpsertSchedule(sessionPath string, sessionID contracts.ID, options ScheduleOptions) (ScheduleState, ScheduleManifest, error) {
101+
manifest, err := LoadScheduleManifest(sessionPath, sessionID)
102+
if err != nil {
103+
return ScheduleState{}, ScheduleManifest{}, err
104+
}
105+
id := sanitizeSidechainID(options.ID)
106+
if id == "" {
107+
id = string(contracts.NewID())
108+
}
109+
now := options.Timestamp
110+
if now.IsZero() {
111+
now = time.Now().UTC()
112+
}
113+
schedule := ScheduleState{
114+
ID: id,
115+
SessionID: sessionID,
116+
Description: strings.TrimSpace(options.Description),
117+
Cron: strings.TrimSpace(options.Cron),
118+
Message: strings.TrimSpace(options.Message),
119+
TeamID: sanitizeSidechainID(options.TeamID),
120+
Target: strings.TrimSpace(options.Target),
121+
Enabled: options.Enabled,
122+
CreatedAt: now.UTC().Format(time.RFC3339Nano),
123+
UpdatedAt: now.UTC().Format(time.RFC3339Nano),
124+
}
125+
replaced := false
126+
for i := range manifest.Schedules {
127+
if manifest.Schedules[i].ID != id {
128+
continue
129+
}
130+
if manifest.Schedules[i].CreatedAt != "" {
131+
schedule.CreatedAt = manifest.Schedules[i].CreatedAt
132+
}
133+
manifest.Schedules[i] = schedule
134+
replaced = true
135+
break
136+
}
137+
if !replaced {
138+
manifest.Schedules = append(manifest.Schedules, schedule)
139+
}
140+
sort.SliceStable(manifest.Schedules, func(i, j int) bool {
141+
return manifest.Schedules[i].ID < manifest.Schedules[j].ID
142+
})
143+
if err := SaveScheduleManifest(sessionPath, manifest); err != nil {
144+
return ScheduleState{}, ScheduleManifest{}, err
145+
}
146+
return schedule, manifest, nil
147+
}
148+
149+
func DeleteSchedule(sessionPath string, sessionID contracts.ID, scheduleID string) (ScheduleState, ScheduleManifest, error) {
150+
manifest, err := LoadScheduleManifest(sessionPath, sessionID)
151+
if err != nil {
152+
return ScheduleState{}, ScheduleManifest{}, err
153+
}
154+
id := sanitizeSidechainID(scheduleID)
155+
if id == "" {
156+
return ScheduleState{}, ScheduleManifest{}, fmt.Errorf("schedule_id is required")
157+
}
158+
next := manifest.Schedules[:0]
159+
var deleted ScheduleState
160+
found := false
161+
for _, schedule := range manifest.Schedules {
162+
if schedule.ID == id {
163+
deleted = schedule
164+
found = true
165+
continue
166+
}
167+
next = append(next, schedule)
168+
}
169+
if !found {
170+
return ScheduleState{}, ScheduleManifest{}, fmt.Errorf("schedule not found: %s", id)
171+
}
172+
manifest.Schedules = next
173+
if err := SaveScheduleManifest(sessionPath, manifest); err != nil {
174+
return ScheduleState{}, ScheduleManifest{}, err
175+
}
176+
return deleted, manifest, nil
177+
}

internal/session/sidechain_manager.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ func (m SidechainManager) DeleteTeam(teamID string) (TeamState, TeamManifest, er
3939
return DeleteTeam(m.Runtime.SessionPath, m.Runtime.SessionID, teamID)
4040
}
4141

42+
func (m SidechainManager) ScheduleManifest() (ScheduleManifest, error) {
43+
return LoadScheduleManifest(m.Runtime.SessionPath, m.Runtime.SessionID)
44+
}
45+
46+
func (m SidechainManager) UpsertSchedule(options ScheduleOptions) (ScheduleState, ScheduleManifest, error) {
47+
return UpsertSchedule(m.Runtime.SessionPath, m.Runtime.SessionID, options)
48+
}
49+
50+
func (m SidechainManager) DeleteSchedule(scheduleID string) (ScheduleState, ScheduleManifest, error) {
51+
return DeleteSchedule(m.Runtime.SessionPath, m.Runtime.SessionID, scheduleID)
52+
}
53+
4254
func (m SidechainManager) Resume(sidechainID string) (SidechainRun, bool, error) {
4355
return ResumeSidechainRun(m.Runtime.SessionPath, m.Runtime.SessionID, sidechainID)
4456
}

internal/tools/file/tools.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ func BuiltinTools() []tool.Tool {
207207
tasktools.NewResumeTaskTool(),
208208
tasktools.NewSleepTool(),
209209
tasktools.NewBriefTool(),
210+
tasktools.NewScheduleCronTool(),
210211
skilltools.NewSkillTool(),
211212
webtools.NewWebFetchTool(),
212213
webtools.NewWebSearchTool(),

0 commit comments

Comments
 (0)