The initial /add-atomic-chat-tool merge added src edits directly to main.
That conflicts with the utility-skill pattern used elsewhere (e.g. /claw):
the skill folder should ship the file and SKILL.md should instruct copy +
idempotent edits at install time, not a git merge that carries src diffs.
- Move container/agent-runner/src/atomic-chat-mcp-stdio.ts →
.claude/skills/add-atomic-chat-tool/atomic-chat-mcp-stdio.ts
- Revert the atomic_chat mcpServers entry in agent-runner index.ts
- Revert mcp__atomic_chat__* from TOOL_ALLOWLIST in providers/claude.ts
- Revert ATOMIC_CHAT_* env forwarding and [ATOMIC] log elevation in
src/container-runner.ts
- Empty .env.example back out
- Rewrite SKILL.md: copy the shipped file, then apply deterministic Edits
(index.ts, providers/claude.ts, container-runner.ts, .env.example)
with exact before/after snippets the installer agent can match.
Main is now back to its pre-PR state for the tool; /add-atomic-chat-tool
re-applies everything at install time.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Exposes local Atomic Chat models (OpenAI-compatible API at
127.0.0.1:1337/v1) as tools to the container agent. Adds
atomic_chat_list_models and atomic_chat_generate alongside
the existing Ollama skill.
Rebased on current main:
- MCP server registered in agent-runner index.ts using bun (no tsc
step in-image), sibling path to index.ts, env: {} with ATOMIC_CHAT_*
forwarded when set.
- allowedTools entry moved to providers/claude.ts TOOL_ALLOWLIST.
- SKILL.md: drop obsolete per-group copy step (single RO mount
supersedes it); use pnpm build.
Made-with: Cursor
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Slack: interactive driver walks through app creation, validates the
bot token via auth.test, installs the adapter, and prints a
post-install checklist for the webhook URL + Event Subscriptions
config. No welcome DM since Slack needs a public URL before inbound
events work — the driver's own "finish in Slack" note replaces the
outro "check your DMs" banner.
iMessage: picks local (macOS) vs remote (Photon) mode. Local mode
opens the node binary's directory in Finder so the user can drag it
into Full Disk Access. Remote mode prompts for Photon URL + API key.
Asks for the operator's phone/email, then wires the first agent
including a welcome iMessage.
Both marked "(experimental)" in the askChannelChoice picker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two installs on the same host could trash each other's containers: the
reaper used `docker ps --filter name=nanoclaw-`, a substring match that
picked up every install's containers. A crash-looping peer (e.g. a legacy
v1 plist respawning ~6k times) would call cleanupOrphans on every boot and
kill the healthy install's session containers within seconds of spawn.
- Stamp `--label nanoclaw-install=<slug>` onto every spawned container.
- cleanupOrphans filters by that label; healthy peers are left alone.
- Setup preflight enumerates `com.nanoclaw*` launchd plists / nanoclaw
user systemd units, probes state/runs, and unloads any that are
crash-looping (state != running AND runs > 10) before installing
this install's service.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three UX tweaks after watching a user walk through setup:
1. Claude-assist "Run this command?" now defaults to Yes. After Claude has already been asked to diagnose + explained the fix, the vast majority of users want to run it — the No-default added friction without proportional safety.
2. claude-assist persists its session across failures in one setup run. First invocation captures session_id from the stream-json init event; subsequent invocations pass --resume <id>. Claude sees prior failures as conversation history instead of treating each hiccup as a blank-slate ticket.
3. First-chat flow no longer drops the user into a free-text chat loop by default. Instead: explain what the ping/pong check is doing, wait for the pong, then offer "Continue with setup" (recommended, default) or "Pause here and chat with your agent from the terminal" (opt-in). The free-text loop is still reachable, just not the default path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The upstream onecli.sh/cli/install script resolves the latest release via
api.github.com/repos/onecli/onecli-cli/releases/latest — anonymous callers
get throttled to 60 req/hour per IP, and once exhausted the installer dies
with "curl: (56) 403 / Error: could not determine latest release". Shared
IPs (corporate NAT, public Wi-Fi) hit this without ever running the
installer themselves. Reproduced locally: rate_limit remaining=0 → upstream
installer returns the exact user error.
Fallback path when upstream fails:
1. Resolve version via `curl -fsSL -o /dev/null -w '%{url_effective}' \
https://github.com/onecli/onecli-cli/releases/latest`. That endpoint
302s to /tag/vX.Y.Z — parses the version without an API call.
2. If the redirect probe also fails, install a pinned fallback version
(ONECLI_CLI_FALLBACK_VERSION, currently 1.3.0).
3. Download the archive from /releases/download/vX.Y.Z/… directly (the
CDN path isn't API-throttled), extract, and install to /usr/local/bin
or ~/.local/bin mirroring upstream's install-dir logic.
Gateway install (onecli.sh/install, docker-compose based) is untouched —
it doesn't hit the API.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dimmed explanatory prose blocks were hard to read against dark terminals. Shift the weight ladder up a notch:
- dimWrap() no longer dims. Multi-line prose (the step-intro copy, etc.) renders at the terminal's regular weight.
- Spinner outcome labels (done/failed/skipped) are now bold via runUnderSpinner, so each step's headline reads stronger than the body copy around it.
- Un-dim two command-hint blocks in auto.ts (docker-group setfacl + service restart; the socket-error remediation commands) — those are commands the user may need to type.
Dim is still used where it helps — (Ns) spinner timings, URLs, short inline parentheticals — and for the preview/debug blocks dim is explicitly reserved for: dumpTranscriptOnFailure tail and claude-assist streams.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When corepack enable fails with EACCES (common when Node is installed to a system-writable prefix like /usr/local that the user doesn't own), we fall back to `npm install -g pnpm`. But npm's global prefix isn't always on the shell's PATH — users often set `npm config set prefix ~/.npm-global` to avoid sudo, and the resulting bin dir isn't picked up by `command -v`. Install succeeded, but pnpm "wasn't there" for the follow-up `pnpm install`.
Now after the npm fallback we query `npm config get prefix` and prepend `<prefix>/bin` to PATH. Mirror the same lookup in nanoclaw.sh right before `exec pnpm run setup:auto` — setup.sh's PATH mutation doesn't propagate back, and the hand-off needs pnpm visible too.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Container step: duration hint + 3-line rolling output window with
60s stall detector that offers "keep waiting" vs "ask Claude"
- First chat: reframed as a try-out with sandbox-model explainer
(wakes on message, sleeps when idle, context persists)
- Timezone: auto-detected non-UTC zones now get an explicit
confirm from the user instead of silent persist
- Outro: added always-on warning + prominent "check your DM" banner
when a channel was configured; directive last line
- Discord: always show token-location reminder even when user says
they have one; new "do you have a server?" branch walks through
server creation if not
- All select prompts: custom brightSelect renderer keeps inactive
option labels at full brightness (was dim gray); adds @clack/core
as a direct dep
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two NanoClaw installs on the same host used to fight over the shared `com.nanoclaw` launchd label / `nanoclaw.service` systemd unit and the `nanoclaw-agent:latest` docker tag — the second install silently rewrote the service pointer and rebuilt the image out from under the first. Introduces a deterministic per-checkout slug (sha1(projectRoot)[:8]) and namespaces everything off it:
- Service: `com.nanoclaw-v2-<slug>` / `nanoclaw-v2-<slug>.service`
- Image: `nanoclaw-agent-v2-<slug>:latest` (base), `nanoclaw-agent-v2-<slug>:<agentGroupId>` (per-group)
New shared helpers: src/install-slug.ts (host) + setup/lib/install-slug.sh (bash). Both compute the same slug so verify/probe/add-*.sh/build.sh/container-runner all agree. Any v1 `com.nanoclaw` service left on the host stays untouched and can coexist.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Before: setup/onecli.ts ran `curl -fsSL onecli.sh/install | sh` unconditionally. For users with OneCLI already running and bound to a specific listener (host-accessible, shared with other apps), re-running the installer rebound the gateway and broke those consumers.
Now: auto.ts probes for an existing install (`onecli version` + `onecli config get api-host`). If detected, clack asks: use the existing instance (recommended) or install a fresh one. The new --reuse flag in the onecli step skips the installer, reads the configured api-host, writes ONECLI_URL to .env, and moves on without touching the running gateway.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Forks that keep the upstream nanoclaw repo under a non-origin remote name (typically `upstream`, with `origin` pointing at the user's fork) hit "git fetch origin channels failed" when adding a channel, because the fork doesn't carry the channels branch. New setup/lib/channels-remote.sh walks `git remote -v` for a url matching qwibitai/nanoclaw, auto-adds `upstream` if none matches, and honors NANOCLAW_CHANNELS_REMOTE as an override. Wired into the four add-*.sh scripts that setup:auto invokes (discord, telegram, whatsapp, teams).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Some Node installs (older nvm, node@22 keg-only on brew, minimal distro packages) don't ship corepack, so the bootstrap was dying with "corepack: command not found" before pnpm could land on PATH. Now guards the corepack call and falls back to `npm install -g pnpm@<pinned>`, reading the version from package.json's packageManager field.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deletes the Claude-orchestrated /setup and /new-setup flows. The scripted installer (bash nanoclaw.sh → setup:auto) now handles bootstrap, container, OneCLI, auth, service, first agent, and optional channel wiring end-to-end with inline Claude-assisted recovery on failure. Keeps /setup as a one-line redirect so the trigger still resolves. Drops the opt-out diagnostics files that belonged to the old flow and updates cross-refs in add-wechat, migrate-nanoclaw, and update-nanoclaw.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ground-up v2 rewrite supersedes all conflicting files from main. The one main-side fix (ONECLI_API_KEY forwarding, 8b5b581) already landed on v2 as 3db66c0. README preview banner dropped since v2 is now main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Trunk ships no channel adapters — /add-telegram installs the package on demand from the channels branch. This dependency was stale and pulled ~2 transitive packages into every fresh install.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes transient analysis/proposal/checklist docs whose purpose is served once v2 ships: REFACTOR.md, docs/v1-vs-v2/, docs/checklist.md, docs/shared-source.md, docs/claude-md-composition.md, docs/module-contract.md, docs/DEBUG_CHECKLIST.md. Updates CLAUDE.md and docs/README.md index rows accordingly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Shared bash + node emitter in setup/lib/diagnostics.{sh,ts} reads/writes data/install-id so every event from a single install shares one distinct_id — bash-side setup_launched/setup_start, node-side auto_started, per-step started/completed, auth_method_chosen, channel_chosen, first_chat_ready/failed, setup_incomplete, setup_aborted, setup_completed. Opt-out via NANOCLAW_NO_DIAGNOSTICS=1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a structured drip-feed of capabilities (memory, agents, scheduling, research, code, UI, files, self-customization) and explicit sections on approvals, access control, and natural interaction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three MCP tool groups were orphaned from the ambient CLAUDE.md context
because they shipped no `*.instructions.md` alongside their source.
Backfill them so the composer picks them up as fragments on next spawn:
- core.instructions.md: add `send_file` (artifact delivery, path relative
to /workspace/agent/) and `add_reaction` (by `#N` id with emoji
shortcode name).
- interactive.instructions.md: `ask_user_question` (blocking
multiple-choice with selectedLabel/value option objects, 300s default
timeout) and `send_card` (non-blocking structured render with
fallbackText). Opens with a one-line framing of the contrast between
the two.
- agents.instructions.md: `create_agent` with how-it-works, when-to-use
(companions vs collaborators — persistent memory vs independent
parallel work), when-NOT-to-use (short tasks should use the SDK `Agent`
tool instead), and guidance for writing the seed instructions string.
No composer changes — scan in `src/claude-md-compose.ts` already picks up
any file matching `*.instructions.md` in the mcp-tools directory.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The HTML comment at the top was aimed at maintainers opening the file,
but it's loaded verbatim into every agent's system prompt via the
`.claude-shared.md` import. Agents don't need the meta-explanation of
where the file is mounted or how identity gets injected — it's just
context-budget drag. Move the maintainer guidance out of the agent's
view.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the passing CLAUDE.local.md mention with an explicit Memory
section: anything substantive the user shares must be stored so it's
retrievable later, with per-topic files indexed from CLAUDE.local.md.
Frames this as a core part of the agent's job — the quality of its
memory systems is a main signal of how useful it is.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
install_packages and add_mcp_server already did the right thing on approve
(install auto-rebuilt+killed, add_mcp_server just killed), so request_rebuild
was redundant plumbing agents sometimes called after an install — wasting an
admin approval round-trip. Delete it end-to-end:
- container/agent-runner/src/mcp-tools/self-mod.ts: remove requestRebuild
tool + registration; update install_packages description.
- src/modules/self-mod/{request,apply,index}.ts: drop handleRequestRebuild
+ applyRequestRebuild + registrations; rewrite the rebuild-failed notify
to point admins at retrying install_packages instead.
- src/modules/{approvals,self-mod}/{agent,project}.md and skill/self-
customize/SKILL.md: scrub agent-facing references; clarify that
add_mcp_server needs no rebuild (bun runs TS directly).
- docs/{module-contract,architecture-diagram,checklist,db-central,shared-
source,v1-vs-v2/*}.md, CLAUDE.md, pending-approvals migration comment,
approvals/index.ts docstring, REFACTOR.md: trailing references.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move every agent-specific instruction out of the shared container/CLAUDE.md
so the base is genuinely universal. Persona/identity now comes from the
system-prompt addendum (buildSystemPromptAddendum now takes assistantName
and prepends "# You are {name}"). Per-module instructions live alongside
each MCP tool source:
container/agent-runner/src/mcp-tools/core.instructions.md
container/agent-runner/src/mcp-tools/scheduling.instructions.md
container/agent-runner/src/mcp-tools/self-mod.instructions.md
composeGroupClaudeMd() scans that directory and emits `module-<name>.md`
fragments as symlinks to /app/src/mcp-tools/<name>.instructions.md (valid
via the existing RO source mount). Skill fragments renamed to
`skill-<name>.md` for naming consistency with `module-*` and `mcp-*`.
Mount tightening so composer-managed files can't be clobbered by agent
writes: nested RO mounts for /workspace/agent/CLAUDE.md and
/workspace/agent/.claude-fragments/. CLAUDE.local.md (per-group memory)
stays RW as the only writable CLAUDE.md-family file.
.gitignore: ignore CLAUDE.local.md, .claude-shared.md, .claude-fragments/
everywhere, and simplify groups/ rules to ignore the whole tree (per-
installation state, not tracked).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Document the selective-mode gotcha for auto-created OneCLI agents
(no secrets injected by default) with the CLI commands to inspect
and fix it. Note that approval policies are not configurable via
the SDK or `onecli@1.3.0` CLI — web UI only.
Replace stale `NANOCLAW_ADMIN_USER_IDS` / `src/access.ts` references
across CLAUDE.md, docs/architecture.md, docs/checklist.md, and
docs/module-contract.md. Admin gating now runs host-side in
src/command-gate.ts against `user_roles`; approver picks live in
src/modules/approvals/primitive.ts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a timezone step between cli-agent and channel wiring in setup:auto.
Autodetect via --step timezone; if it resolves to UTC or fails, confirm
with the user and accept either an IANA zone or a free-text description
(e.g. "New York"). Free-text falls through to a headless `claude -p`
call that returns a single IANA string, gated on the claude CLI being
on PATH.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prepend a Claude-addressed banner so that when an upgrader (or Claude on
their behalf) runs `git pull` / `git merge` from v1 and hits merge
conflicts, Claude aborts the merge and routes the user to
`bash migrate-v2.sh` instead of trying to resolve the rewrite by hand.
Fresh clones are explicitly told to ignore the banner.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports the v1 fix from PR #1777 (originally 8b5b581 by @johnnyfish).
Cherry-pick did not apply cleanly because v2 reformatted the surrounding
code and split OneCLI usage into two sites — manual port was needed.
v2-specific adaptations:
- Also forward apiKey at the second OneCLI call site in
src/modules/approvals/onecli-approvals.ts (v2 split the approvals
module out of container-runner).
- Skipped the companion test-mock commit (38163bc) — it patches
src/container-runner.test.ts, which no longer exists in v2 (tests
consolidated into host-core.test.ts).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: johnnyfish <jonathanfishner11@gmail.com>