Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5ca626e
ci: add npm Trusted Publishing workflow
willytop8 Jun 15, 2026
3744970
feat: require evidence to complete and a concrete blocker to block
willytop8 Jun 15, 2026
1a304ba
feat: pause auto-continue on repeated tool-free continuation turns
willytop8 Jun 15, 2026
0c836a3
feat: pause auto-continue on real user intervention
willytop8 Jun 15, 2026
f28bd38
feat: add success criteria, constraints, and mode to the goal schema
willytop8 Jun 15, 2026
d4f91b8
feat: project-local state default with env override and XDG migration
willytop8 Jun 15, 2026
88cf857
feat: add inline --budget flag with k/m suffix support
willytop8 Jun 15, 2026
33261c1
feat: configurable command name and register toggle
willytop8 Jun 15, 2026
bd0408a
feat: append-only lifecycle ledger with reconstruction and fail-close…
willytop8 Jun 15, 2026
3edc4f9
feat: announce completion/blocker audits with visible messages
willytop8 Jun 15, 2026
7880b9d
feat: deterministic compaction summary from the persisted goal record
willytop8 Jun 15, 2026
d119d78
feat: multiple goals per session with /goal add, list, and focus
willytop8 Jun 15, 2026
f5423e2
feat: /goal sisyphus ordered goal sequences
willytop8 Jun 15, 2026
33447c4
feat: optional separate completion auditor before archival
willytop8 Jun 15, 2026
c959159
Merge branch 'wr/ci-auto-publish' into wr/v0.3.0
willytop8 Jun 15, 2026
e0285c7
Merge branch 'wr/budget-flag' into wr/v0.3.0
willytop8 Jun 15, 2026
b37a7a7
Merge branch 'wr/goal-schema-fields' into wr/v0.3.0
willytop8 Jun 15, 2026
75d1c32
Merge branch 'wr/configurable-command' into wr/v0.3.0
willytop8 Jun 15, 2026
7d289f3
Merge branch 'wr/state-path-defaults' into wr/v0.3.0
willytop8 Jun 15, 2026
92cc5b6
Merge branch 'wr/compaction-summary' into wr/v0.3.0
willytop8 Jun 15, 2026
7564931
Merge branch 'wr/multi-goal' into wr/v0.3.0
willytop8 Jun 15, 2026
c23c9e0
Merge branch 'wr/lifecycle-ledger' into wr/v0.3.0
willytop8 Jun 15, 2026
a124225
Merge branch 'wr/evidence-blocker' into wr/v0.3.0
willytop8 Jun 15, 2026
f8b0f39
Merge branch 'wr/no-tool-call-gate' into wr/v0.3.0
willytop8 Jun 15, 2026
d893f54
Merge branch 'wr/user-intervention' into wr/v0.3.0
willytop8 Jun 15, 2026
d7eeecd
Merge branch 'wr/audit-messages' into wr/v0.3.0
willytop8 Jun 15, 2026
8e447bc
Merge branch 'wr/auditor-session' into wr/v0.3.0
willytop8 Jun 15, 2026
2aade05
Merge branch 'wr/sisyphus-goals' into wr/v0.3.0
willytop8 Jun 15, 2026
b8c7df6
chore(release): 0.3.0
willytop8 Jun 15, 2026
2ecbf9c
fix(state): resolve home-based paths from injected env for cross-plat…
willytop8 Jun 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
name: Publish

# Publishes opencode-goal-plugin to npm via npm Trusted Publishing (OIDC),
# so no long-lived NPM_TOKEN is stored in the repo.
#
# Release model: publish-on-version-change. The version in package.json is the
# single source of truth. On a push to main this workflow runs the full check
# matrix, then compares package.json's version against what is already on npm
# and publishes only when the version is new. Bumping the version stays a manual
# step (edit package.json + CHANGELOG.md, commit as `chore(release): x.y.z`);
# when that commit lands on main, CI publishes exactly that version.
#
# SETUP REQUIRED BEFORE THE FIRST RUN (left intentionally to a human):
# 1. Publish at least one version manually (`npm publish`) — npm only lets you
# add a Trusted Publisher after the package exists.
# 2. On npmjs.com → package settings → Trusted Publisher, add a GitHub Actions
# publisher pointing at this repo, workflow file `publish.yml`, environment
# `release`.
# 3. (Recommended) In GitHub → Settings → Environments, create the `release`
# environment and add required reviewers so each publish is gated.
# Until step 2 is done, the publish step will fail rather than publish silently.

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [18, 20, 22]
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- name: Run checks
run: npm run check

- name: Run package smoke test
run: npm run smoke

- name: Verify package contents
run: npm run pack:check

publish:
needs: test
runs-on: ubuntu-latest
# Gates each publish behind the `release` environment (configure required
# reviewers in GitHub). Also the environment name npm's Trusted Publisher
# is scoped to.
environment: release
permissions:
contents: read
# Required for npm Trusted Publishing: lets the job mint a short-lived
# OIDC token instead of using a stored npm auth token.
id-token: write
steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 22
registry-url: https://registry.npmjs.org

- name: Upgrade npm to a Trusted-Publishing-capable version
# npm Trusted Publishing (OIDC) requires npm >= 11.5.1.
run: npm install -g npm@latest

- name: Determine whether this version needs publishing
id: version-check
run: |
PKG_NAME="$(node -p "require('./package.json').name")"
LOCAL_VERSION="$(node -p "require('./package.json').version")"
echo "Package: $PKG_NAME@$LOCAL_VERSION"
if npm view "$PKG_NAME@$LOCAL_VERSION" version >/dev/null 2>&1; then
echo "already-published=true" >> "$GITHUB_OUTPUT"
echo "Version $LOCAL_VERSION is already on npm; skipping publish."
else
echo "already-published=false" >> "$GITHUB_OUTPUT"
echo "Version $LOCAL_VERSION is not on npm yet; will publish."
fi

- name: Publish to npm (Trusted Publishing / OIDC)
if: steps.version-check.outputs.already-published == 'false'
run: npm publish
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,43 @@

## Unreleased

## 0.3.0 — 2026-06-14

> A large feature release. Stronger completion integrity (evidence gate, optional auditor, visible audit messages), durable lifecycle ledger with state reconstruction, multiple goals per session with focus and ordered sisyphus sequences, richer goal schema, more auto-continue guardrails, project-local state with migration, agent-facing tools, a deterministic compaction summary, and npm Trusted Publishing CI. All changes are additive and backward-compatible; older state files load unchanged.

### Completion integrity & audit

- **Require evidence to complete a goal and a concrete blocker to block one.** A `[goal:complete]` marker is now only honored when the assistant also supplies a non-empty `[goal:evidence] <summary>` line (on or before the completion marker); a `[goal:blocked]` is only honored when a concrete blocker is stated on the line before it. An unsubstantiated `[goal:complete]` or `[goal:blocked]` is rejected (not recorded / does not stop the goal) and the plugin sends a corrective continuation prompt demanding the missing evidence or blocker. The accepted evidence is stored on the result and shown in `/goal status` / `/goal history`. New `extractCompletionEvidence` helper, an `<evidence_required>` structural tag (added to the injection-escaping set), and continuation/system/compaction/creation prompts all updated to instruct the evidence requirement. Implements megalist item 2.1.
- **Add an optional separate completion auditor that verifies before archival.** When a completion auditor is configured, a `[goal:complete]` (with evidence) is verified before the goal is archived: on approval it archives as achieved, on rejection the goal is *restored* (paused with stop reason `audit rejected` and the reason surfaced) rather than archived. Enable the built-in auditor — which spawns an independent OpenCode child session that replies `[audit:approved]`/`[audit:rejected]` — with `completionAudit: true`, or supply a custom `auditor({ goal, sessionID, latestText }) => { approved, reason }` (takes precedence). The built-in child-session auditor fails open if the session API is unavailable; a custom auditor that throws is treated as a rejection (fail closed). New `parseAuditVerdict` / `buildAuditPrompt` / `createChildSessionAuditor` helpers. Off by default. Implements megalist item 2.2.
- **Announce completion/blocker audits with visible messages instead of silent background work.** When the assistant marks a goal complete or blocked, the plugin emits an audit-start and an audit-result message (e.g. "Auditing goal completion…" → "Audit result: completion accepted — goal archived"). Delivery defaults to OpenCode's structured log (`client.app.log`) and is pluggable via an `auditMessenger(sessionID, text)` option or disable-able with `auditMessages: false`. New `defaultAuditMessenger` helper. Implements megalist item 2.4.

### Durability

- **Add an append-only JSONL lifecycle ledger with state reconstruction, and fail-closed terminal-state persistence.** Every lifecycle event (`pushHistory`) is also appended as one JSON line to `<stateFile>.ledger.jsonl` (synchronous, owner-only `0600`). Because in-memory history is capped, the ledger is the durable record: when the main state file is missing on startup, the plugin reconstructs still-active (non-`completed`/`cleared`) goals from the ledger and reloads them paused (new `reconstructed` load status). Terminal events are written to the ledger before the main state write, so a goal's terminal outcome survives a failed state write (fail-closed); `persistState` now returns success/failure and a failed terminal persist is logged at error level. Tied to `persistState`. New `appendLedgerLine` / `readLedgerEntries` / `reconstructGoalsFromLedger` helpers. Implements megalist items 2.3 and 2.5.
- **Build the compaction summary deterministically from the persisted goal record.** `buildCompactionContext` folds in a reproducible progress summary — recent checkpoints and lifecycle events — derived from the goal's persisted `checkpoints`/`history` (new `buildCompactionProgressSummary` helper) rather than chat memory, and labels it as such. Implements megalist item 6.3.

### Auto-continue guardrails

- **Pause auto-continue on repeated tool-free continuation turns (no-tool-call gate).** Complementing the low-output no-progress check, the plugin tracks continuation turns whose assistant message has no tool calls (OpenCode `tool` / `subtask` parts) and, after `noToolCallTurnsBeforePause` consecutive such turns (default `2`), pauses with stop reason `no tool calls` to guard against self-chat loops. A tool-using turn resets the counter. Configurable via the `noToolCallTurnsBeforePause` option and `--no-tool-turns <n>` flag. New `messageHasToolCall` helper. Implements megalist item 5.1.
- **Pause auto-continue when a real user message arrives ("latest instruction wins").** The idle handler detects a genuine human message that arrived after the plugin's most recent continuation and pauses the goal (stop reason `user intervention`) instead of talking over the user; `/goal resume` hands control back. Plugin-generated continuation prompts (user-role messages framed in `<goal_continuation>`) are ignored, and detection requires `turnCount > 0` plus a visible plugin continuation so the first idle and scrolled-out sessions are never misread. New `isPluginContinuationMessage` / `userInterventionDetected` helpers. Implements megalist items 5.2 and 5.3.

### Multiple goals

- **Support multiple goals per session with `/goal add`, `/goal list`, and `/goal focus`.** A session can hold several live goals via a new `sessionGoals` registry; `goalStates` continues to track the single *focused* goal the idle handler drives. `/goal <condition>` replaces the focused goal; `/goal add <condition>` backgrounds the current goal and focuses a new one (only the focused goal auto-continues). `/goal list` shows numbered live goals plus a per-session archive of completed/cleared goals, and `/goal focus <number|id>` switches the active goal (numeric refs are index-only). Focus is tracked per session and persisted (state files gain a per-goal `focused` flag and an `archives` array; older single-goal files load with their goal focused). New `buildGoalState` / `formatGoalList` / session-registry helpers. Implements megalist items 3.1, 3.2, and 3.3.
- **Add `/goal sisyphus` ordered goal sequences.** `/goal sisyphus <obj 1>; <obj 2>; …` sets up a strict execution sequence: the first objective is focused and the rest queued, and when the focused goal completes the plugin auto-promotes the next until the sequence is exhausted. The ordered flag is tracked per session, shown in `/goal list`, persisted (`orderedSessions`), and cleared by `/goal clear`. New `promoteNextOrderedGoal` helper. Implements megalist item 3.4.

### Schema & command UX

- **Add success-criteria, constraints/non-goals, and mode to the goal schema.** A goal can carry `successCriteria` (`--success`), `constraints` (`--constraints` / `--non-goals`), and a `mode` of `normal` or `ordered` (`--mode`, `sisyphus` alias). These thread through state, persistence, the injected goal block (escaped, new `success_criteria` / `constraints` structural tags), creation output, and `/goal status`. New `normalizeMode` helper. Implements megalist items 4.1, 4.2, and 4.3.
- **Add an inline `--budget <n>` flag** on the create command — a shorthand for the context-token limit accepting a plain integer or `k`/`m` suffix (e.g. `--budget 100k`). New `parseTokenBudget` helper. Implements megalist item 8.1.
- **Make the slash command configurable (`commandName`) and optional (`registerCommand`).** `commandName` (default `goal`, leading slash tolerated) lets the plugin own e.g. `/objective`, with all user-facing hints following the configured name; `registerCommand: false` skips installing the command hook entirely. New `normalizeCommandOptions` helper. Implements megalist item 8.2.

### Storage, tools & packaging

- **Default goal state to a project-local path, with an env override and migration fallbacks.** State resolves as `stateFilePath` option → `OPENCODE_GOAL_STATE_PATH` env var → project-local `<cwd>/.opencode/goals/state.json` (previously `~/.opencode-goal-plugin/state.json`). When the default path is empty, the plugin migrates forward on first load from the legacy home path and the XDG path, then writes project-local. Explicit option/env paths are literal with no fallback; a present-but-corrupt primary is preserved. New `resolveStateFilePath` / `xdgStateFilePath` / `legacyStateFilePaths` helpers. Home-based fallback paths resolve from an injectable `env.HOME` (falling back to `os.homedir()`), making path resolution deterministic across platforms — `os.homedir()` ignores `$HOME` on macOS. Implements megalist items 6.1 and 6.2.
- **Expose agent-facing goal tools (`get_goal`, `get_goal_history`, `set_goal`, `update_goal`, `clear_goal`)** when the host provides `@opencode-ai/plugin` (a new *optional* peer dependency, loaded via a cached dynamic import so the zero-runtime-dependency posture is preserved). `set_goal` is constrained to explicit user requests; `update_goal` supports objective edits and complete/blocked/paused/resumed transitions. Registration is skipped gracefully when the package is absent or with `registerTools: false`. New `buildAgentToolHandlers`. Implements megalist items 7.1 and 7.2.
- **Add a `Publish` GitHub Actions workflow (`.github/workflows/publish.yml`) for npm Trusted Publishing (OIDC).** On a push to `main` it runs the full check matrix on Node 18/20/22, then publishes via OIDC with no stored `NPM_TOKEN`, using a publish-on-version-change model (only publishes when `package.json`'s version is new). The publish job requires `id-token: write` and is gated behind a `release` environment. First run still requires a human to publish an initial version and configure the npm Trusted Publisher. Implements megalist item 9.1.

## 0.2.0 — 2026-06-14

- **Add `/goal edit <new objective>`.** Revise the active goal's objective in place while preserving its turn/token/time budget and lifecycle history. Any pause/blocked state is cleared and `noProgressTurns` resets so the revised goal can continue; a goal already at a hard limit re-pauses on the next idle (use `/goal resume` for a fresh budget window). Ported from prevalentWare/opencode-goal-plugin's `update_goal_objective` tool, adapted to the marker-based command model.
Expand Down
Loading
Loading