{"id":"add-clidash","name":"add-clidash","summary":"Add clidash — 依存性ゼロ、読み取り専用のウェブダッシュボードで、ランタイム時にリソースをJSONとしてリストアップする任意のCLIからタブやテーブルを派生させます。","body":"# /add-clidash — CLI-derived read-only dashboard\n\nclidash is a small, read-only web dashboard. You point it at any CLI that can\nlist resources as JSON (NanoClaw's `ncl`, `docker`, `kubectl`, …) and it builds\nthe dashboard at runtime: one tab per resource, a generic table over whatever\ncolumns the rows have. A new `ncl` resource becomes a new tab and a new column\nbecomes a new table column with **zero code changes**.\n\nIt ships pre-wired for NanoClaw's `ncl` CLI and adds three NanoClaw-aware\npanels driven entirely by config:\n\n- **Agents overview** — status cards joining groups + sessions + messaging\n  groups + wirings (green <15m / amber <2h / red older).\n- **Activity** — per-session inbound/outbound message totals and a daily series,\n  read directly from the session DBs (`ncl` has no messages resource).\n- **Logs** — last N lines of allowlisted host log files.\n- **Files** — a read-only viewer for group skills, `CLAUDE.md`, and profiles.\n\n## Why it's safe\n\nclidash is **read-only by construction**: the server can only `execFile` the\nargv templates in its config. `{resource}` is the sole substitution and is\nallowlist-validated against the discovered/static resource set before exec —\nnever a shell, no free-form input reaches argv. There is no auth; **the network\nis the auth boundary** — it binds `127.0.0.1` by default. Only ever bind a\nprivate interface (e.g. a tailnet IP), never a public one.\n\nIt's distinct from `/add-dashboard` (which pushes JSON snapshots to a separate\n`@nanoco/nanoclaw-dashboard` npm package): clidash has **zero dependencies**, no\nbuild step, no push pipeline, and no edits to NanoClaw source — it just reads\n`ncl` and the session DBs.\n\n## Steps\n\n### 1. Copy the tool into place\n\nclidash is fully self-contained — copy the whole directory in:\n\n`tools/` is not a standard NanoClaw directory and `cp -R` won't create it, so\nmake it first:\n\n```bash\nmkdir -p tools\ncp -R .claude/skills/add-clidash/add/tools/clidash tools/clidash\n```\n\nThat is the only file change this skill makes. Nothing in NanoClaw `src/` is\ntouched, no dependency is added.\n\n### 2. Create the config\n\nThe example config is pre-wired for NanoClaw with paths relative to the repo\nroot, so it works as-is when you run clidash from `tools/clidash/`:\n\n```bash\ncd tools/clidash\ncp clidash.config.example.json clidash.config.json\n```\n\n`clidash.config.json` is your local config — add it to `.gitignore` if you\ndon't want to commit install-specific paths:\n\n```bash\necho 'tools/clidash/clidash.config.json' >> ../../.gitignore\n```\n\nThe example assumes `ncl` is built at `bin/ncl`. If `bin/ncl` doesn't exist,\nbuild it first (`pnpm run build`) or point `clis.ncl.bin` at the right path.\n\n### 3. Test\n\nTests use a stub CLI — no real `ncl` or `docker` needed:\n\n```bash\nnpm test\n```\n\nAll tests should pass (Node ≥ 22.5, `node:test`, zero dependencies).\n\n### 4. Run and verify\n\n```bash\nnode server.js          # serves http://127.0.0.1:4690\n```\n\nIn another shell, confirm it's live and that `ncl` discovery worked:\n\n```bash\ncurl -s http://127.0.0.1:4690/api/clis | head -c 400      # CLIs + discovered resources\ncurl -s http://127.0.0.1:4690/api/r/ncl/groups | head -c 400   # a real resource table\n```\n\nThen open `http://127.0.0.1:4690/` in a browser. You should see the Agents\noverview plus a tab per `ncl` resource.\n\n### 5. (Optional) Run as a service\n\nclidash binds `127.0.0.1` by default. To reach it from other devices, bind a\nprivate (e.g. tailnet) IP via the `BIND` env var or `bind` in config — never a\npublic interface.\n\n```ini\n# ~/.config/systemd/user/clidash.service   (Linux)\n[Unit]\nDescription=clidash read-only CLI dashboard\n\n[Service]\nWorkingDirectory=%h/nanoclaw/tools/clidash\nExecStart=/usr/bin/node %h/nanoclaw/tools/clidash/server.js\nEnvironment=BIND=127.0.0.1\nRestart=on-failure\n\n[Install]\nWantedBy=default.target\n```\n\n```bash\nsystemctl --user enable --now clidash\n```\n\nOn macOS, wrap `node server.js` (with `WorkingDirectory` = `tools/clidash`) in a\nlaunchd plist the same way the main NanoClaw service is configured.\n\n## Configuration reference\n\n`clidash.config.json` keys (see `tools/clidash/README.md` and\n`clidash.config.example.json` for the full shape):\n\n| Key | Purpose |\n|-----|---------|\n| `port`, `bind`, `refreshSeconds` | server bind + UI auto-refresh cadence |\n| `clis.<name>.bin` / `cwd` / `env` | how to invoke the CLI (`bin` is relative to `cwd`) |\n| `clis.<name>.discover` or `resources` | runtime discovery (`ncl help`) vs a static resource list |\n| `clis.<name>.list` | argv template; `{resource}` is the only substitution |\n| `clis.<name>.output` | `json` or `jsonlines` (docker/kubectl style) |\n| `clis.<name>.unwrap` | dot-path into a response envelope (e.g. `data`) |\n| `clis.<name>.enrich`/`badges`/`summary` | table decorations (ID→name joins, status colors, summary cards) |\n| `activity` | `sessionsRoot` + `days` for the message-activity charts |\n| `logs` | `dir`, `tailLines`, and an allowlist of `files` to tail |\n| `docs` | file viewer: `root`, a `deny` glob list, and `collections` of glob patterns |\n\nAdding a second CLI is config-only — e.g. `docker` is included as a `jsonlines`\nexample. View plugins (`views/<cli>-<view>.js`) are the only per-CLI code and\nare optional.\n\n## Troubleshooting\n\n- **`ENOENT` / config not found** — run from `tools/clidash/` and make sure you\n  copied `clidash.config.example.json` to `clidash.config.json` (step 2), or set\n  `CLIDASH_CONFIG=/abs/path.json`.\n- **No `ncl` resources / discovery empty** — `bin/ncl` isn't built or the path\n  is wrong. Build it (`pnpm run build`) or fix `clis.ncl.bin`.\n- **docker tab errors** — the docker daemon isn't running, or remove the\n  `docker` CLI from config if you don't need it.\n- **Can't reach it from another device** — it binds `127.0.0.1`; set\n  `BIND=<private-ip>` (tailnet), never a public interface.\n- **Empty Activity/Logs/Files** — check that `activity.sessionsRoot`,\n  `logs.dir`, and `docs.root` resolve to your NanoClaw root (relative to where\n  you launch `node server.js`).\n\n## Removal\n\nSee [REMOVE.md](REMOVE.md).","author":"@nanocoai","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/nanocoai/nanoclaw/tree/main/.claude/skills/add-clidash","license":"MIT","category":"devops","lang":"en","tokens":1736,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"add/tools/clidash/activity.js","size":3624,"sha256":"d6895fad3ec58f8f5db1d6b829b4f9869e325e7923d7a083f84222f931ce6fdd"},{"path":"add/tools/clidash/clidash.config.example.json","size":3332,"sha256":"8cbd5cdfe6d8eff600c1e010f0d509a3cd893896e8be6a45f67db39a8a19e1f2"},{"path":"add/tools/clidash/docs.js","size":3826,"sha256":"7711e394cd3e42ec3186880fe77a7aa0e621324e31cc438ba92790170f9f6270"},{"path":"add/tools/clidash/logs.js","size":667,"sha256":"b46f52200873f4abd013d34be5a52c8a14e86181af768b26bf71b3129645f0ef"},{"path":"add/tools/clidash/package.json","size":347,"sha256":"cf99a9de0adf436edb6f5f7ebbf65152f7185aaeda7914f4a480b89392175671"},{"path":"add/tools/clidash/parsers.js","size":3401,"sha256":"696f8876f95251c1b35e9e17ab2775849ef5ed29f681331b5416a1f389fefbb7"},{"path":"add/tools/clidash/public/app.js","size":33414,"sha256":"333d5b6053c2cd073487480154b0d654046e19aba7cb214278ac384196dacf10"},{"path":"add/tools/clidash/public/index.html","size":1921,"sha256":"0bc460bd174c1ad626682a52fef7ab4fdb604ae9840f55fc897980de41728efd"},{"path":"add/tools/clidash/public/md.js","size":2765,"sha256":"2c3447ee950d674d7d5fce1a2f54d341ce3d4e2293753f3289811d9a255564e1"},{"path":"add/tools/clidash/public/site.webmanifest","size":298,"sha256":"5bd622c9040378fd901d883a4eade059a4be1caf3cfe50c329b30f5546012f4f"},{"path":"add/tools/clidash/public/style.css","size":18475,"sha256":"8cbdf9da333a5a84a9c97053a6b50e6ae1b0946798858ab5e4b6abf08710e56d"},{"path":"add/tools/clidash/README.md","size":4146,"sha256":"7401a7def51696fe4a325484400a95f631a5eb4ef5d99f2b799d384f2ca6a998"},{"path":"add/tools/clidash/server.js","size":17572,"sha256":"b1e9ccf62b57434a6212105577fbee78bec6badd478e556aa47044842884c8a1"},{"path":"add/tools/clidash/test/activity-server.test.js","size":2323,"sha256":"6da2087e172debe0a39794951de26d92baf5bf0de49cb476aad06640cffe8353"},{"path":"add/tools/clidash/test/activity.test.js","size":3899,"sha256":"6ca7929c644ae31886b0bf5d5c95d3e981ae20feb5ff59e7f5d7436189136b33"},{"path":"add/tools/clidash/test/cmd.test.js","size":3517,"sha256":"1e0fd43caa4e9061541a66bfa21e1e61297371633de1d369c025fe1c1d53846d"},{"path":"add/tools/clidash/test/css.test.js","size":991,"sha256":"88809e63f550af390e985fac0b9dbdb48c32a348d0deedda20af5f86198fa332"},{"path":"add/tools/clidash/test/docs-server.test.js","size":4098,"sha256":"cd0908d8ef6df3acf10c2edc3e80a9a74d8e70f06236c4712cbd8abb3f07bdd4"},{"path":"add/tools/clidash/test/docs.test.js","size":4539,"sha256":"4ba678647b2f4be17025e0e069ae9b46f7dd5145c7aef940f5136602e1083099"},{"path":"add/tools/clidash/test/fixtures/ncl-help.txt","size":4155,"sha256":"cbf98f4d2d0cc0740ebf79141e365b26cfeae1a38cd18c5bb4611a2e8d7c1a22"},{"path":"add/tools/clidash/test/fixtures/stub-cli.js","size":1875,"sha256":"68cdd661132ff660768207a5c763ac4ac71e9f3bdcad81adcbd46933ec96ef03"},{"path":"add/tools/clidash/test/help.test.js","size":2233,"sha256":"9735270611a95130fe5c9cedc26588a0a09eda78fd95b975d61c44cb233e5b68"},{"path":"add/tools/clidash/test/logs.test.js","size":2956,"sha256":"2827eb56b0574479b7035bf17156929f5eea8dd78962c8ade523bceba86fe977"},{"path":"REMOVE.md","size":544,"sha256":"e753d4d05e43cfcd1d08938bc7bb6b620a725b2df8ad13477a7b81e7adcc96a3"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[{"code":"code.eval","kind":"dangerous-code","where":"add/tools/clidash/test/activity-server.test.js:13","excerpt":"exec(","message":"evaluates code at runtime","severity":"warn"},{"code":"code.eval","kind":"dangerous-code","where":"add/tools/clidash/test/activity.test.js:14","excerpt":"exec(","message":"evaluates code at runtime","severity":"warn"}],"scannedAt":"2026-08-22","hasScripts":true,"networkEndpoints":[]}}