Skip to content

Releases: andrejtonev/opencode-kasper

v1.2.0 — non-default agent prompt layouts, plugin overrides, and custom paths

Choose a tag to compare

@andrejtonev andrejtonev released this 24 Jun 06:32
de180b0

A feature release that hardens the resolver for opencode plugin ecosystems (notably oh-my-opencode), adds support for non-default agent prompt layouts and agents_md_paths, and makes the e2e suite deterministic and actually load-bearing. No breaking changes — the public plugin surface and kasper.jsonc / kasper key config are unchanged.

Highlights

  • Real oh-my-opencode integration. The e2e suite now loads the actual oh-my-opencode package via a file:// plugin entry, writes the real oh-my-openagent.json config, and verifies Kasper's write path lands the ## Kasper Inferred Instructions section in sisyphus.prompt_append while leaving build.prompt_append byte-identical.
  • Plugin-override entries located by name, not value (B1). Previously, with two agents sharing the same prompt text, the first one in insertion order won and Kasper silently edited the wrong agent. The writer now uses the canonical config key directly.
  • Display-name → canonical-key fallback. omo reports the agent's display name ("Sisyphus - ultraworker") as agentName, not the config key (sisyphus). The resolver's lookupAgentEntryWithFallback now handles this with exact → case-insensitive → "display name starts with key" (longest match wins), then threads the canonical key through every subsequent lookup.
  • All four opencode prompt-source shapes supported in plugin override files: inline string, {file:/abs/path}, {path:/abs/path}, and file:/// URIs (cross-platform via WHATWG URL + fileURLToPath).
  • Non-default AGENTS.md locations via a new agents_md_paths: string[] config field. Walks each entry for AGENTS.md then CLAUDE.md, falls back to a local walk-up from projectRoot, then to the global opencode dir / ~/.claude/CLAUDE.md. Honors OPENCODE_CONFIG_DIR and OPENCODE_DISABLE_CLAUDE_CODE. Backup directories are namespaced per resolved path.

Fixed

  • B1 regressionappendToPluginOverridePrompt now locates entries by canonical key, not by matching prompt/prompt_append value. (#6)
  • B2 regressionfindOverrideInDir now uses basename() from node:path so Windows \ separators are handled correctly and the standard opencode.json/.jsonc skip fires. (#6)
  • B3 regressionrunHealthCheck no longer hardcodes <cwd>/AGENTS.md; it runs the resolver first and uses the chosen path + reason. (#6)
  • B4 regression — Config reload now re-resolves the rules file and pushes new prompt_paths into the agent-prompt manager via new setResolvedPath() / setResolverInputs() methods. Edits to kasper.json take effect without a restart. (#6)
  • Display-name fallback false positives on hyphenated names — a follow-up to the resolver fix that requires a space (or end-of-string) after the matched key. A test creating code-quality-0b16404e no longer false-positive matches a global code-quality agent and silently routes the improvement to the wrong file. Closes a real production bug surfaced by tests/auto-update.test.ts. (#6)
  • appendToPluginOverridePrompt no longer reads actualKey as string | undefined — pre-existing TS errors in the resolver refactor are now resolved. npm run build and tsc --noEmit are clean for the first time. (#6)
  • file:// URIs handled cross-platform — replaced the string-based parser (which broke on Windows file:///C:/path) with WHATWG URL + fileURLToPath. Malformed URIs degrade gracefully to the config target. Fixes 7 Windows CI failures. (#8)
  • Failed scorings no longer poison a session — the runEvaluation() fallback path was unconditionally adding pending.sessionID to ctx.sessionsEvaluated even when the scorer returned a fallback card (model unavailable, LLM timeout, etc.). The runEvaluation() guard then short-circuited any future retry for the rest of the plugin lifetime. A failed scoring is transient; the user should be able to fix their model config and have the next poll pick up the session. The success path is now the only path that marks a session as evaluated. (#7)
  • bun run lint is now clean — removed unused imports in tests/e2e/edge-cases-inprocess.test.ts and applied biome formatting across tests/. prepublishOnly can now succeed. (#6)

Added

  • agents_md_paths: string[] in kasper.jsonc — explicit list of directories to check for AGENTS.md (then CLAUDE.md). Paths may be absolute, project-relative, or ~/.... Each path is checked top-to-bottom; first hit becomes the primary read/write target. If no entry has an existing file, the first entry's AGENTS.md is the write target and Kasper creates it on first write. (#6)
  • KASPER_E2E_SCORE_OVERRIDE=<float> test-only env var, read at the top of Scorer.evaluate() in src/scorer.ts. When set, returns a synthetic low-score card without calling the LLM, making e2e write-path tests deterministic. The override short-circuits the LLM call entirely; production users never set it. (#6)
  • {path:...} and file:// URI prompt definition support in plugin override files — the resolver now recognises all four opencode prompt shapes. file:// is the form used by oh-my-opencode to redirect built-in agent prompts. (#6, #8)
  • 33 tests for non-default agent prompt layouts (15 plugin_override resolver, 8 custom-paths resolver, 10 plugin_override manager). (#6)
  • 23 tests for the new AgentsMdResolver (21 resolver, 2 manager). (#6)
  • 11 in-process unit tests covering all four prompt-source shapes in tests/e2e/prompt-shapes.test.ts — runs in ~40 ms without spawning opencode. Verifies the resolver's classification, the inline→file promote path (materializeInlinePrompt), and the write-path file_uri/external_file replace semantics. (#6)
  • 5 in-process tests replacing the USELESS EC-2 and EC-7 from the original audit in tests/e2e/edge-cases-inprocess.test.ts — 4 unit tests of isKasperSession plus 1 disabled-mode integration test. Verified USEFUL via targeted mutations. (#6)
  • 5 tests for materializePluginOverrideToFile (previously unverified — the inline-prompt equivalent had 4 tests but the plugin-override version had none). (#6)
  • 11 B1–B4 regression tests in tests/b1..b4-regression.test.ts. (#6)
  • Artifact-verification report (tests/e2e/ARTIFACT-VERIFICATION.md) — proves via on-disk evidence that Kasper's tests actually produce the artifacts they claim. 11 rows covering omo + Kasper integration, AGENTS.md auto-apply, state.json lifecycle, different prompt definition types, different AGENTS.md locations, and different agent files. Stricter than the mutation audit: proves the test's claim about the side effect, not just that some code path was exercised. (#6)
  • Mutation audit (tests/e2e/MUTATION-AUDIT.md) — documents which tests are USEFUL (catch targeted mutations) vs USELESS (vacuous) vs SMOKE (test opencode, not Kasper). Was the basis for the EC-2 / EC-7 fix in this release. (#6)
  • src/path-utils.ts — shared expandTilde / fileExists / dirExists / candidateGlobalOpencodeDirs helpers extracted from the two resolvers. Removes ~50 lines of duplicated code and the underscore-prefix re-export code smell. (#6)

Changed

  • Test discipline: silent passes are gone. The mutation audit found multiple e2e tests that were vacuous (assertion could never fail) or relied on if (state) { log warn; return } paths. Every e2e describe block now uses waitForKasperLoaded() in beforeAll to fail loudly if the plugin symlink is .disabled, and assertions that previously only logged are now expect()s. The e2e suite is now 53/53 passing (was 1/9). (#6)
  • oh-my-opencode-live test is now a real e2e of the integration. Previously the test's omo config was a dead-drop file (.opencode/oh-my-opencode.json was the wrong basename after the package rename to oh-my-openagent) and the npm specifier oh-my-opencode never actually loaded in opencode serve (the serve command is instance: false — plugins only load when a per-project instance is created via opencode run --attach). The test now uses plugin: ["file:///path/to/dist/index.js"] to load the local omo install synchronously. (#6)
  • E2E suite is deterministic for the auto-apply pathKASPER_E2E_SCORE_OVERRIDE=0.3 is now set in the relevant beforeAll blocks; the 2 e2e tests that were previously flaky on the LLM judge are now deterministic. (#6)
  • cleanupE2EProject honors KASPER_E2E_KEEP_TMP=1 — every e2e test that produces a durable artifact leaves it on disk for inspection. (#6)
  • opencode >=1.15.13 e2e compatibility — HTTP Basic Auth, ?limit=N on /api/session, and the --attach flow are now handled by the harness. LOG_MAX_LINES 300 → 5000 to keep early lifecycle events visible. Scoring timeouts bumped (60 s → 120 s) for the slower LLM judges. (#6)
  • injectSection rewritten as an exhaustive switch on source.kind so the TS compiler catches missing cases when a new source kind is added. Idempotency check hardened to block-level match (split on blank lines, then compare) — the previous includes() check produced a false positive when an existing block was a prefix of the new one. (#6)

Notes

  • No public API changes. package.json main / types / exports are unchanged. The new KASPER_E2E_SCORE_OVERRIDE is opt-in via env var; existing deployments are unaffected.
  • Test counts: 542 unit tests pass (up from 470 in 1.1.1); the e2e suite adds another 78 tests (up from 32 in 1.1.0). 27 e2e tests are skipped when the opencode binary is not on $PATH (gated behind OPENCODE_E2E=1).
  • PRs: #6, #7, #8.
  • Full changelog: see CHANGELOG.md.