Merge branch 'main' into docs/pr-hygiene-check

This commit is contained in:
Daniel M
2026-03-29 11:17:37 +03:00
committed by GitHub
6 changed files with 158 additions and 96 deletions
-8
View File
@@ -202,14 +202,6 @@ launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist
# systemctl --user start nanoclaw
```
## Agent Swarms (Teams)
After completing the Telegram setup, use `AskUserQuestion`:
AskUserQuestion: Would you like to add Agent Swarm support? Without it, Agent Teams still work — they just operate behind the scenes. With Swarm support, each subagent appears as a different bot in the Telegram group so you can see who's saying what and have interactive team sessions.
If they say yes, invoke the `/add-telegram-swarm` skill.
## Removal
To remove Telegram integration:
+74 -40
View File
@@ -9,7 +9,7 @@ Run setup steps automatically. Only pause when user action is required (channel
**Principle:** When something is broken or missing, fix it. Don't tell the user to go fix it themselves unless it genuinely requires their manual action (e.g. authenticating a channel, pasting a secret token). If a dependency is missing, install it. If a service won't start, diagnose and repair. Ask the user for permission when needed, then do the work.
**UX Note:** Use `AskUserQuestion` for all user-facing questions.
**UX Note:** Use `AskUserQuestion` for multiple-choice questions only (e.g. "Docker or Apple Container?", "which channels?"). Do NOT use it when free-text input is needed (e.g. phone numbers, tokens, paths) — just ask the question in plain text and wait for the user's reply.
## 0. Git & Fork Setup
@@ -50,7 +50,7 @@ Already configured. Continue.
**Verify:** `git remote -v` should show `origin` → user's repo, `upstream``qwibitai/nanoclaw.git`.
## 1. Bootstrap (Node.js + Dependencies + OneCLI)
## 1. Bootstrap (Node.js + Dependencies)
Run `bash setup.sh` and parse the status block.
@@ -62,34 +62,6 @@ Run `bash setup.sh` and parse the status block.
- If NATIVE_OK=false → better-sqlite3 failed to load. Install build tools and re-run.
- Record PLATFORM and IS_WSL for later steps.
After bootstrap succeeds, install OneCLI and its CLI tool:
```bash
curl -fsSL onecli.sh/install | sh
curl -fsSL onecli.sh/cli/install | sh
```
Verify both installed: `onecli version`. If the command is not found, the CLI was likely installed to `~/.local/bin/`. Add it to PATH for the current session and persist it:
```bash
export PATH="$HOME/.local/bin:$PATH"
# Persist for future sessions (append to shell profile if not already present)
grep -q '.local/bin' ~/.bashrc 2>/dev/null || echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
grep -q '.local/bin' ~/.zshrc 2>/dev/null || echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
```
Then re-verify with `onecli version`.
Point the CLI at the local OneCLI instance (it defaults to the cloud service otherwise):
```bash
onecli config set api-host http://127.0.0.1:10254
```
Ensure `.env` has the OneCLI URL (create the file if it doesn't exist):
```bash
grep -q 'ONECLI_URL' .env 2>/dev/null || echo 'ONECLI_URL=http://127.0.0.1:10254' >> .env
```
## 2. Check Environment
Run `npx tsx setup/index.ts --step environment` and parse the status block.
@@ -112,7 +84,10 @@ Run `npx tsx setup/index.ts --step timezone` and parse the status block.
Check the preflight results for `APPLE_CONTAINER` and `DOCKER`, and the PLATFORM from step 1.
- PLATFORM=linux → Docker (only option)
- PLATFORM=macos + APPLE_CONTAINER=installed → Use `AskUserQuestion: Docker (cross-platform) or Apple Container (native macOS)?` If Apple Container, run `/convert-to-apple-container` now, then skip to 3c.
- PLATFORM=macos + APPLE_CONTAINER=installed → AskUserQuestion with two options:
1. **Docker (recommended)** — description: "Cross-platform, better credential management, well-tested."
2. **Apple Container (experimental)** — description: "Native macOS runtime. Requires advanced setup."
If Apple Container, run `/convert-to-apple-container` now, then skip to 3c.
- PLATFORM=macos + APPLE_CONTAINER=not_found → Docker
### 3a-docker. Install Docker
@@ -147,9 +122,39 @@ Run `npx tsx setup/index.ts --step container -- --runtime <chosen>` and parse th
**If TEST_OK=false but BUILD_OK=true:** The image built but won't run. Check logs — common cause is runtime not fully started. Wait a moment and retry the test.
## 4. Anthropic Credentials via OneCLI
## 4. Credential System
NanoClaw uses OneCLI to manage credentials — API keys are never stored in `.env` or exposed to containers. The OneCLI gateway injects them at request time.
The credential system depends on the container runtime chosen in step 3.
### 4a. Docker → OneCLI
Install OneCLI and its CLI tool:
```bash
curl -fsSL onecli.sh/install | sh
curl -fsSL onecli.sh/cli/install | sh
```
Verify both installed: `onecli version`. If the command is not found, the CLI was likely installed to `~/.local/bin/`. Add it to PATH for the current session and persist it:
```bash
export PATH="$HOME/.local/bin:$PATH"
# Persist for future sessions (append to shell profile if not already present)
grep -q '.local/bin' ~/.bashrc 2>/dev/null || echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
grep -q '.local/bin' ~/.zshrc 2>/dev/null || echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
```
Then re-verify with `onecli version`.
Point the CLI at the local OneCLI instance (it defaults to the cloud service otherwise):
```bash
onecli config set api-host http://127.0.0.1:10254
```
Ensure `.env` has the OneCLI URL (create the file if it doesn't exist):
```bash
grep -q 'ONECLI_URL' .env 2>/dev/null || echo 'ONECLI_URL=http://127.0.0.1:10254' >> .env
```
Check if a secret already exists:
```bash
@@ -163,16 +168,20 @@ AskUserQuestion: Do you want to use your **Claude subscription** (Pro/Max) or an
1. **Claude subscription (Pro/Max)** — description: "Uses your existing Claude Pro or Max subscription. You'll run `claude setup-token` in another terminal to get your token."
2. **Anthropic API key** — description: "Pay-per-use API key from console.anthropic.com."
### Subscription path
#### Subscription path
Tell the user to run `claude setup-token` in another terminal and copy the token it outputs. Do NOT collect the token in chat.
Tell the user:
Once they have the token, they register it with OneCLI. AskUserQuestion with two options:
> Run `claude setup-token` in another terminal. It will output a token — copy it but don't paste it here.
Then stop and wait for the user to confirm they have the token. Do NOT proceed until they respond.
Once they confirm, they register it with OneCLI. AskUserQuestion with two options:
1. **Dashboard** — description: "Best if you have a browser on this machine. Open http://127.0.0.1:10254 and add the secret in the UI. Use type 'anthropic' and paste your token as the value."
2. **CLI** — description: "Best for remote/headless servers. Run: `onecli secrets create --name Anthropic --type anthropic --value YOUR_TOKEN --host-pattern api.anthropic.com`"
### API key path
#### API key path
Tell the user to get an API key from https://console.anthropic.com/settings/keys if they don't have one.
@@ -181,7 +190,7 @@ Then AskUserQuestion with two options:
1. **Dashboard** — description: "Best if you have a browser on this machine. Open http://127.0.0.1:10254 and add the secret in the UI."
2. **CLI** — description: "Best for remote/headless servers. Run: `onecli secrets create --name Anthropic --type anthropic --value YOUR_KEY --host-pattern api.anthropic.com`"
### After either path
#### After either path
Ask them to let you know when done.
@@ -189,6 +198,31 @@ Ask them to let you know when done.
**After user confirms:** verify with `onecli secrets list` that an Anthropic secret exists. If not, ask again.
### 4b. Apple Container → Native Credential Proxy
Apple Container is not compatible with OneCLI. The credential proxy code is already included in the apple-container branch — do NOT invoke `/use-native-credential-proxy` (it would conflict with already-applied code).
Instead, just configure the credentials in `.env`:
AskUserQuestion: Do you want to use your **Claude subscription** (Pro/Max) or an **Anthropic API key**?
1. **Claude subscription (Pro/Max)** — description: "Uses your existing Claude Pro or Max subscription. Run `claude setup-token` in another terminal to get your token."
2. **Anthropic API key** — description: "Pay-per-use API key from console.anthropic.com."
For subscription: tell the user to run `claude setup-token` in another terminal. Stop and wait for the user to confirm they have completed this step successfully before proceeding.
Once confirmed, add the token to `.env`:
```bash
echo 'CLAUDE_CODE_OAUTH_TOKEN=<their-token>' >> .env
```
For API key: add to `.env`:
```bash
echo 'ANTHROPIC_API_KEY=<their-key>' >> .env
```
Verify the proxy starts: `npm run dev` should show "Credential proxy listening" in the logs.
## 5. Set Up Channels
AskUserQuestion (multiSelect): Which messaging channels do you want to enable?
@@ -265,7 +299,7 @@ Run `npx tsx setup/index.ts --step verify` and parse the status block.
**If STATUS=failed, fix each:**
- SERVICE=stopped → `npm run build`, then restart: `launchctl kickstart -k gui/$(id -u)/com.nanoclaw` (macOS) or `systemctl --user restart nanoclaw` (Linux) or `bash start-nanoclaw.sh` (WSL nohup)
- SERVICE=not_found → re-run step 7
- CREDENTIALS=missing → re-run step 4 (check `onecli secrets list` for Anthropic secret)
- CREDENTIALS=missing → re-run step 4 (Docker: check `onecli secrets list`; Apple Container: check `.env` for credentials)
- CHANNEL_AUTH shows `not_found` for any channel → re-invoke that channel's skill (e.g. `/add-telegram`)
- REGISTERED_GROUPS=0 → re-invoke the channel skills from step 5
- MOUNT_ALLOWLIST=missing → `npx tsx setup/index.ts --step mounts -- --empty`
@@ -274,7 +308,7 @@ Tell user to test: send a message in their registered chat. Show: `tail -f logs/
## Troubleshooting
**Service not starting:** Check `logs/nanoclaw.error.log`. Common: wrong Node path (re-run step 7), OneCLI not running (check `curl http://127.0.0.1:10254/api/health`), missing channel credentials (re-invoke channel skill).
**Service not starting:** Check `logs/nanoclaw.error.log`. Common: wrong Node path (re-run step 7), credential system not running (Docker: check `curl http://127.0.0.1:10254/api/health`; Apple Container: check `.env` credentials), missing channel credentials (re-invoke channel skill).
**Container agent fails ("Claude Code process exited with code 1"):** Ensure the container runtime is running — `open -a Docker` (macOS Docker), `container system start` (Apple Container), or `sudo systemctl start docker` (Linux). Check container logs in `groups/main/logs/container-*.log`.
+81
View File
@@ -0,0 +1,81 @@
# Branch & Fork Maintenance Guidelines
## Structure
**`qwibitai/nanoclaw`** (upstream) — core engine with skill definitions (`.claude/skills/`). No channel code on `main`.
**Channel forks** (`nanoclaw-whatsapp`, `nanoclaw-telegram`, `nanoclaw-slack`, etc.) — each fork = upstream + one channel's code applied. Users clone upstream, then merge a fork into their clone to add a channel.
**`skill/*` and `feat/*` branches on upstream** — add features unrelated to channels (e.g. `skill/compact`, `skill/apple-container`). Users merge these into their clone to add capabilities. Channel-specific skill branches that duplicate the forks (e.g. `skill/whatsapp`, `skill/telegram`) are legacy.
## How users add capabilities
```
user clones upstream main
├── merges nanoclaw-whatsapp fork → adds WhatsApp
├── merges skill/compact branch → adds /compact command
└── merges skill/apple-container → switches to Apple Container
```
## Merge directions
```
upstream main ──→ channel forks (forward merge to keep forks caught up)
upstream main ──→ skill branches (forward merge to keep branches caught up)
```
Forks and skill branches carry applied code changes. Users merge them into their own clones/forks to add capabilities. They are never merged back into upstream `main`.
## Forward merge procedure
```bash
# In your local nanoclaw checkout
git checkout main && git pull
# For a fork:
git fetch nanoclaw-whatsapp
git checkout -B whatsapp-merge nanoclaw-whatsapp/main
git merge main
# Resolve conflicts (see below)
# Remove upstream-only workflows (re-added by every merge since main has them):
git rm .github/workflows/bump-version.yml .github/workflows/update-tokens.yml 2>/dev/null
git push nanoclaw-whatsapp HEAD:main
git checkout main && git branch -D whatsapp-merge
# For a skill branch:
git checkout -B skill/compact origin/skill/compact
git merge main
# Resolve conflicts (see below)
git push origin skill/compact
git checkout main && git branch -D skill/compact
```
## Conflict resolution
The same files conflict every time:
| File | Resolution |
|------|------------|
| `package.json` | Take main's version + keep fork/branch-specific deps |
| `package-lock.json` | `git checkout main -- package-lock.json && npm install` |
| `.env.example` | Combine: main's entries + fork/branch-specific entries |
| `repo-tokens/badge.svg` | Take main's version (auto-generated) |
Source code changes (e.g. `src/types.ts`, `src/index.ts`) usually auto-merge cleanly, but can conflict if both sides modify the same lines. **Always build and test after every forward merge** — auto-merged code can be silently wrong (e.g. referencing a renamed function or using a removed parameter) even when git reports no conflicts.
## When to merge forward
After any main change that touches shared files (`package.json`, `src/index.ts`, `CLAUDE.md`, etc.). Small frequent merges = trivial conflicts. Large infrequent merges = painful.
## Fork setup
When creating a new channel fork:
1. Fork `nanoclaw` to `nanoclaw-{channel}`
2. Remove upstream-only workflows: `bump-version.yml`, `update-tokens.yml`
3. Add channel code, deps, env vars
4. Forward-merge main immediately to establish a clean baseline
## Dependencies
Forks and branches add their own deps on top of upstream's. When upstream adds or removes a dependency, verify that forks/branches still build after the next forward merge — transitive dependency changes can break downstream code.
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "nanoclaw",
"version": "1.2.41",
"version": "1.2.42",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "nanoclaw",
"version": "1.2.41",
"version": "1.2.42",
"dependencies": {
"@onecli-sh/sdk": "^0.2.0",
"better-sqlite3": "11.10.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "nanoclaw",
"version": "1.2.41",
"version": "1.2.42",
"description": "Personal Claude assistant. Lightweight, secure, customizable.",
"type": "module",
"main": "dist/index.js",
-45
View File
@@ -1,45 +0,0 @@
import fs from 'fs';
import os from 'os';
import path from 'path';
import { spawnSync } from 'child_process';
import { describe, expect, it } from 'vitest';
describe('claw skill script', () => {
it('exits zero after successful structured output even if the runtime is terminated', { timeout: 20000 }, () => {
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'claw-skill-test-'));
const binDir = path.join(tempDir, 'bin');
fs.mkdirSync(binDir, { recursive: true });
const runtimePath = path.join(binDir, 'container');
fs.writeFileSync(
runtimePath,
`#!/bin/sh
cat >/dev/null
printf '%s\n' '---NANOCLAW_OUTPUT_START---' '{"status":"success","result":"4","newSessionId":"sess-1"}' '---NANOCLAW_OUTPUT_END---'
sleep 30
`,
);
fs.chmodSync(runtimePath, 0o755);
const result = spawnSync(
'python3',
['.claude/skills/claw/scripts/claw', '-j', 'tg:123', 'What is 2+2?'],
{
cwd: process.cwd(),
encoding: 'utf8',
env: {
...process.env,
NANOCLAW_DIR: tempDir,
PATH: `${binDir}:${process.env.PATH || ''}`,
},
timeout: 15000,
},
);
expect(result.status).toBe(0);
expect(result.signal).toBeNull();
expect(result.stdout).toContain('4');
expect(result.stderr).toContain('[session: sess-1]');
});
});