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.
- Repo:
stablekernel/opencode-bgrun(public) — https://github.com/stablekernel/opencode-bgrun - npm package:
@stablekernel/opencode-bgrun(scoped, public npm — published ✅) - Current version:
0.1.2(tagged, published on npm)
# 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)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
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):
- Plugin's
bgruntool capturescontext.sessionIDat call time. - Calls
bin/bgrun -s <sessionID> -- sh -c "<command>"— shells out, writes.run/<job>.sessionatomically BEFORE spawn. _bgrunner.shruns the command detached, writes.run/<job>.notifyon completion.- Plugin's poller (1s interval) detects
.notify, atomically claims it (rename →.notified), reads.session, callsclient.session.promptAsync()to wake that exact session.
Key file: plugin/bgrun-wake.js
loadSDK()cascade: (a) bare import gated ontypeof mod.tool === 'function', (b) fallback to<OPENCODE_CONFIG_DIR>/node_modules/@opencode-ai/plugin/dist/index.js, (c) loudconsole.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.
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 PATH70364da 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.ymlrunslint-teston every push tomainand every PR. The job is namedlint-test— do not rename it (it is the required status check on the branch-protectedmain; renaming silently disables protection).lint-test.ymlis a reusableworkflow_callcopy of the same gate used byrelease.yml. It exists ONLY to giverelease.ymla CI dependency without breaking theci.ymlcheck name. Do not rename its job either.release.ymlfires on GitHub Release published. Runsverify(calls lint-test.yml) thenpublish(OIDC trusted publishing, provenance, version-consistency guard). Also acceptsworkflow_dispatchwithdry-run: true(default) for safe dry-run testing.
| 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.
All commits MUST be signed (commit.gpgsign=true, key 824AA8A544E8AB33, lloyd.engebretsen@stablekernel.com). Tags are signed annotated (git tag -s).
- Public npm (
registry.npmjs.org): authed aslloydsk(SK account, linked to GitHub). The repo.npmrcpins public registry so publish always targets npm, not CFA Artifactory. - CFA Artifactory: global
~/.npmrc→cfa.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 aslloydskautomatically).
-
✅ Wire OIDC trusted publisher on npmjs.com — done. Trusted publisher wired: repo
stablekernel/opencode-bgrun, workflowrelease.yml.default_workflow_permissionsset toreadon repo actions settings. -
✅ Dogfood the npm install — done. Plugin confirmed loading from
~/.cache/opencode/packages/@stablekernel/opencode-bgrun@0.1.2/...after updating~/.config/opencode/opencode.jsonto"@stablekernel/opencode-bgrun@0.1.2". -
✅ 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; documentednpm i -g @stablekernel/opencode-bgrunas recommended human CLI path. -
Future: add Dependabot (
.github/dependabot.yml) for github-actions SHA updates and npm deps. Optional but recommended per the release pipeline design.
- NEVER rename the
lint-testjob inci.yml— it is the required status check; renaming silently breaks branch protection. - NEVER commit secrets — no
.env, no NPM_TOKEN in code. Use OIDC trusted publishing. - NEVER publish a version that doesn't have a matching signed git tag — the
release.ymlversion-consistency guard enforces this, but also enforce it manually. - NEVER modify generated files in
node_modules/. - NEVER force-push
main— use admin bypass via normalgit push(bypass is already enabled for admins). - NEVER add a user-facing feature without updating both
README.mdandskill/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.
- Option B architecture: plugin captures sessionID at tool-call time → passes via
-sflag to the shell script → written to.sessionsidecar atomically before spawn. Poller reads.sessionfor exact-session routing. This was chosen over alternatives (auto-track viachat.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.shkept for dev/clone path —install.shis a dev tool; npmbinmap 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|shone-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_TOKENis documented as a commented fallback inrelease.yml.
.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