{"_id":"@amabush/runcommand","_rev":"6-994fd92c074ae244c1d005e35f127d83","name":"@amabush/runcommand","dist-tags":{"latest":"0.5.1"},"versions":{"0.2.0":{"name":"@amabush/runcommand","version":"0.2.0","keywords":["claude-code","statusline","status-line","run-command","dev-command"],"author":"Amir Abushanab","license":"MIT","_id":"@amabush/runcommand@0.2.0","maintainers":[{"name":"amabush","email":"amabush@outlook.com"}],"homepage":"https://amir-abushanab.github.io/runcommand/","bugs":{"url":"https://github.com/Amir-Abushanab/runcommand/issues"},"bin":{"runcommand":"bin/runcommand.mjs"},"dist":{"shasum":"8bc04fb075fe3d4ee858ce67376d6738df38d720","tarball":"https://registry.npmjs.org/@amabush/runcommand/-/runcommand-0.2.0.tgz","fileCount":4,"integrity":"sha512-TFceHKzljxC+x11slGLMi/gYR/Sn7l54lgdtiJxcJQvhgnhol5LYMh6adbrHTtIEhH/5iKnA6DI+MAJHTYn79w==","signatures":[{"sig":"MEQCICAFTNbm8ofnfw8bHnA0jcUzItKI/9ONq9NTz8+4fi/TAiBS3Gs3exfdCJMCNCAPc4D8k7hm8NbbeCZTj94UsaLM8A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":86440},"type":"module","engines":{"node":">=18"},"scripts":{"site":"pnpm --dir site dev","test":"node --test \"test/**/*.test.mjs\"","detect":"node bin/runcommand.mjs detect","release":"changeset publish","version":"changeset version","changeset":"changeset","statusline":"node bin/runcommand.mjs statusline"},"_npmUser":{"name":"amabush","email":"amabush@outlook.com"},"repository":{"url":"git+https://github.com/Amir-Abushanab/runcommand.git","type":"git"},"description":"Show each project's run/dev command in the Claude Code status bar. LLM-detected once, cached per-project.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@changesets/cli":"^3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/runcommand_0.2.0_1787228100155_0.32408242407325294","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@amabush/runcommand","version":"0.2.1","keywords":["claude-code","statusline","status-line","run-command","dev-command"],"author":"Amir Abushanab","license":"MIT","_id":"@amabush/runcommand@0.2.1","maintainers":[{"name":"amabush","email":"amabush@outlook.com"}],"homepage":"https://amir-abushanab.github.io/runcommand/","bugs":{"url":"https://github.com/Amir-Abushanab/runcommand/issues"},"bin":{"runcommand":"bin/runcommand.mjs"},"dist":{"shasum":"4563978d2e72560a7461c9be8c646ee85cd16960","tarball":"https://registry.npmjs.org/@amabush/runcommand/-/runcommand-0.2.1.tgz","fileCount":4,"integrity":"sha512-uutrdDj7kV5dWBmtHiM24kg9yxCMn2u8WpzGus/84S2fKPwCpf9TwPncLEAuTVzJguXebVSL3GvHxPUz5E/aTQ==","signatures":[{"sig":"MEQCIEUbo89b3FcKwiQNm+V2WhCy+OnDPrNkq3RiB1/3rp5gAiA3x0CXHoPQl24HukzAZWtp5xxr/wjXUzVCCioqlgSt8A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amabush%2fruncommand@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":86486},"type":"module","engines":{"node":">=18"},"scripts":{"site":"pnpm --dir site dev","test":"node --test \"test/**/*.test.mjs\"","detect":"node bin/runcommand.mjs detect","release":"changeset publish","version":"changeset version","changeset":"changeset","statusline":"node bin/runcommand.mjs statusline"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5e07dbe8-4317-4703-b7c2-8d6397c79c8b"}},"repository":{"url":"git+https://github.com/Amir-Abushanab/runcommand.git","type":"git"},"description":"Show each project's run/dev command in the Claude Code status bar. LLM-detected once, cached per-project.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@changesets/cli":"^3.0.0","@changesets/changelog-github":"^0.7.0"},"_npmOperationalInternal":{"tmp":"tmp/runcommand_0.2.1_1787235472900_0.044583053070928136","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@amabush/runcommand","version":"0.3.0","keywords":["claude-code","statusline","status-line","run-command","dev-command"],"author":"Amir Abushanab","license":"MIT","_id":"@amabush/runcommand@0.3.0","maintainers":[{"name":"amabush","email":"amabush@outlook.com"}],"homepage":"https://amir-abushanab.github.io/runcommand/","bugs":{"url":"https://github.com/Amir-Abushanab/runcommand/issues"},"bin":{"runcommand":"bin/runcommand.mjs"},"dist":{"shasum":"7c6f2948b03aa09575a2ae0193c2a2805d6a8364","tarball":"https://registry.npmjs.org/@amabush/runcommand/-/runcommand-0.3.0.tgz","fileCount":4,"integrity":"sha512-zZgE69G+pQRjPj4VqGDb91EW3INiPr/CtX23La38WLI0p1XjL3rhzBTT3EqM48pPVx2a+MC4lGZ7vfmz5w+YZQ==","signatures":[{"sig":"MEYCIQDylttRtj3kwW96B96JSxustgsuG5/JzNbwaUKDw4sTJAIhAMoS3beaAhaOXdoZXjzcaoBRuF76oqSAsX+lejI2TkCZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amabush%2fruncommand@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":87047},"type":"module","engines":{"node":">=18"},"scripts":{"site":"pnpm --dir site dev","test":"node --test \"test/**/*.test.mjs\"","detect":"node bin/runcommand.mjs detect","release":"changeset publish","version":"changeset version","changeset":"changeset","statusline":"node bin/runcommand.mjs statusline"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5e07dbe8-4317-4703-b7c2-8d6397c79c8b"}},"repository":{"url":"git+https://github.com/Amir-Abushanab/runcommand.git","type":"git"},"description":"Show each project's run/dev command in the Claude Code status bar. LLM-detected once, cached per-project.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@changesets/cli":"^3.0.0","@changesets/changelog-github":"^0.7.0"},"_npmOperationalInternal":{"tmp":"tmp/runcommand_0.3.0_1787236301634_0.9527184933567405","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@amabush/runcommand","version":"0.4.0","keywords":["claude-code","statusline","status-line","run-command","dev-command"],"author":"Amir Abushanab","license":"MIT","_id":"@amabush/runcommand@0.4.0","maintainers":[{"name":"amabush","email":"amabush@outlook.com"}],"homepage":"https://amir-abushanab.github.io/runcommand/","bugs":{"url":"https://github.com/Amir-Abushanab/runcommand/issues"},"bin":{"runcommand":"bin/runcommand.mjs"},"dist":{"shasum":"d3d51faa5121da273d47bf618ed79b801daef7eb","tarball":"https://registry.npmjs.org/@amabush/runcommand/-/runcommand-0.4.0.tgz","fileCount":4,"integrity":"sha512-hXRzVbI54nKHFyiAr1vSOWY2hUFNsoTifoLpTip0KI3jJL4SwHiayPPt3R7NYVLqiFnkJEYzb+IZ+BuCH5CDmQ==","signatures":[{"sig":"MEUCIQDqwXQXbAujlsmD3l5j09v9S4jK8Lj699iT3dVaMl1jswIgBHKvsOVdyvWnLNjQ5J74Rggf/idvu4wObRdRnKYpVSw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amabush%2fruncommand@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":89154},"type":"module","engines":{"node":">=18"},"scripts":{"site":"pnpm --dir site dev","test":"node --test \"test/**/*.test.mjs\"","detect":"node bin/runcommand.mjs detect","release":"node scripts/release.mjs","version":"changeset version","changeset":"changeset","statusline":"node bin/runcommand.mjs statusline"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5e07dbe8-4317-4703-b7c2-8d6397c79c8b"}},"repository":{"url":"git+https://github.com/Amir-Abushanab/runcommand.git","type":"git"},"description":"Show each project's run/dev command in the Claude Code status bar. LLM-detected once, cached per-project.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@changesets/cli":"^3.0.0","@changesets/changelog-github":"^0.7.0"},"_npmOperationalInternal":{"tmp":"tmp/runcommand_0.4.0_1787304617810_0.38872970650628114","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@amabush/runcommand","version":"0.5.0","keywords":["claude-code","statusline","status-line","run-command","dev-command"],"author":"Amir Abushanab","license":"MIT","_id":"@amabush/runcommand@0.5.0","maintainers":[{"name":"amabush","email":"amabush@outlook.com"}],"homepage":"https://amir-abushanab.github.io/runcommand/","bugs":{"url":"https://github.com/Amir-Abushanab/runcommand/issues"},"bin":{"runcommand":"bin/runcommand.mjs"},"dist":{"shasum":"04ef0bcfc9476fc222e13548d4e12b038de33d2d","tarball":"https://registry.npmjs.org/@amabush/runcommand/-/runcommand-0.5.0.tgz","fileCount":4,"integrity":"sha512-VnyOalJDrZ15AE7X6irz7p/RiifBpHRbzBZEE3NG4mDZ+zcJ6xtIhx0sD7sT2XM7ME/Q6fIJGOouQfl0xgOm/w==","signatures":[{"sig":"MEUCIE0VRg6x63/QuRnxuOhO9QgMoMmPUfXGvIbrgFHD8aBtAiEAyyXZokrgAb1gffcv1q7JNozkFcxLgtFEBS0QxwVaDXE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amabush%2fruncommand@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":95977},"type":"module","engines":{"node":">=18"},"scripts":{"site":"pnpm --dir site dev","test":"node --test \"test/**/*.test.mjs\"","detect":"node bin/runcommand.mjs detect","release":"node scripts/release.mjs","version":"changeset version","changeset":"changeset","statusline":"node bin/runcommand.mjs statusline"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5e07dbe8-4317-4703-b7c2-8d6397c79c8b"}},"repository":{"url":"git+https://github.com/Amir-Abushanab/runcommand.git","type":"git"},"description":"Show each project's run/dev command in the Claude Code status bar. LLM-detected once, cached per-project.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@changesets/cli":"^3.0.0","@changesets/changelog-github":"^0.7.0"},"_npmOperationalInternal":{"tmp":"tmp/runcommand_0.5.0_1787744598905_0.6728757975006954","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"@amabush/runcommand","version":"0.5.1","description":"Show each project's run/dev command in the Claude Code status bar. LLM-detected once, cached per-project.","author":"Amir Abushanab","license":"MIT","repository":{"type":"git","url":"git+https://github.com/Amir-Abushanab/runcommand.git"},"homepage":"https://amir-abushanab.github.io/runcommand/","bugs":{"url":"https://github.com/Amir-Abushanab/runcommand/issues"},"publishConfig":{"access":"public"},"type":"module","bin":{"runcommand":"bin/runcommand.mjs"},"engines":{"node":">=18"},"keywords":["claude-code","statusline","status-line","run-command","dev-command"],"devDependencies":{"@changesets/changelog-github":"^0.7.0","@changesets/cli":"^3.0.0"},"scripts":{"detect":"node bin/runcommand.mjs detect","statusline":"node bin/runcommand.mjs statusline","site":"pnpm --dir site dev","test":"node --test \"test/**/*.test.mjs\"","changeset":"changeset","version":"changeset version","release":"node scripts/release.mjs"},"_nodeVersion":"24.19.0","_id":"@amabush/runcommand@0.5.1","dist":{"integrity":"sha512-AVIpsXSYmqfUbrrMCoOnMu7LQ26KLIXdwyfTyXsKkeQKZBBJpqfwWM/ykf6+V7CWIG0H3iopj5V673/LMskDjA==","shasum":"3cc80698e4bd4fe29a4fefc43f492193f551065e","tarball":"https://registry.npmjs.org/@amabush/runcommand/-/runcommand-0.5.1.tgz","fileCount":4,"unpackedSize":100150,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@amabush%2fruncommand@0.5.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAkRTTJ17jE1Htk27O0NxhMX/kPjLaQ7E5qTGvHltuz2AiBQtGgaoT9I7+te4UtuOoj7FjvPhYx3Mf1bLEEXUHr2wQ=="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5e07dbe8-4317-4703-b7c2-8d6397c79c8b"}},"directories":{},"maintainers":[{"name":"amabush","email":"amabush@outlook.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/runcommand_0.5.1_1788364438673_0.887894616990065"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-20T12:15:00.068Z","modified":"2026-09-02T15:53:59.210Z","0.2.0":"2026-08-20T12:15:00.308Z","0.2.1":"2026-08-20T14:17:53.052Z","0.3.0":"2026-08-20T14:31:41.813Z","0.4.0":"2026-08-21T09:30:17.969Z","0.5.0":"2026-08-26T11:43:19.049Z","0.5.1":"2026-09-02T15:53:58.819Z"},"bugs":{"url":"https://github.com/Amir-Abushanab/runcommand/issues"},"author":"Amir Abushanab","license":"MIT","homepage":"https://amir-abushanab.github.io/runcommand/","keywords":["claude-code","statusline","status-line","run-command","dev-command"],"repository":{"type":"git","url":"git+https://github.com/Amir-Abushanab/runcommand.git"},"description":"Show each project's run/dev command in the Claude Code status bar. LLM-detected once, cached per-project.","maintainers":[{"name":"amabush","email":"amabush@outlook.com"}],"readme":"<p align=\"center\">\n  <img src=\"assets/logo.svg\" width=\"84\" height=\"84\" alt=\"runcommand\" />\n</p>\n\n<h1 align=\"center\">runcommand</h1>\n\n<p align=\"center\">\n  Your project's <strong>run command</strong> — and the localhost ports it's\n  serving — in your status bar and shell prompt.<br/>\n  LLM-detected once, cached, self-healing, and clickable.\n</p>\n\n**The problem:** you're juggling a dozen repos and every one starts differently —\n`pnpm dev` here, `yarn dev` there, `cargo run`, `docker compose up`… You open a\nproject and have to dig through `package.json` (again) just to remember how to run\nit. **runcommand** works it out and keeps it in front of you:\n\n<p align=\"center\">\n  <img src=\"assets/shot-statusbar.png\" width=\"720\" alt=\"runcommand in the Claude Code status bar: ▶ pnpm dev with clickable ports :3000 :5173, beneath the existing status line\" />\n</p>\n\nA headless LLM call (Claude Code's `claude -p` by default — or [any agent you\nconfigure](#detection-agent)) works the command out from your scripts, lockfile and\nmanifests, **caches it per project**, and only re-asks when a manifest changes — so the\nrender never waits on the model (cache hit ~50ms; a miss shows `▶ finding run command…`\nand detects in the background). It's a tiny, **dependency-free** Node CLI that speaks\nClaude Code's status-line contract, so the same answer drops into **starship**,\n**OpenCode**, **Qwen Code** and more, [alongside](#coexisting-with-another-status-line)\nwhatever already draws your status line.\n\n## Install\n\nOne command:\n\n```sh\nnpm i -g @amabush/runcommand && runcommand init\n```\n\n`init` detects the tools you actually have — Claude Code, Qwen Code, starship, tmux — and\nwires each one, backing up every file it touches and preserving any status line you already\nrun. `--dry-run` previews the lot without writing, `--yes` skips the prompts, and\n`runcommand uninstall` puts everything back.\n\nNeeds **Node ≥ 18** (already there if you run Claude Code) and an **AI CLI on your\n`PATH`** for detection — [`claude`](https://claude.com/claude-code) by default; OpenCode,\nGemini CLI, Qwen Code, Codex or [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)\nwork too. Developed on macOS/Linux; [Windows](#windows) is best-effort.\n\n<details>\n<summary>From a clone instead</summary>\n\n```sh\ngit clone https://github.com/Amir-Abushanab/runcommand.git ~/.runcommand && node ~/.runcommand/bin/runcommand.mjs init\n```\n\nRun from a checkout, `init` offers to put `runcommand` on your `PATH` (a symlink into\n`~/.local/bin`) and otherwise writes the absolute `node …/runcommand.mjs` invocation into\nyour configs — so keep the clone somewhere permanent. You can also skip `init` entirely\nand wire things up by hand: every section below is the manual equivalent of what it\nwrites. (Don't install with `npx` — it runs from a cache directory that disappears, so\n`init` refuses.)\n</details>\n\n## Claude Code\n\nAdd a `statusLine` to `~/.claude/settings.json`:\n\n```json\n{\n  \"statusLine\": {\n    \"type\": \"command\",\n    \"command\": \"runcommand statusline\",\n    \"padding\": 0\n  }\n}\n```\n\nIf you didn't link it, use the full path instead:\n`\"node /ABSOLUTE/PATH/TO/runcommand/bin/runcommand.mjs statusline\"`. That's it — the first\ntime you open each project the line reads `▶ finding run command…` for a few seconds, then\nflips to the real command and stays cached.\n\n### Coexisting with another status line\n\nClaude Code has exactly one `statusLine` slot, so to show **both** lines one has to render\nthe other. Set `RUNCOMMAND_BASE` to any command that prints a status line and runcommand\nrenders that first, then its own line beneath:\n\n```json\n\"command\": \"RUNCOMMAND_BASE='your existing status line command here' runcommand statusline\"\n```\n\nThe same stdin JSON is passed through, so anything that reads the standard contract just\nworks, and the other tool's config is never touched. (The reverse works too: if your other\ntool can chain a child status line, point *it* at `runcommand statusline`.)\n\n## Detection agent\n\nDetection is just a headless LLM call: runcommand hands an AI CLI your project's signals —\n`package.json` scripts, the package manager, the manifest list, a run hint from the README —\nand reads the command back. It tries agents in the table's order and uses the **first\nthat's installed and answers** — so uninstalling Claude Code silently falls through to whatever\nelse is on the machine, and you never *need* a specific agent. `RUNCOMMAND_AGENT` pins one\n(`opencode`) or sets the chain (`opencode,claude`). A broken agent falls through too; a\nclean \"no run command\" answer does not.\n\nA directory that only *holds* projects — `~/Code`, `~/src`, `~/work` — is never asked about.\nIt has no `.git` and no manifest of its own, and its subdirectories are separate repos, so\nruncommand recognizes it and renders nothing: no run command, and no ports (the ones listening\nbelong to the projects inside it, not to it). `runcommand ports --all` still lists them.\n\n| `RUNCOMMAND_AGENT` | Runs | Needs on `PATH` |\n| --- | --- | --- |\n| `claude` *(default)* | `claude -p \"<prompt>\" --model haiku` | [Claude Code](https://claude.com/claude-code) |\n| `opencode` | `opencode run \"<prompt>\"` | [OpenCode](https://opencode.ai) |\n| `gemini` | `gemini -p \"<prompt>\"` | [Gemini CLI](https://github.com/google-gemini/gemini-cli) |\n| `qwen` | `qwen -p \"<prompt>\"` | [Qwen Code](https://github.com/QwenLM/qwen-code) |\n| `codex` | `codex exec \"<prompt>\"` | [Codex CLI](https://github.com/openai/codex) |\n| `deepseek` | `dsh --profile headless \"<prompt>\"` | [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) |\n| `cursor` | `cursor-agent -p --output-format text \"<prompt>\"` | [Cursor CLI](https://cursor.com/docs/cli/headless) |\n| `crush` | `crush run -q \"<prompt>\"` | [Charm Crush](https://github.com/charmbracelet/crush) |\n| `amp` | `amp -x \"<prompt>\"` | [Amp](https://ampcode.com) |\n| `llm` | `llm \"<prompt>\"` | [llm](https://llm.datasette.io) |\n| `sgpt` | `sgpt \"<prompt>\"` | [Shell GPT](https://github.com/TheR1D/shell_gpt) |\n| `aider`† | `aider --yes --no-auto-commits --message \"<prompt>\"` | [Aider](https://aider.chat) |\n| `goose`† | `goose run --no-session -t \"<prompt>\"` | [goose](https://block.github.io/goose/) |\n| `copilot`† | `copilot -p \"<prompt>\"` | [Copilot CLI](https://docs.github.com/en/copilot/how-tos/copilot-cli) |\n| `ollama`\\* | `ollama run <model> \"<prompt>\"` | [Ollama](https://ollama.com) — local |\n\n<sub>† Agentic — `aider` edits files, `goose` runs tools, `copilot` is auth-gated — so they sit **last** in the default chain (reached only when nothing cleaner is installed, i.e. it genuinely is your agent), with side-effect-minimizing flags.<br/>\n\\* Never in the default chain, since `ollama run` pulls a multi-GB model on first use. Opt in with `RUNCOMMAND_AGENT=ollama RUNCOMMAND_MODEL=llama3.2`.</sub>\n\n`RUNCOMMAND_MODEL` overrides the model (`haiku` for `claude`, the agent's own default\notherwise; `amp`, `goose` and `deepseek` take no per-call model flag, so it's ignored\nthere — DeepSeek Harness picks its model from the booted profile). **`runcommand agents`** shows the resolved chain; `--probe` actually calls each\none to confirm it responds (installed ≠ authenticated).\n\n**Any other CLI?** Point `RUNCOMMAND_DETECT_CMD` at anything that takes a prompt and prints\nthe answer to stdout. The prompt is appended as the final argument, or substituted for a\n`{}` placeholder, so quoting is never your problem:\n\n```sh\nRUNCOMMAND_DETECT_CMD=\"my-llm --fast\"          # runs: my-llm --fast \"<prompt>\"\nRUNCOMMAND_DETECT_CMD=\"my-llm --prompt {} -q\"  # runs: my-llm --prompt \"<prompt>\" -q\n```\n\nIt only has to emit the command in `<cmd></cmd>` tags (the prompt says how); caching, ports\nand self-healing are unchanged. `RUNCOMMAND_AGENT_BIN` points at a binary that isn't on the\nstatus line's `PATH`.\n\n## Overrides (instant, no model call)\n\nTo pin a project's command yourself — for anything the model gets wrong, or to skip the\nmodel entirely — add a **`.runcommand`** file, one command per line:\n\n```\nmake serve PORT=8080\n```\n\n…or a **`Run:`** line to the project's `CLAUDE.md` (only `CLAUDE.md`, and only outside code\nfences, so documentation examples like this one don't count):\n\n```\nRun: docker compose up --build\n```\n\nOverrides win over the cache and cost nothing. **Commit the file** — everyone who\nclones the repo gets the pinned command.\n\n**Several services?** Prefix each with a `label:` and they render on one compact line, each\nin its own color (cycled from a colorblind-safe palette; tune with `RUNCOMMAND_COLORS`):\n\n```\nweb: pnpm dev:web\napi: pnpm dev:api\n```\n\n<p align=\"center\">\n  <img src=\"assets/shot-monorepo.png\" width=\"720\" alt=\"multiple run commands on one line — web: pnpm dev:web · api: pnpm dev:api\" />\n</p>\n\nDetection finds these on its own too — the model returns each genuinely-distinct\nlong-running service, and won't split one app into build/lint/test. A single-service\nproject stays a single unlabeled entry (`▶ pnpm dev`).\n\n## Live localhost ports\n\nAlongside the run command, the line shows the project's **currently running** localhost\nservers, as clickable links:\n\n```\n▶ pnpm dev   ◉ :3000 :5173\n```\n\nPorts come from `lsof` (`netstat -ano` on Windows) and are **scoped to the project** — only\nlisteners whose process runs inside the project directory show up, so postgres, docker and\nother repos' servers don't leak in. Ephemeral ports (`≥ 49152`) and debuggers (`9229`) are\nfiltered, and the scan is cached for 2.5s so it stays cheap on the hot path.\n`RUNCOMMAND_NO_PORTS=1` hides them entirely.\n\n**Clickability, per surface.** Two styles: **`compact`** (`:3000`, a short OSC 8\nhyperlink) is the default, and **`url`** (`http://localhost:3000` as visible text, which\nterminals auto-link on their own) is for surfaces that strip OSC 8. Which is which isn't a\nguess:\n\n| Surface | OSC 8 | Style | How we know |\n| --- | --- | --- | --- |\n| Claude Code status line | passes through | `compact` | [documented](https://code.claude.com/docs/en/statusline#clickable-links) — OSC 8 links are a supported status-line feature |\n| starship / Oh My Posh | passes through | `compact` | by construction: your terminal draws the prompt, so only its support matters |\n| **tmux** status bar | **stripped** | **`url`** | measured — tmux stores the escape verbatim, then emits none of it to the client |\n| Qwen Code | passes through | `compact` | measured — its TUI writes the full hyperlink to the terminal intact |\n| Zellij (zjstatus) | unverified | `url` | assume tmux-like until someone measures it |\n| OpenCode footer | n/a | n/a | the plugin reads `--json` and builds its own links via OpenTUI |\n\n`RUNCOMMAND_PORT_STYLE` overrides the default on any surface.\n\n## Other surfaces\n\nSame tool, different places to put the line. Of the terminal AI tools only Claude Code and\nQwen Code expose a config-based status line; for everything else, use an ambient surface.\n\n<p align=\"center\">\n  <img src=\"assets/shot-starship.png\" width=\"720\" alt=\"runcommand as a starship prompt tagline: ~/Code/principlestash.com on main  ▶ pnpm dev · :5173\" />\n</p>\n\n**starship** — exactly what `runcommand init` writes:\n\n```toml\n[custom.runcommand]\ncommand = \"runcommand promptline\"   # or: node /path/to/bin/runcommand.mjs promptline\nwhen = true\nformat = \"($output )\"\nshell = [\"bash\", \"--noprofile\", \"--norc\"]\nignore_timeout = true\n```\n\n`when = true` is not optional — starship skips a custom module that declares no run\ncondition, so without it the segment renders nothing and the command is never even\nspawned. `promptline` is non-blocking and prints nothing outside a project, so it decides\nfor itself when to disappear; `ignore_timeout` keeps a cold port scan under starship's\n500 ms cap. For a\nright-aligned tagline add `right_format = \"${custom.runcommand}\"`. The trailing space\ninside `($output )` is load-bearing — starship trims a module's output, so without it the\nnext segment renders as `:4321took 10s`.\n\n**Qwen Code** — it copied Claude Code's `statusLine` contract, so it drops straight into\n`~/.qwen/settings.json`:\n\n```json\n{ \"ui\": { \"statusLine\": {\n  \"type\": \"command\",\n  \"command\": \"runcommand statusline\"\n} } }\n```\n\n**Oh My Posh** — a `command` segment in a block of your theme (`properties` is being\nrenamed to `options` upstream):\n\n```json\n{ \"type\": \"command\", \"style\": \"plain\", \"template\": \" {{ .Output }} \",\n  \"properties\": { \"shell\": \"bash\", \"command\": \"runcommand promptline\" } }\n```\n\n**OpenCode** — no status-line command, but it loads TUI plugins: a ready-made one in\n[`integrations/opencode/`](integrations/opencode/) renders the command + ports into the\nfooter. `npm i @amabush/runcommand-opencode` inside `~/.config/opencode`, then point\n`tui.json` at the installed `dist/tui.js` by **path** — OpenCode resolves plugin\nspecifiers from your project, so a package name is never found. See its README.\n\n**Codex** — no command-backed status line yet\n([openai/codex#17827](https://github.com/openai/codex/issues/17827)). Its contract is Claude\nCode's, so when it ships it'll be a one-block write to `~/.codex/config.toml`.\n\n### Ambient surfaces\n\nSome tools — **aider**, **goose**, **Cursor**, **Codex**, GitHub **Copilot** — are\nfull-screen TUIs with no status-line hook, so runcommand can't render *inside* them. It\ndoesn't need to: an ambient surface shows the line *around* whatever's running (and so does\nthe shell prompt above, before you launch the tool).\n\n**tmux** — the command stays visible the whole time you're inside aider/goose/etc., and\ntracks the active pane's project. `runcommand init` wires this for you, appending with\n`set -ga` so your existing `status-right` is kept, not clobbered:\n\n```tmux\nset -ga status-right \" #(runcommand prompt -C '#{pane_current_path}')\"\n```\n\n`runcommand prompt` prints the plain command, and nothing in non-project dirs. Append live\nports with `#(runcommand ports --urls -C '#{pane_current_path}')` — `--urls` matters here,\nbecause tmux strips OSC 8 hyperlinks out of the status line, so the compact `:3000` form\nwould render as dead text.\n\n**Terminal title** — a universal fallback, from a zsh `precmd()` or bash `PROMPT_COMMAND`\n(some TUIs overwrite it):\n\n```sh\nprintf '\\033]0;%s\\007' \"$(runcommand prompt)\"\n```\n\n<details>\n<summary><b>Zellij</b> — needs the <code>zjstatus</code> plugin</summary>\n\nZellij's built-in status bar can't run commands, so this needs\n[`zjstatus`](https://github.com/dj95/zjstatus) (download `zjstatus.wasm` into\n`~/.config/zellij/plugins/`). In your layout:\n\n```kdl\npane size=1 borderless=true {\n    plugin location=\"file:~/.config/zellij/plugins/zjstatus.wasm\" {\n        format_left                 \"{command_runcommand}\"\n        command_runcommand_command  \"bash -lc 'runcommand prompt'\"\n        command_runcommand_format   \"{stdout}\"\n        command_runcommand_interval \"5\"\n    }\n}\n```\n\n`runcommand init` detects zellij and prints these steps (it won't download the plugin for\nyou). Note zjstatus runs the command from the session's directory, not per-pane.\n</details>\n\n## Commands\n\n```sh\nruncommand statusline        # render run command + live ports (Claude Code calls this)\nruncommand prompt [dir]      # plain run command for a shell prompt (non-blocking)\nruncommand promptline [dir]  # run command + clickable ports, styled (for starship)\nruncommand ports [dir]       # this project's live localhost ports (--links --json --all)\nruncommand detect [dir]      # detect now (asks the configured agent), print + cache\nruncommand get [dir]         # print the cached command (detect if missing)\nruncommand refresh [dir]     # re-detect and overwrite the cache\nruncommand path [dir]        # print the cache file path\nruncommand init              # wire runcommand into your installed tools (--yes --dry-run --surfaces)\nruncommand uninstall         # remove that wiring (restores what was there; --dry-run)\nruncommand agents            # show the detection chain — which agents are installed (--probe to test each)\n```\n\n`dir` defaults to the current directory; the project root is the nearest `package.json` /\nmanifest / `.git`, walking up.\n\n**Wrong guess?** Tell it what's wrong with `--hint`:\n\n```sh\nruncommand refresh --hint \"it's picking the API; I want the web dev server\"\n# -> pnpm run dev:web\n```\n\nThe note is **saved with the project** and reused on every later re-detect, so a future\nmanifest change won't regress to the wrong answer. Wipe it with `--clear-hint`; for a hard,\nexact pin use an [override](#overrides-instant-no-model-call).\n\n## Config (env vars)\n\n| Var | Default | Purpose |\n| --- | --- | --- |\n| `RUNCOMMAND_AGENT` | *(auto)* | Detection agent priority list, first installed wins — e.g. `opencode,claude` |\n| `RUNCOMMAND_DETECT_CMD` | – | Full custom detect command; the prompt is appended, or put where `{}` appears |\n| `RUNCOMMAND_AGENT_BIN` | – | Path to the agent binary (else auto-resolved) |\n| `RUNCOMMAND_MODEL` | `haiku`\\* | Detection model (\\*`haiku` for `claude`; the agent's own default otherwise) |\n| `RUNCOMMAND_BASE` | – | Another status-line command to render above ours |\n| `RUNCOMMAND_ICON` | `▶` | Leading glyph (set empty to drop it) |\n| `RUNCOMMAND_LABEL` | – | Text before the command, e.g. `run: ` |\n| `RUNCOMMAND_COLORS` | `36,33,35,34,32` | Per-service colors (ANSI codes), cycled by position; one code = one color |\n| `RUNCOMMAND_TTL_MS` | `0` (off) | Re-detect after this long even if the manifest is unchanged |\n| `RUNCOMMAND_PORT_STYLE` | `compact` | `compact` = `:PORT` as an OSC 8 link; `url` = full visible `http://localhost:PORT`, for surfaces that strip OSC 8 |\n| `RUNCOMMAND_NO_PORTS` | – | Hide live ports in the status line |\n| `RUNCOMMAND_PORTS_TTL_MS` | `2500` | How long a port scan is cached (ms) |\n| `RUNCOMMAND_IGNORE_PORTS` | `9229,9230` | Ports to never show (e.g. debuggers) |\n| `RUNCOMMAND_CLAUDE` | – | Path to the `claude` binary (alias for `RUNCOMMAND_AGENT_BIN` when the agent is `claude`) |\n| `NO_COLOR` | – | Disable ANSI color |\n\n## The cache\n\n`$XDG_CACHE_HOME/runcommand/` (falls back to `~/.cache/runcommand/`, or\n`%LOCALAPPDATA%\\runcommand` on Windows), one JSON file per project.\n\nThere's **no time-based expiry** — a detected command is cached indefinitely and\ninvalidated by **content, not time**. Every render hashes the project's signals\n(`package.json` scripts and `packageManager`, the lockfile and manifest list, the\n*contents* of command-defining manifests like `Makefile`, `Justfile`, `docker-compose*`,\n`Cargo.toml`, `pyproject.toml`, and any override) and re-detects in the background the\nmoment they change — so when an agent, or you, edits one, the command updates itself.\n`RUNCOMMAND_TTL_MS` adds a fixed-age re-detect on top.\n\nCache files carry a version, and a build that doesn't recognise it treats the file as a\nplain miss. The config blocks `init` writes are versioned too, so **re-run `runcommand\ninit` after upgrading**: it offers an in-place refresh of any stale block, and never\ntouches a config you wired by hand. Full rules in [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Windows\n\nWindows is wired up but **has not been run on a real Windows machine** — treat it as\nbest-effort and please open an issue with what breaks. Rendering is the easy half: Windows\nTerminal handles ANSI styling and OSC 8, so the line and its clickable ports look the same\nas anywhere else.\n\nThe one real compromise is port scoping: Windows exposes no process working directory to a\nplain CLI, so runcommand matches the project path against each listener's **command line**\ninstead. That covers the common case (a dev server launched from the project carries the\npath in its argv) and is deliberately biased toward showing *nothing* rather than another\nproject's port — `runcommand ports --all` ignores scoping entirely.\n\n<details>\n<summary>What else differs underneath</summary>\n\n| | macOS / Linux | Windows |\n|---|---|---|\n| Listener scan | `lsof -nP -iTCP -sTCP:LISTEN` | `netstat -ano -p TCP` |\n| Scoped to project by | process **cwd** (`lsof -a -d cwd`) | process **command line** (`Get-CimInstance Win32_Process`) |\n| Agent lookup | exact filename on `PATH` | `PATHEXT` probing, so `claude` finds `claude.cmd` |\n| starship `shell` | pinned to `bash --noprofile --norc` | omitted; starship's `cmd /C` default |\n| PATH install | symlink into `~/.local/bin` | `npm i -g <repo>` (symlinks need Developer Mode) |\n| Cache dir | `~/.cache/runcommand` | `%LOCALAPPDATA%\\runcommand` |\n\n`init` won't offer tmux on Windows (no `tmux` binary, no `~/.tmux.conf`); Claude Code and\nstarship wiring are unchanged, since both keep their config under `~/`.\n</details>\n\n## Development\n\n`pnpm test` runs the suite — on Linux, macOS **and** Windows in CI, which is the only place\nthe Windows code actually executes. The [`site/`](site/) directory is an Astro showcase\n(`pnpm site`). Release process and the versioning rules for generated config blocks are in\n[CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\nMIT\n","readmeFilename":""}