opencode-goal-plugin 0.6.4 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.5 — 2026-07-12
4
+
5
+ - Make `/goal sequence` the canonical ordered multi-goal command, retain the previous command and mode spelling as input-only compatibility aliases, correct the public auditor snapshot mode type to `"normal" | "ordered"`, and align README archive and compatibility claims with verified behavior.
6
+
3
7
  ## 0.6.4 — 2026-07-12
4
8
 
5
9
  - Re-check session status and recent messages after an auto-continue cooldown, pause immediately for human intervention, Plan-agent switches, permission rejection, aborts, and provider errors, and abort an accepted continuation when the user takes control.
package/README.md CHANGED
@@ -36,15 +36,16 @@ surface and versioning expectations.
36
36
 
37
37
  ### OpenCode version compatibility
38
38
 
39
- Manually tested via the OpenCode TUI (`tmux` + real provider credentials, no mocks), verified against the plugin's own persisted state rather than terminal display alone:
39
+ Tested against real OpenCode 1.17.15 processes with live provider credentials and no mocked plugin hooks. State, ledger entries, and workspace files were checked independently of terminal or model prose:
40
40
 
41
41
  | OpenCode Version | Provider Tested | `/goal status` | Auto-continue | Evidence-gated completion | Hook Output Display |
42
42
  |---|---|---|---|---|---|
43
+ | 1.17.15 | opencode (`deepseek-v4-flash-free`) | ✅ Canonical tool | ✅ Checkpoint + idle continuation | ✅ Structured `goal_complete` claim | ⚠️ Command text routed to model; mutation guard verified |
43
44
  | 1.17.15 | opencode-go (`qwen3.7-plus`) | ✅ | ✅ | ✅ Self-corrected after one rejection (bare `[goal:complete]` with no evidence), then completed cleanly | ⚠️ Not displayed |
44
45
  | 1.17.15 | opencode-go (`glm-5.2`) | ✅ | ✅ | ✅ Clean `[goal:evidence]` + `[goal:complete]` on the first attempt | ⚠️ Not displayed |
45
46
  | 1.17.15 | deepseek (`deepseek-chat`) | ✅ | ✅ | ✅ Clean `[goal:evidence]` + `[goal:complete]` on the first attempt; also verified end-to-end via the [demo](demo/) — autonomously fixed a real bug and reported evidence-backed completion | ⚠️ Not displayed |
46
47
 
47
- `/goal status` and auto-continue are graded on **state correctness** (verified directly against the plugin's persisted state file: correct limits parsed, correct turn/stop accounting, correct completion detection) not on what's rendered in the terminal, since that's tracked separately as Hook Output Display.
48
+ `/goal status` and auto-continue are graded on **state correctness** (verified directly against persisted state: correct limits, turn/stop accounting, completion state, and file effects), not on terminal rendering. The `deepseek-v4-flash-free` canary suite additionally covers pause/resume across processes, blocker/restart, hard-process recovery, real host compaction, and stale-history clear enforcement. See [`docs/providers.md`](docs/providers.md) for the complete lifecycle matrix and session evidence.
48
49
 
49
50
  **Note:** Hook output display depends on OpenCode version — on 1.17.15, `command.execute.before`'s `output.parts` text is not rendered in the TUI for any provider tested; the raw command argument is instead routed to the model as a normal chat turn (see [Limitations](#limitations)). State mutations always work regardless of display: goal creation, flag parsing, auto-continue, limit enforcement, and evidence-gated completion detection were all verified correct via the persisted state file in every combination above. Re-test against your own OpenCode build before relying on unattended runs, and see [`docs/providers.md`](docs/providers.md) for the full per-model marker-compliance notes.
50
51
 
@@ -89,7 +90,7 @@ Add success criteria, constraints / non-goals, and a mode:
89
90
  /goal ship the release --success "tests pass and changelog updated" --constraints "do not touch the public API" --mode ordered
90
91
  ```
91
92
 
92
- `--success` (alias `--success-criteria`) and `--constraints` (alias `--non-goals`) take quoted text and are injected alongside the objective so the assistant keeps them in view. `--mode` is `normal` (default) or `ordered` (alias `sisyphus`); `ordered` asks the assistant to work through the objective as a strict sequence. Multi-word values must be quoted.
93
+ `--success` (alias `--success-criteria`) and `--constraints` (alias `--non-goals`) take quoted text and are injected alongside the objective so the assistant keeps them in view. `--mode` is `normal` (default) or `ordered`; `ordered` tells the assistant to preserve step order inside one objective. To queue distinct objectives that auto-promote one at a time, use `/goal sequence`. Multi-word values must be quoted.
93
94
 
94
95
  Flags accept either `--flag value` or `--flag=value`. If a flag is unknown, missing a value, given a non-positive integer, or (for `--mode`) an unrecognized mode, the plugin rejects the command with a helpful error instead of silently folding the bad flag into the goal text.
95
96
 
@@ -125,7 +126,7 @@ Pause without clearing the active goal:
125
126
  /goal pause
126
127
  ```
127
128
 
128
- Clear the active goal:
129
+ Clear all live goals in the current session and discard their saved status:
129
130
 
130
131
  ```
131
132
  /goal clear
@@ -155,14 +156,14 @@ A session can hold more than one goal. `/goal <condition>` replaces the focused
155
156
  /goal focus 1
156
157
  ```
157
158
 
158
- `/goal list` shows the numbered live goals (which is focused, which are backgrounded) and a per-session archive of completed/cleared goals so they stay readable. `/goal focus <number>` switches the active goal, backgrounding the previous one. Focus is tracked per session and survives a restart.
159
+ `/goal list` shows numbered live goals (focused and backgrounded) plus achieved goals retained in the per-session archive. `/goal clear` intentionally removes live goals and saved status from these views; its terminal ledger entries remain available for crash-safe recovery decisions. `/goal focus <number>` switches the active goal, backgrounding the previous one. Focus is tracked per session and survives a restart.
159
160
 
160
- #### Ordered (sisyphus) sequences
161
+ #### Ordered sequences
161
162
 
162
- `/goal sisyphus` sets up a strict execution sequence: separate the objectives with `;` or newlines, and the plugin runs them one at a time, auto-focusing the next as soon as the current one completes.
163
+ `/goal sequence` sets up a strict execution queue: separate objectives with `;` or newlines, and the plugin runs them one at a time, focusing the next as soon as the current one completes.
163
164
 
164
165
  ```
165
- /goal sisyphus build the parser; write the tests; ship the release
166
+ /goal sequence build the parser; write the tests; ship the release
166
167
  ```
167
168
 
168
169
  The first goal is focused and the rest are queued. `/goal list` marks the session as ordered. Auto-promotion stops when the sequence is exhausted; `/goal clear` ends the sequence.
@@ -187,7 +188,7 @@ With success criteria, constraints, and a token budget shorthand:
187
188
  An ordered sequence, run as a strict pipeline:
188
189
 
189
190
  ```
190
- /goal sisyphus build the parser; write the tests; ship the release
191
+ /goal sequence build the parser; write the tests; ship the release
191
192
  ```
192
193
 
193
194
  ## How it works
@@ -272,7 +273,7 @@ Override any limit for a single goal:
272
273
  | `--no-progress-turns <n>` | Consecutive stalled low-output turns before pausing |
273
274
  | `--success <text>` | Success criteria that define when the goal is satisfied (quote multi-word text) |
274
275
  | `--constraints <text>` | Constraints / non-goals to respect (alias `--non-goals`) |
275
- | `--mode <normal\|ordered>` | Execution mode; `ordered` (alias `sisyphus`) asks for a strict sequence |
276
+ | `--mode <normal\|ordered>` | Prompt mode for one goal; `ordered` preserves step order inside its objective |
276
277
  | `--no-tool-turns <n>` | Consecutive tool-free continuation turns before pausing |
277
278
 
278
279
  Examples:
@@ -343,7 +344,7 @@ Registered tools:
343
344
 
344
345
  `goal_set` and `set_goal` are explicitly constrained to user-requested goals. `goal_complete` accepts a structured claim: a required non-empty `summary`, plus optional criterion/evidence pairs, checks (`passed`, `failed`, or `not-run`), changed files, and known limitations. Failed checks and empty criterion evidence are rejected before archival; accepted claims are serialized deterministically for the configured completion auditor. The legacy `update_goal` tool retains its string `evidence` field for compatibility.
345
346
 
346
- These operate on the same per-session multi-goal state as the command path: a tool-set goal persists, shows up in `/goal list`, and is driven by the idle auto-continue; completing a goal in an ordered (sisyphus) sequence auto-promotes the next.
347
+ These operate on the same per-session multi-goal state as the command path: a tool-set goal persists, shows up in `/goal list`, and is driven by the idle auto-continue; completing a goal in an ordered sequence auto-promotes the next.
347
348
 
348
349
  > Integration note: the tool execute-context shape (`ctx.sessionID`) and the `tool.schema` surface follow the OpenCode plugin docs. The tool **logic** is unit-tested independently, but the live registration should be confirmed against a real OpenCode run (see the smoke-test checklist).
349
350
 
package/index.d.ts CHANGED
@@ -48,7 +48,7 @@ export interface GoalAuditSnapshot {
48
48
  condition: string
49
49
  successCriteria: string
50
50
  constraints: string
51
- mode: "normal" | "sisyphus"
51
+ mode: "normal" | "ordered"
52
52
  sessionID: string
53
53
  turnCount: number
54
54
  startedAt: number
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-goal-plugin",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "description": "Durable, guarded goal workflows for OpenCode.",
5
5
  "type": "module",
6
6
  "main": "./src/goal-plugin.js",
@@ -74,7 +74,7 @@ const DEFAULT_OPTIONS = {
74
74
  // handler drives and that the system-prompt transform injects. `sessionGoals`
75
75
  // is the full registry of live goals per session (focused + backgrounded);
76
76
  // the focused goal is the same object reference held in both. `sessionArchive`
77
- // keeps a capped list of completed/cleared goals so they stay readable.
77
+ // keeps a capped list of achieved goals so completed work stays readable.
78
78
  function createRuntimeState() {
79
79
  return {
80
80
  goalStates: new Map(),
@@ -129,7 +129,7 @@ function runtimeCollection(name) {
129
129
  const goalStates = runtimeCollection("goalStates")
130
130
  const sessionGoals = runtimeCollection("sessionGoals")
131
131
  const sessionArchive = runtimeCollection("sessionArchive")
132
- // Sessions running an ordered (sisyphus) sequence: when the focused goal
132
+ // Sessions running an ordered sequence: when the focused goal
133
133
  // completes, the next live goal (in creation order) is auto-promoted to focus
134
134
  // so the sequence advances on its own.
135
135
  const sessionOrdered = runtimeCollection("sessionOrdered")
@@ -147,6 +147,9 @@ const seenOutputTokens = runtimeCollection("seenOutputTokens")
147
147
  const activeContinues = runtimeCollection("activeContinues")
148
148
  const CLEAR_COMMANDS = new Set(["clear", "stop", "off", "reset", "none", "cancel"])
149
149
  const PAUSE_COMMANDS = new Set(["pause"])
150
+ // `sequence` is canonical. The former public spelling remains accepted at
151
+ // the parser boundary so existing scripts do not break.
152
+ const SEQUENCE_COMMANDS = ["sequence", "sisyphus"]
150
153
  const READ_ONLY_COMMAND_TOOLS = new Set(["goal_status", "get_goal", "get_goal_history", "read", "glob", "grep"])
151
154
  const GOAL_FLAG_SPECS = {
152
155
  "--max-turns": {
@@ -211,8 +214,8 @@ function messageHasToolCall(message) {
211
214
 
212
215
  const GOAL_MODES = new Set(["normal", "ordered"])
213
216
 
214
- // Goal mode: normal vs ordered (a.k.a. sisyphus). `ordered`
215
- // signals a strict execution sequence; `sisyphus` is accepted as an alias.
217
+ // Goal mode: normal vs ordered. The former public spelling remains accepted
218
+ // as an input alias, while stored state and output always use `ordered`.
216
219
  // Returns the canonical mode or null when unrecognized.
217
220
  function normalizeMode(value) {
218
221
  const normalized = String(value || "").trim().toLowerCase()
@@ -727,7 +730,7 @@ function archiveSessionResult(sessionID, result) {
727
730
  sessionArchive.set(sessionID, list.slice(-MAX_ARCHIVED_PER_SESSION))
728
731
  }
729
732
 
730
- // Advance an ordered (sisyphus) sequence: focus the next live goal in creation
733
+ // Advance an ordered sequence: focus the next live goal in creation
731
734
  // order, clearing any backgrounded state so the idle handler drives it. Returns
732
735
  // the promoted goal, or null when the sequence is exhausted (which also clears
733
736
  // the session's ordered flag).
@@ -743,7 +746,7 @@ function promoteNextOrderedGoal(sessionID) {
743
746
  resumeGoalClock(next)
744
747
  next.skipNextTerminalCheck = true
745
748
  next.lastStatus = "Promoted as the next ordered goal."
746
- pushHistory(next, "focused", "Auto-promoted as the next goal in the ordered (sisyphus) sequence.")
749
+ pushHistory(next, "focused", "Auto-promoted as the next goal in the ordered sequence.")
747
750
  focusGoal(sessionID, next)
748
751
  return next
749
752
  }
@@ -2463,7 +2466,7 @@ function buildAgentToolHandlers({ defaultGoalOptions, persist, persistTerminalSt
2463
2466
  const ordered = sessionOrdered.has(sessionID)
2464
2467
  rememberGoalResult(sessionID, goal, "achieved", "", evidence)
2465
2468
  cleanupGoal(sessionID)
2466
- // Advance an ordered (sisyphus) sequence just like the marker path does.
2469
+ // Advance an ordered sequence just like the marker path does.
2467
2470
  if (ordered) promoteNextOrderedGoal(sessionID)
2468
2471
  const durable = await persistFinal("completion", ledgerDurable)
2469
2472
  if (durable === false) {
@@ -2707,7 +2710,7 @@ function formatGoalList(sessionID, commandName = "goal") {
2707
2710
 
2708
2711
  const lines = []
2709
2712
  if (goals.length) {
2710
- lines.push(`Goals (${goals.length})${sessionOrdered.has(sessionID) ? " — ordered (sisyphus)" : ""}:`)
2713
+ lines.push(`Goals (${goals.length})${sessionOrdered.has(sessionID) ? " — ordered sequence" : ""}:`)
2711
2714
  goals.forEach((goal, index) => {
2712
2715
  const marker = goal.goalId === focusedId ? "focused" : goal.stopped ? "background" : "idle"
2713
2716
  const state = goal.stopped && goal.goalId !== focusedId ? ` — ${goal.stopReason || "stopped"}` : ""
@@ -3347,8 +3350,11 @@ async function createGoalPlugin({ client, directory } = {}, pluginOptions = {})
3347
3350
  return
3348
3351
  }
3349
3352
 
3350
- if (args === "sisyphus" || args.toLowerCase().startsWith("sisyphus ")) {
3351
- const rest = args.slice("sisyphus".length).trim()
3353
+ const sequenceCommand = SEQUENCE_COMMANDS.find(
3354
+ (command) => args.toLowerCase() === command || args.toLowerCase().startsWith(`${command} `),
3355
+ )
3356
+ if (sequenceCommand) {
3357
+ const rest = args.slice(sequenceCommand.length).trim()
3352
3358
  const objectives = rest
3353
3359
  .split(/\n|;/)
3354
3360
  .map((part) => stripWrappingQuotes(part.trim()))
@@ -3356,7 +3362,7 @@ async function createGoalPlugin({ client, directory } = {}, pluginOptions = {})
3356
3362
  if (!objectives.length) {
3357
3363
  output.parts = [
3358
3364
  makeTextPart(
3359
- `No objectives provided. Use \`/${commandName} sisyphus <objective 1>; <objective 2>; …\` (separate with \`;\` or newlines).`,
3365
+ `No objectives provided. Use \`/${commandName} sequence <objective 1>; <objective 2>; …\` (separate with \`;\` or newlines).`,
3360
3366
  ),
3361
3367
  ]
3362
3368
  return
@@ -3400,7 +3406,7 @@ async function createGoalPlugin({ client, directory } = {}, pluginOptions = {})
3400
3406
  pushHistory(
3401
3407
  created,
3402
3408
  "set",
3403
- `Ordered goal ${index + 1}/${objectives.length} created (sisyphus sequence).`,
3409
+ `Ordered goal ${index + 1}/${objectives.length} created.`,
3404
3410
  )
3405
3411
  registerSessionGoal(created)
3406
3412
  })
@@ -3410,7 +3416,7 @@ async function createGoalPlugin({ client, directory } = {}, pluginOptions = {})
3410
3416
  output.parts = [
3411
3417
  makeTextPart(
3412
3418
  [
3413
- `Started an ordered sequence of ${objectives.length} goal(s) (sisyphus mode):`,
3419
+ `Started an ordered sequence of ${objectives.length} goal(s):`,
3414
3420
  ...objectives.map((objective, index) => `${index + 1}. ${objective}`),
3415
3421
  "",
3416
3422
  `Focused goal 1: ${firstGoal.condition}`,
@@ -3560,7 +3566,7 @@ async function createGoalPlugin({ client, directory } = {}, pluginOptions = {})
3560
3566
  // Replace the focused goal (cleanupGoal discards it); backgrounded goals
3561
3567
  // for this session are preserved. Use `/goal add` to keep the current
3562
3568
  // goal and add another. Clear any ordered-sequence flag so the new
3563
- // standalone goal does not trigger sisyphus auto-promotion of old sequence
3569
+ // standalone goal does not trigger auto-promotion of the old sequence
3564
3570
  // goals that may still be in the registry (matches the agent setGoal path).
3565
3571
  sessionOrdered.delete(sessionID)
3566
3572
  cleanupGoal(sessionID)
@@ -3852,7 +3858,7 @@ async function createGoalPlugin({ client, directory } = {}, pluginOptions = {})
3852
3858
  const ordered = sessionOrdered.has(sessionID)
3853
3859
  rememberGoalResult(sessionID, activeGoalAfterMessages, "achieved", "", evidence)
3854
3860
  cleanupGoal(sessionID)
3855
- // Ordered (sisyphus) sequence: auto-promote the next goal so the
3861
+ // Ordered sequence: auto-promote the next goal so the
3856
3862
  // session keeps working through the sequence without manual /goal focus.
3857
3863
  if (ordered) {
3858
3864
  promoteNextOrderedGoal(sessionID)