v0.13.0 befd698

update_goal.go RawHistoryFolder

1package tools
2
3import (
4	"bytes"
5	"context"
6	"encoding/json"
7	"io"
8	"strings"
9
10	"github.com/bnema/zut/packages/core"
11	"github.com/bnema/zut/packages/provider"
12)
13
14const (
15	UpdateGoalToolName = "update_goal"
16	updateGoalSchema   = `{"type":"object","properties":{"status":{"type":"string","enum":["complete","blocked"],"description":"Mark the active autonomous goal complete or blocked."},"reason":{"type":"string","description":"Concise reason when the goal is blocked."}},"required":["status"],"additionalProperties":false}`
17)
18
19// UpdateGoalTool lets the model stop an explicitly user-started autonomous
20// goal. Starting, pausing, and resuming remain user-controlled.
21type UpdateGoalTool struct{}
22
23type updateGoalArgs struct {
24	Status string `json:"status"`
25	Reason string `json:"reason,omitempty"`
26}
27
28// GoalUpdate is trusted metadata returned only by UpdateGoalTool.
29type GoalUpdate struct {
30	Status core.GoalStatus
31	Reason string
32}
33
34func (t *UpdateGoalTool) Name() string { return UpdateGoalToolName }
35
36func (t *UpdateGoalTool) Description() string {
37	return "Mark the active autonomous goal complete or blocked. Only use this for a goal explicitly started by the user with /goal."
38}
39
40func (t *UpdateGoalTool) Schema() json.RawMessage { return json.RawMessage(updateGoalSchema) }
41
42func (t *UpdateGoalTool) Execute(_ context.Context, raw json.RawMessage, _ func(string)) (core.ToolResult, error) {
43	var args updateGoalArgs
44	decoder := json.NewDecoder(bytes.NewReader(raw))
45	decoder.DisallowUnknownFields()
46	if err := decoder.Decode(&args); err != nil {
47		return goalToolError("invalid arguments"), nil
48	}
49	if err := decoder.Decode(&struct{}{}); err != io.EOF {
50		return goalToolError("invalid arguments"), nil
51	}
52	args.Status = strings.TrimSpace(args.Status)
53	args.Reason = strings.TrimSpace(args.Reason)
54
55	var update GoalUpdate
56	switch args.Status {
57	case "complete":
58		update.Status = core.GoalDone
59	case "blocked":
60		if args.Reason == "" {
61			return goalToolError("reason is required when blocking a goal"), nil
62		}
63		update.Status = core.GoalBlocked
64		update.Reason = args.Reason
65	default:
66		return goalToolError("status must be complete or blocked"), nil
67	}
68
69	text := "autonomous goal marked " + string(update.Status)
70	if update.Reason != "" {
71		text += ": " + update.Reason
72	}
73	return core.ToolResult{
74		Content: []provider.Content{provider.TextBlock{Text: text}},
75		Details: update,
76	}, nil
77}
78
79// GoalUpdateFromResult extracts a valid update_goal state transition.
80func GoalUpdateFromResult(result core.ToolResult) (GoalUpdate, bool) {
81	if result.IsError {
82		return GoalUpdate{}, false
83	}
84	update, ok := result.Details.(GoalUpdate)
85	if !ok {
86		return GoalUpdate{}, false
87	}
88	if update.Status != core.GoalDone && update.Status != core.GoalBlocked {
89		return GoalUpdate{}, false
90	}
91	if update.Status == core.GoalBlocked && strings.TrimSpace(update.Reason) == "" {
92		return GoalUpdate{}, false
93	}
94	return update, true
95}
96
97func goalToolError(message string) core.ToolResult {
98	return core.ToolResult{
99		Content: []provider.Content{provider.TextBlock{Text: message}},
100		IsError: true,
101	}
102}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 477

↑↓ moveenter openesc close