Skip to content

Latest commit

 

History

History
142 lines (104 loc) · 9.13 KB

File metadata and controls

142 lines (104 loc) · 9.13 KB

AGENTS.md — opencode-bgrun

Background job runner for AI coding agents with live session wake-to-act on OpenCode. When a long-running command finishes, it wakes the exact originating agent session (not just a desktop notification) so the agent can proactively continue. Distributed as an OpenCode plugin + optional human shell CLIs.

Quick Start

# Verify everything works
npm test          # smoke tests (node plugin/bgrun-wake.test.js) — expect 39/39 pass
npm run lint      # shellcheck + node --check — expect exit 0
npm pack --dry-run  # verify tarball contents (expect 14 files, @stablekernel/opencode-bgrun@0.1.2)

Project Structure

bin/               # Shell CLI scripts (bgrun, bgstatus, bgtail, bgclean, _bgrunner.sh)
plugin/
  bgrun-wake.js    # OpenCode plugin — registers bgrun + bgclean MCP tools, runs the wake poller
  bgrun-wake.test.js  # Standalone smoke tests (39 tests, no npm deps in runner)
skill/run-bg/SKILL.md  # Agent-facing skill for consuming this tool
install.sh         # Dev/clone install: symlinks bin+plugin+skill + --cli-only mode
uninstall.sh       # Removes install.sh-created symlinks + --cli-only mode
CHANGELOG.md       # Keep-a-Changelog format, backfilled through v0.1.2
.github/workflows/
  ci.yml           # Lint+test on push/PR — job named 'lint-test' (MUST stay that name)
  lint-test.yml    # Reusable workflow_call CI gate (for release.yml to depend on)
  release.yml      # npm publish pipeline: triggered by GitHub Release or workflow_dispatch dry-run

Architecture

Two separate audiences, two separate paths:

Audience Path Wake feature
AI agent Calls the bgrun TOOL registered by the plugin ✅ Wakes the exact originating session via promptAsync
Human (shell) Runs bgrun/bgstatus/bgtail/bgclean directly ❌ Desktop notification only (notify-only)

How the wake works (Option B):

  1. Plugin's bgrun tool captures context.sessionID at call time.
  2. Calls bin/bgrun -s <sessionID> -- sh -c "<command>" — shells out, writes .run/<job>.session atomically BEFORE spawn.
  3. _bgrunner.sh runs the command detached, writes .run/<job>.notify on completion.
  4. Plugin's poller (1s interval) detects .notify, atomically claims it (rename → .notified), reads .session, calls client.session.promptAsync() to wake that exact session.

Key file: plugin/bgrun-wake.js

  • loadSDK() cascade: (a) bare import gated on typeof mod.tool === 'function', (b) fallback to <OPENCODE_CONFIG_DIR>/node_modules/@opencode-ai/plugin/dist/index.js, (c) loud console.error (never silent).
  • bin/bgrun resolved by absolute path from plugin file (line ~144: path.resolve(path.dirname(_pluginRealPath), '..', 'bin', 'bgrun')). Never uses $PATH. Works from any install location.

Distribution

Canonical install (npm):

{ "plugin": ["@stablekernel/opencode-bgrun@0.1.2"] }

Human CLI on PATH (optional, notify-only):

# From a clone:
./install.sh --cli-only

# Without a clone:
npm i -g @stablekernel/opencode-bgrun   # bin map puts bgrun/bgstatus/bgtail/bgclean on PATH

Git State & Versioning

70364da  HEAD, main, origin/main  — docs: update AGENTS.md and uninstall.sh to reflect npm as canonical install
e5f5601  ci: add npm release pipeline
0f0ed39  tag: v0.1.2              — chore: release v0.1.2 (scoped npm, install scripts shipped)
58e1938  tag: v0.1.1              — chore: release v0.1.1 (--cli-only install mode)
...      tag: v0.1.0              — initial release

Working tree is clean — no uncommitted changes as of 2026-07-31.

CI / CD

  • ci.yml runs lint-test on every push to main and every PR. The job is named lint-testdo not rename it (it is the required status check on the branch-protected main; renaming silently disables protection).
  • lint-test.yml is a reusable workflow_call copy of the same gate used by release.yml. It exists ONLY to give release.yml a CI dependency without breaking the ci.yml check name. Do not rename its job either.
  • release.yml fires on GitHub Release published. Runs verify (calls lint-test.yml) then publish (OIDC trusted publishing, provenance, version-consistency guard). Also accepts workflow_dispatch with dry-run: true (default) for safe dry-run testing.

Branch Protection (main)

Rule Setting
Required status check lint-test (strict, up-to-date)
PR reviews 1 approval, stale reviews dismissed
Signed commits required
Force-push blocked
Branch deletion blocked
Enforce for admins false — admins can bypass

Admin bypass is intentionally enabled (Lloyd's call). Admins can push directly to main.

Signing

All commits MUST be signed (commit.gpgsign=true, key 824AA8A544E8AB33, lloyd.engebretsen@stablekernel.com). Tags are signed annotated (git tag -s).

npm Auth

  • Public npm (registry.npmjs.org): authed as lloydsk (SK account, linked to GitHub). The repo .npmrc pins public registry so publish always targets npm, not CFA Artifactory.
  • CFA Artifactory: global ~/.npmrccfa.jfrog.io. This is the CFA default for other projects — irrelevant here.
  • To publish from this repo dir: cd ~/sk/opencode-bgrun && npm publish --access public (routes to public npm as lloydsk automatically).

After First Publish — Remaining Work (in order)

  1. Wire OIDC trusted publisher on npmjs.com — done. Trusted publisher wired: repo stablekernel/opencode-bgrun, workflow release.yml. default_workflow_permissions set to read on repo actions settings.

  2. Dogfood the npm install — done. Plugin confirmed loading from ~/.cache/opencode/packages/@stablekernel/opencode-bgrun@0.1.2/... after updating ~/.config/opencode/opencode.json to "@stablekernel/opencode-bgrun@0.1.2".

  3. PR3: docs reconciliation — done. Merged as PR #1 (commit 70364da) on 2026-07-31. Stripped all git-install (@github:) references from README, SKILL.md, install.sh; updated cache-path examples to npm cache layout; documented npm i -g @stablekernel/opencode-bgrun as recommended human CLI path.

  4. Future: add Dependabot (.github/dependabot.yml) for github-actions SHA updates and npm deps. Optional but recommended per the release pipeline design.

Critical DO NOTs

  1. NEVER rename the lint-test job in ci.yml — it is the required status check; renaming silently breaks branch protection.
  2. NEVER commit secrets — no .env, no NPM_TOKEN in code. Use OIDC trusted publishing.
  3. NEVER publish a version that doesn't have a matching signed git tag — the release.yml version-consistency guard enforces this, but also enforce it manually.
  4. NEVER modify generated files in node_modules/.
  5. NEVER force-push main — use admin bypass via normal git push (bypass is already enabled for admins).
  6. NEVER add a user-facing feature without updating both README.md and skill/run-bg/SKILL.md — README is the human reference; SKILL.md is what agents read. Both must stay in sync with the implementation. Shell commands, MCP tools, env vars, flags, and default values all count. Internal implementation details (slug format, sidecar file structure) do not need to be documented.

Key Design Decisions (don't relitigate without reading these)

  • Option B architecture: plugin captures sessionID at tool-call time → passes via -s flag to the shell script → written to .session sidecar atomically before spawn. Poller reads .session for exact-session routing. This was chosen over alternatives (auto-track via chat.message, session.list() polling) because it's the only approach that correctly identifies the originating session when multiple sessions run in the same directory.
  • install.sh kept for dev/clone pathinstall.sh is a dev tool; npm bin map handles PATH for npm users; they are complementary, not competing.
  • Repo visibility flipped to public (2026-07-30) — security audit confirmed clean (no secrets, no internal refs in history). Required for curl|sh one-liner and npm publish without auth friction.
  • Admin bypass on branch protection — enables direct-to-main commits for releases and admin operations without opening a PR, while keeping the protection for the ~26 write-holders.
  • OIDC trusted publishing over NPM_TOKEN — no long-lived secret; provenance is automatic; NPM_TOKEN is documented as a commented fallback in release.yml.

Useful Context Files

  • .opencode/wip/release-pipeline-design.md — full release pipeline design (architect doc, covers trigger model, OIDC, lockstep, install.sh gap decision, PR sequence, human action table)
  • .opencode/wip/retro-2026-07-29-option-b-bgrun-tool.md — Option B design rationale and the SDK-resolution bug history
  • .opencode/wip/retro-2026-07-30-git-install-wake-validation.md — git-install wake validation + PATH gap discovery