7 Commits

Author SHA1 Message Date
nntrivi2001 aff863c69f fix(plugins): retry claude plugin install|update without -y on an old CLI
An older Claude Code has no -y on `plugin install|update` and exits with
"error: unknown option '-y'", so the dashboard's Update now chain failed at
its plugin_update step (and so did Update all, the plugin buttons and the
superpowers auto-install). Those versions also predate the confirmation
prompt -y answers, so every wrapper now retries the same argv without it
when, and only when, the CLI rejects the flag. /agenthub-update, which the
failure message points users to, falls back the same way.
2026-09-23 16:46:20 +07:00
nntrivi2001 aca35dd4cd feat(cc-config): update every plugin and marketplace at once, then reload the open sessions
The plugins tab could already update one plugin or one marketplace at a time. It gains an install-wide batch and, separately, the reload step that actually makes an update take effect.

Update all runs in a DETACHED worker rather than the request handler, for a structural reason: the batch ends with the plugin the dashboard ITSELF runs from, and that step's plugin-bootstrap.js --force SIGTERMs this very server. An in-process loop would die partway through with the remaining plugins unrun and nothing left to report. Order is marketplaces (one call, no name = all) -> every other installed plugin -> AgentHub last -> one reload. Each transition is journalled to cc-update-all.json in the runtime dir, outside the plugin cache the update replaces, so whichever server is alive serves progress from it; the POST's response is only an acknowledgement.

readUpdateAll DERIVES an abandoned status for a running journal older than an hour, because a detached worker killed by a reboot leaves nobody to write the terminal entry and the button would stay wedged forever. isRunning gates a concurrent batch: two claude plugin update chains over one config dir race.

Reload is its own capability (server/lib/cc-reload.js, POST /api/cc-config/reload). /reload-plugins and /reload-skills are scoped BY CLAUDE CODE to the session that runs them, so nothing can reload a session from outside - typing into a session already running is the only real mechanism, and that is what this does, over tmux send-keys into the dashboard's live lane panes. It spawns NO claude process on purpose: a one-shot claude -p "/reload-plugins" would reload only its own short-lived session, exit, and leave a junk session in the dashboard's history - a headless run gets a full sessions row and there is no filter anywhere to suppress it. Reusing live panes means no session is created at all. Panes at a shell prompt are skipped (the text would run as a shell command) and only claude lanes are addressed, since Codex has no such command.

Update-all reloads once at the end; updating a single plugin passes reload:true and reloads immediately, and never after a failed action.

Fixes a real pre-existing bug on the way: pluginUpdate omitted -y while runClaude closes stdin, and the CLI requires it whenever stdin is not a TTY - so the per-plugin Update button failed for any plugin whose marketplace declares an install command. Also completes CcConfigChangedPayload's action union, which claimed write|delete while nine other values were already being emitted.

Verified: server 1683/1683, client 486/486 (27 new tests), build, prettier, file-header audit, openapi.yaml regenerated (117 paths). Update all was NOT executed against this machine - it would update 7 real plugins and restart the running dashboard.
2026-09-10 16:05:07 +07:00
nntrivi2001 c34931c3f5 fix(cc-config): .claude.json sits BESIDE ~/.claude by default, not inside it
The previous commit routed the read through getClaudeHome(), which appends
/.claude when CLAUDE_HOME is unset — pointing the dashboard at
~/.claude/.claude.json, a file the CLI never writes by default. Verified
against the real binary: with CLAUDE_CONFIG_DIR unset the CLI writes
$HOME/.claude.json; with it set, .claude.json sits flat inside that dir.
Branch on CLAUDE_HOME accordingly.

The test now drives a fake claude that writes where the real one does and
asserts getClaudeJsonPath() names that exact file, under both branches —
the previous test only restated the implementation.
2026-09-09 10:00:05 +07:00
nntrivi2001 17e4321761 fix(cc-config): read .claude.json from the same root the CLI writes it to
getClaudeJsonPath() was the only one of cc-discovery's 20 read paths not
derived from getClaudeHome(), so a CLAUDE_HOME install read one file while
the CLI wrote another. The default case was fixed by dropping the
CLAUDE_CONFIG_DIR pin; this closes the mirrored half.

Also replaces the Remove button's display-string comparison with a
removable flag from the reader: rewording the source label silently
resurrected a button that always fails.
2026-09-09 09:54:27 +07:00
nntrivi2001 0ad4b8c920 fix(cc-config): stop pinning CLAUDE_CONFIG_DIR when CLAUDE_HOME is unset
runClaude() unconditionally set CLAUDE_CONFIG_DIR to getClaudeHome()
(~/.claude), so the CLI wrote ~/.claude/.claude.json — a file
cc-discovery.getClaudeJsonPath() never reads (it reads ~/.claude.json,
beside ~/.claude/, by design). Every default-install MCP add/remove and
plugin/marketplace mutation was inert: add toasted success and vanished on
refetch, remove 422'd for every server the panel lists.

With CLAUDE_HOME unset (the default), leave CLAUDE_CONFIG_DIR unset too —
the CLI's own default config dir already lines up with what cc-discovery
reads, so alignment comes from doing nothing. Only pin it when the operator
explicitly set CLAUDE_HOME, which is the one case the pin is meant for.
Left a residual, documented at both sites: with CLAUDE_HOME set, reads still
come from $HOME/.claude.json while the CLI writes
<CLAUDE_HOME>/.claude.json — a split predating this task, affecting a
configuration nobody here runs.

New tests pin the env contract directly (no real CLI needed): runClaude
must not set CLAUDE_CONFIG_DIR when CLAUDE_HOME is absent, and must set it
to CLAUDE_HOME's value when present.
2026-09-09 09:48:28 +07:00
nntrivi2001 17d56d0e01 refactor!: rebrand CCAM to AgentHub
Clean break, no compatibility alias. Renames the product name, the CLI
binary (`ccam` -> `agenthub`), the env var prefix (`CCAM_` ->
`AGENTHUB_`), the Prometheus metric namespace (`ccam_*` ->
`agenthub_*`), the tmux session prefix (`ccam-lane-<id>` ->
`agenthub-lane-<id>`), the per-repo runtime profile directory
(`.ccam/profile` -> `.agenthub/profile`), the lanes state root
(`~/.claude/ccam-lanes` -> `~/.claude/agenthub-lanes`), the bundled
plugins (`plugins/ccam*` -> `plugins/agenthub*`) and their slash
commands (`/ccam-doctor` -> `/agenthub-doctor`, `/ccam:*` ->
`/agenthub:*`).

Because there is no alias, an existing install has to be updated by
hand: rename its `.ccam/profile` directory and any `CCAM_*` variables,
and reinstall the plugin under its new name.

Two other name families are deliberately left alone here, since they are
not just strings: the `Claude-Code-Monitor` repository/marketplace name,
and the `agent-dashboard` npm package plus its `~/.claude/agent-dashboard`
runtime directory, which holds the live database and needs a migration
rather than a rename.

Verified: server 1406/1406, client 459/459, `grep -i ccam` clean across
tracked files, `openapi.yaml` regenerated (95 paths, unchanged count).
2026-09-07 12:09:15 +07:00
nntrivi2001 008ebf1c5c feat(cc-config): manage plugins, marketplaces, hooks and settings from the dashboard
The Claude Config page was read-only for these four surfaces. It can now:

- register a marketplace from a pasted GitHub `owner/repo`, http(s) URL or
  absolute path, and install / enable / disable / update / uninstall plugins,
  all delegated to the `claude plugin …` CLI so its registry stays consistent;
- edit hooks and settings.json keys in the user, project and project-local
  layers.

Server:

- `server/lib/cc-plugin-cli.js` wraps the CLI with `execFile` and an explicit
  argv array — never a shell string. Every argument is validated before the
  spawn (shape allowlist per kind, no control characters, no leading `-`, no
  `..`), `CLAUDE_CONFIG_DIR` is pinned to the dashboard's CLAUDE_HOME, stdin is
  closed so a prompt errors instead of hanging, and mutating calls are
  serialized in-process.
- `cc-mutate.patchSettings` merges individual dotted keys into one settings
  layer. It re-reads the file unredacted, so keys the UI never displayed —
  including secret-valued ones — survive byte-for-byte. A payload carrying the
  `<redacted>` placeholder is refused (EREDACTED), as are `__proto__` /
  `prototype` / `constructor` segments; a settings file that does not parse is
  reported (EBADJSON) rather than clobbered. Backup first, then atomic write.
- Six additive routes on /api/cc-config; a non-zero CLI exit is a 422 carrying
  the CLI's own message, a missing binary a 503.

Client: install/add bars, per-card plugin and marketplace actions behind a
two-click confirm, and edit modes for hooks and settings that send only the
keys that changed. New i18n keys in en + vi; the explainer banners that said
these surfaces were read-only now describe what the writes actually do.

Tests: 61 new server tests (CLI argv construction and validation, the settings
key-merge, and the six routes against a fake `claude` on PATH).
2026-08-25 15:58:03 +07:00