Commit Graph

273 Commits

Author SHA1 Message Date
github-actions[bot]
f5b2068852 docs: update token count to 37.9k tokens · 19% of context window 2026-02-22 22:04:36 +00:00
gavrielc
5958175ba1 fix: use 'Assistant' as fallback name instead of 'AssistantNameMissing'
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 00:04:16 +02:00
gavrielc
495b7df5fc merge: resolve conflict with origin/main
Keep ASSISTANT_NAME import, drop removed GROUPS_DIR import.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 00:03:20 +02:00
github-actions[bot]
1046a79682 docs: update token count to 37.8k tokens · 19% of context window 2026-02-22 21:23:47 +00:00
gavrielc
77f7423172 fix: pass host timezone to container and reject UTC-suffixed timestamps (#371)
Containers had no TZ set, so any time-aware code inside ran in UTC while
the host interpreted bare timestamps as local time. Now TIMEZONE from
config.ts is passed via -e TZ= to the container args.

Also rejects Z-suffixed or offset-suffixed timestamps in the container's
schedule_task validation, since bare timestamps are expected to be local
time and silently accepting UTC suffixes would cause an offset mismatch.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 23:23:34 +02:00
Dan Shapiro
107aff850c fix: pass assistantName to container agent instead of hardcoding 'Andy'
The container agent-runner had 'Andy' hardcoded as the sender name in
archived conversation transcripts. This ignored the configurable
ASSISTANT_NAME setting, so users who changed their assistant's name
(via .env or config) would still see 'Andy' in transcripts.

- Add assistantName field to ContainerInput interface (both host and
  container copies)
- Pass ASSISTANT_NAME from config through to container in index.ts
  and task-scheduler.ts
- Thread assistantName through createPreCompactHook and
  formatTranscriptMarkdown in the agent-runner
- Use 'AssistantNameMissing' as fallback instead of 'Andy' so a
  missing name is visible rather than silently wrong

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 12:22:07 -08:00
github-actions[bot]
6b85ac59bd docs: update token count to 37.7k tokens · 19% of context window 2026-02-22 19:02:53 +00:00
gavrielc
2e1c768207 fix: block group folder path escapes (#387) 2026-02-22 21:02:27 +02:00
Lawyered
02d8528684 fix: pause malformed scheduled tasks 2026-02-22 21:01:53 +02:00
Lawyered
c6391cceb1 fix: block group folder path escapes 2026-02-22 21:01:53 +02:00
gavrielc
de64dab3e9 Merge pull request #369 from lawyered0/codex/fix-file-ops-symlink-escape
Fix critical symlink escape in skills file operations
2026-02-22 21:01:11 +02:00
github-actions[bot]
6d4277f669 docs: update token count to 37.0k tokens · 18% of context window 2026-02-22 18:58:12 +00:00
gavrielc
5fb10645cd fix: mount project root read-only to prevent container escape (#392)
The main group's project root was mounted read-write, allowing the
container agent to modify host application code (e.g. dist/container-runner.js)
to inject arbitrary mounts on next restart — a full sandbox escape.

Fix: mount the project root read-only. Writable paths the agent needs
(group folder, IPC, .claude/) are already mounted separately. The
agent-runner source is now copied into a per-group writable location
so agents can still customize container-side behavior without affecting
host code or other groups.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:57:57 +02:00
gavrielc
ef00320018 Update README.md 2026-02-22 18:47:01 +02:00
github-actions[bot]
e8d1d1ee55 docs: update token count to 36.9k tokens · 18% of context window 2026-02-22 16:43:42 +00:00
gavrielc
92d14405c5 refactor: move setup scripts out of src/ to reduce build token count
Setup scripts are standalone CLI tools run via tsx with no runtime
imports from the main app. Moving them out of src/ excludes them from
the tsc build output and reduces the compiled bundle size.

- git mv src/setup/ setup/
- Fix imports to use ../src/logger.js and ../src/config.js
- Update package.json, vitest.config.ts, SKILL.md references
- Fix platform tests to be cross-platform (macOS + Linux)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:43:22 +02:00
github-actions[bot]
c1a2491e77 docs: update token count to 49.9k tokens · 25% of context window 2026-02-22 16:25:24 +00:00
Daniel M
8fc1c23925 Migrate setup from bash scripts to cross-platform Node.js modules (#382)
* refactor: migrate setup from bash scripts to cross-platform Node.js modules

Replace 9 bash scripts + qr-auth.html with a two-phase setup system:
a bash bootstrap (setup.sh) for Node.js/npm verification, and TypeScript
modules (src/setup/) for everything else. Resolves cross-platform issues:
sed -i replaced with fs operations, sqlite3 CLI replaced with better-sqlite3,
browser opening made cross-platform, service management supports launchd/
systemd/WSL nohup fallback, SQL injection prevented with parameterized queries.

Add Linux systemctl equivalents alongside macOS launchctl commands in 8 skill
files and CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: setup migration issues — pairing code, systemd fallback, nohup escaping

- Emit WhatsApp pairing code immediately when received, before polling
  for auth completion. Previously the code was only shown in the final
  status block after auth succeeded — a catch-22 since the user needs
  the code to authenticate. (whatsapp-auth.ts)

- Add systemd user session pre-check before attempting to write the
  user-level service unit. Falls back to nohup wrapper when user-level
  systemd is unavailable (e.g. su session without login/D-Bus). (service.ts)

- Rewrite nohup wrapper template using array join instead of template
  literal to fix shell variable escaping (\\$ → $). (service.ts)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: detect stale docker group and kill
  orphaned processes on Linux systemd

* fix: remove redundant shell option from execSync to fix TS2769

execSync already runs in a shell by default; the explicit `shell: true`
caused a type error with @types/node which expects string, not boolean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: hide QR browser auth option on headless Linux

Emit IS_HEADLESS from environment step and condition SKILL.md to
only show pairing code + QR terminal when no display server is
available (headless Linux without WSL). WSL is excluded from the
headless gate because browser opening works via Windows interop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 18:25:11 +02:00
Lawyered
ccef3bb8ab fix: block symlink escapes in skills file ops 2026-02-22 00:00:13 -05:00
github-actions[bot]
1980d97d90 docs: update token count to 36.8k tokens · 18% of context window 2026-02-21 21:23:05 +00:00
gavrielc
5f58941db2 fix: add .catch() handlers to fire-and-forget async calls (#221) (#355)
Several async calls in the message loop and group queue are
fire-and-forget without .catch() handlers. When WhatsApp disconnects
or containers fail unexpectedly, these produce unhandled rejections
that can crash the process.

Add explicit .catch() at each call site so errors are logged with
full context (groupJid, taskId) instead of crashing:

- channel.setTyping() in message loop (adapted for channel abstraction)
- startMessageLoop() in main()
- runForGroup() and runTask() in group-queue (5 call sites)

Closes #221

Co-authored-by: Naveen Jain <1779929+naveenspark@users.noreply.github.com>
Co-authored-by: Skip Potter <skip.potter.va@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:22:51 +02:00
gavrielc
cb294405a5 fix: update voice note test to match empty-content skip behavior
The test expected voice notes (audioMessage with no caption) to be
delivered with empty content, but 6f177ad added a guard that skips
messages with no text content. Update assertion accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 23:13:43 +02:00
vaibhav
6e22abb1ba fix: replace hardcoded /Users/user fallback with os.homedir()
The HOME_DIR fallback '/Users/user' is macOS-specific and incorrect.
Use os.homedir() from Node's os module which works cross-platform
and returns the actual home directory from /etc/passwd on Linux.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-21 22:40:57 +02:00
github-actions[bot]
9003259675 docs: update token count to 36.6k tokens · 18% of context window 2026-02-21 20:19:38 +00:00
Gavriel Cohen
3d8c0d1c0d test: add coverage for isTaskContainer and idleWaiting reset
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:19:24 +02:00
Gavriel Cohen
c6b69e87a9 fix: correctly trigger idle preemption in streaming input mode
The original notifyIdle condition (!result.result) never fired in
streaming input mode because every result has non-null text content.
This caused due tasks to wait up to 30 minutes for the idle timer.

- Call notifyIdle for ALL successful results (not just null ones)
- Add isTaskContainer flag so user messages queue instead of being
  forwarded to task containers (which blocked notifyIdle from the
  message container's onOutput path)
- Reset idleWaiting in sendMessage so containers aren't preempted
  while actively working on a new incoming message
- Replace 30-min IDLE_TIMEOUT with 10s close timer for task containers
  since they are single-turn and should exit promptly after their result

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:19:24 +02:00
gavrielc
93bb94ff55 fix: only preempt idle containers when scheduled tasks enqueue
Containers that finish work but stay alive in waitForIpcMessage() block
queued scheduled tasks. Previous approaches killed active containers
mid-work. This fix tracks idle state via the session-update marker
(status: success, result: null) and only preempts when the container
is idle-waiting, not actively working.

Closes #293

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:19:24 +02:00
Peyton-Spencer
6f177adafe fix: skip empty WhatsApp protocol messages
WhatsApp protocol messages (encryption key distribution, read receipts,
ephemeral settings, senderKeyDistributionMessage) have a message envelope
but no text content. These were being stored in messages.db and processed
by the agent, causing:

1. Agent responds to empty messages, wasting API tokens
2. Container stays alive indefinitely (idle timer resets)
3. Scheduled tasks blocked (queue slot occupied)

This fix skips messages with empty content before calling onMessage,
preventing protocol messages from being stored and processed.

Fixes #250

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-21 17:27:07 +02:00
Stefan Gasser
d336b32460 fix: copy skill subdirectories recursively (#175)
copyFileSync crashes with EISDIR when a skill contains subdirectories
like scripts/. Skills support nested folders (scripts/, examples/,
templates/) per the Claude Code spec. Use fs.cpSync to handle the
complete skill structure.
2026-02-21 17:23:19 +02:00
Gio Lodi
94ba537310 Decouple formatting test from @Andy (#329)
* Fix trigger pattern tests to use config name

Tests hardcoded "Andy" but the pattern is built from
`ASSISTANT_NAME` which comes from `.env`.

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>

* Restore usage comment in trim test

---

Generated with the help of Claude Code, https://claude.ai/code

Co-Authored-By: Claude Code Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Code Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:18:08 +02:00
gavrielc
3c79c61f67 docs: fix README_zh consistency and remove Skills System CLI section
- Fix 你→您 inconsistency in README_zh.md
- Add missing nanoclaw.dev link to README_zh.md header
- Remove Skills System CLI section from both README.md and README_zh.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 17:16:28 +02:00
jiakeboge
3d1859f919 docs(zh): Apply stylistic and consistency improvements to README_zh.md (#328) 2026-02-21 17:15:47 +02:00
gavrielc
646411ff03 docs: add nanoclaw.dev link to README header
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 14:49:20 +02:00
Gavriel Cohen
41e54a9460 fix: pass filePath in setupRerereAdapter stale MERGE_HEAD cleanup
cleanupMergeState() without a filePath runs bare `git reset`, which
resets the entire index and can stage deletions of unrelated files.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 15:14:00 +02:00
gavrielc
7181c49ada feat: add /convert-to-apple-container skill, remove /convert-to-docker (#324)
Docker is now the default runtime. The /convert-to-apple-container skill
uses the new skills engine format (manifest.yaml, modify/, intent files,
tests/) to switch to Apple Container on macOS.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:57:05 +02:00
gavrielc
a4072162b7 feat: add voice transcription as nanorepo skill (#326)
Add voice transcription skill package at
.claude/skills/add-voice-transcription/ so it can be applied via the
skills engine. Skill adds src/transcription.ts (OpenAI Whisper), modifies
whatsapp.ts to detect/transcribe voice notes, and includes intent files,
3 test cases, and 8 skill validation tests.

Also fixes skills engine runNpmInstall() to use --legacy-peer-deps,
needed for any skill adding deps with Zod v3 peer requirements.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:18:54 +02:00
gavrielc
6b9b3a12c9 docs: update skills to use Docker commands after runtime migration (#325)
All skills now reference Docker CLI instead of Apple Container CLI.
Setup skill defaults to Docker with optional /convert-to-apple-container.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 14:06:31 +02:00
github-actions[bot]
a7faac664a docs: update token count to 36.3k tokens · 18% of context window 2026-02-20 11:20:28 +00:00
gavrielc
607623aa59 feat: convert container runtime from Apple Container to Docker (#323)
Swap container-runtime.ts to the Docker variant:
- CONTAINER_RUNTIME_BIN: 'container' → 'docker'
- readonlyMountArgs: --mount bind,readonly → -v host:container:ro
- ensureContainerRuntimeRunning: container system status → docker info
- cleanupOrphans: Apple Container JSON format → docker ps --filter
- build.sh default: container → docker

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 13:20:13 +02:00
github-actions[bot]
51a50d402b docs: update token count to 36.4k tokens · 18% of context window 2026-02-20 11:14:09 +00:00
gavrielc
c6e1bfecc6 refactor: extract runtime-specific code into src/container-runtime.ts (#321)
Move all container-runtime-specific logic (binary name, mount args,
stop command, startup check, orphan cleanup) into a single file so
swapping runtimes only requires replacing this one file.

Neutralize "Apple Container" references in comments and docs that
would become incorrect after a runtime swap. References that list
both runtimes as options are left unchanged.

No behavior change — Apple Container remains the default runtime.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 13:13:55 +02:00
gavrielc
8fd67916b3 Update README.md (#316) 2026-02-19 20:49:11 +02:00
gavrielc
5a16a9db9f Documentation improvements
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 15:26:57 +02:00
github-actions[bot]
646491ce5c docs: update token count to 36.3k tokens · 18% of context window 2026-02-18 23:55:13 +00:00
gavrielc
51788de3b9 Skills engine v0.1 + multi-channel infrastructure (#307)
* refactor: multi-channel infrastructure with explicit channel/is_group tracking

- Add channels[] array and findChannel() routing in index.ts, replacing
  hardcoded whatsapp.* calls with channel-agnostic callbacks
- Add channel TEXT and is_group INTEGER columns to chats table with
  COALESCE upsert to protect existing values from null overwrites
- is_group defaults to 0 (safe: unknown chats excluded from groups)
- WhatsApp passes explicit channel='whatsapp' and isGroup to onChatMetadata
- getAvailableGroups filters on is_group instead of JID pattern matching
- findChannel logs warnings instead of silently dropping unroutable JIDs
- Migration backfills channel/is_group from JID patterns for existing DBs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: skills engine v0.1 — deterministic skill packages with rerere resolution

Three-way merge engine for applying skill packages on top of a core
codebase. Skills declare which files they add/modify, and the engine
uses git merge-file for conflict detection with git rerere for
automatic resolution of previously-seen conflicts.

Key components:
- apply: three-way merge with backup/rollback safety net
- replay: clean-slate replay for uninstall and rebase
- update: core version updates with deletion detection
- rebase: bake applied skills into base (one-way)
- manifest: validation with path traversal protection
- resolution-cache: pre-computed rerere resolutions
- structured: npm deps, env vars, docker-compose merging
- CI: per-skill test matrix with conflict detection

151 unit tests covering merge, rerere, backup, replay, uninstall,
update, rebase, structured ops, and edge cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add Discord and Telegram skill packages

Skill packages for adding Discord and Telegram channels to NanoClaw.
Each package includes:
- Channel implementation (add/src/channels/)
- Three-way merge targets for index.ts, config.ts, routing.test.ts
- Intent docs explaining merge invariants
- Standalone integration tests
- manifest.yaml with dependency/conflict declarations

Applied via: npx tsx scripts/apply-skill.ts .claude/skills/add-discord
These are inert until applied — no runtime impact.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* remove unused docs (skills-system-status, implementation-guide)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 01:55:00 +02:00
gavrielc
a689f8b3fa fix: quote ASSISTANT_NAME in .env to handle special characters
Use pipe delimiter in sed and quote the value to prevent breakage
from names containing /, &, \, or spaces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:31:02 +02:00
github-actions[bot]
6f719879aa docs: update token count to 35.6k tokens · 18% of context window 2026-02-18 08:30:11 +00:00
Koshkoshinsk
802805d2ec Fix/WA reconnect, container perms, assist name in env (#297)
* fix: WA 515 stream error reconnect exiting early before key sync

Pass isReconnect flag on 515 reconnect so the registered-creds check
doesn't bail out before the handshake completes (caused "logging in..."
hang after successful pairing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: container permission errors on Docker with non-default uid

Make /home/node world-writable in the Dockerfile so the SDK can write
.claude.json. Add --user flag matching host uid/gid in container-runner
so bind-mounted files are accessible. Skip when running as root (uid 0),
as the container's node user (uid 1000), or on native Windows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: write ASSISTANT_NAME to .env during setup

When a custom assistant name is chosen, persist it to .env so config.ts
picks it up at runtime. Uses temp file for cross-platform sed
compatibility (macOS/Linux/WSL).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:29:55 +02:00
gavrielc
f257b93d34 chore: update Discord invite link
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 07:17:01 +02:00
gavrielc
b7c9d98ec2 fix: ensure setup skill runs Docker conversion before building containers
The setup skill would skip the /convert-to-docker step because:
1. The conversion instructions were buried inline in runtime choice bullets
2. The convert-to-docker skill had disable-model-invocation: true

Restructured step 3 into explicit sub-steps with a hard gate (3b) that
checks source files for Apple Container references before allowing the
build to proceed. Enabled model invocation on the convert-to-docker skill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 22:17:51 +02:00