Commit Graph

70 Commits

Author SHA1 Message Date
glifocat
5b2bafd7bb fix(whatsapp): use sender's JID for DM-with-bot registration, skip trigger (#751)
Two bugs in the DM with dedicated bot number setup:

1. The skill asked for the bot's own phone number to use as the JID.
   But from the bot's perspective, incoming DMs appear with the SENDER's
   JID (the user's personal number), not the bot's own number. The
   registration must use the user's personal number as the JID.

2. DM with bot (1:1 conversation) should use --no-trigger-required, same
   as self-chat. A trigger prefix is unnecessary in a private DM.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 19:58:48 +02:00
Thomas Lok
cfabdd816b fix broken step references in setup/SKILL.md (#794) 2026-03-07 20:58:52 +02:00
glifocat
af937d6453 feat(skills): add image vision skill for WhatsApp (#770)
* chore: prepare image-vision skill for template regeneration

- Delete stale modify/*.ts templates (built against 1.1.2)
- Update core_version to 1.2.6
- Strip fork-specific details from intent docs

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

* feat(skills): regenerate image-vision modify/ templates against upstream

Templates regenerated against upstream 1.2.6:
- src/container-runner.ts: imageAttachments field in ContainerInput
- src/index.ts: parseImageReferences + threading to runAgent
- src/channels/whatsapp.ts: downloadMediaMessage + image handling block
- src/channels/whatsapp.test.ts: image mocks + 4 test cases
- container/agent-runner/src/index.ts: ContentBlock types, pushMultimodal, image loading

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

* test: update image-vision tests for upstream templates

- Relax downloadMediaMessage import pattern check (multi-line import)
- Remove check for [Image - processing failed] (not in upstream template)
- Add vitest.skills.config.ts for skill package test runs

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

* chore: update image-vision core_version to 1.2.8

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 18:52:59 +02:00
glifocat
be1991108b fix(whatsapp): write pairing code to file for immediate access (#745)
The pairing code was only emitted to stdout, which is buffered by the
calling process and not visible until the auth command exits (~120s).
By also writing to store/pairing-code.txt the moment the code is ready,
callers can poll that file and display the code to the user within seconds
instead of after the 60s expiry window.

Update the add-whatsapp skill instructions to use the background +
file-poll pattern instead of waiting on buffered stdout.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 18:51:00 +02:00
glifocat
0b260ece57 feat(skills): add pdf-reader skill (#772)
Thanks @glifocat! Clean skill package — good docs, solid tests, nice intent files. Pushed a small fix for path traversal on the PDF filename before merging.
2026-03-06 18:47:12 +02:00
vsabavat
47ad2e654c fix: add-voice-transcription skill drops WhatsApp registerChannel call (#766)
The modify/src/channels/whatsapp.ts patch in the add-voice-transcription
skill was missing the registerChannel() call and its registry import.
When the three-way merge ran, this caused the WhatsApp channel to silently
skip registration on every service restart — messages were never received.

Added the missing import and registerChannel factory with a creds.json
guard, matching the pattern used by the add-whatsapp skill template.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 12:27:25 +02:00
daniviber
298c3eade4 feat: add /add-ollama skill for local model inference (#712)
* feat: add /add-ollama skill for local model inference

Adds a skill that integrates Ollama as an MCP server, allowing the
container agent to offload tasks to local models (summarization,
translation, general queries) while keeping Claude as orchestrator.

Skill contents:
- ollama-mcp-stdio.ts: stdio MCP server with ollama_list_models and
  ollama_generate tools
- ollama-watch.sh: macOS notification watcher for Ollama activity
- Modifications to index.ts (MCP config) and container-runner.ts
  (log surfacing)

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

* chore: rename skill from /add-ollama to /add-ollama-tool

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: gavrielc <gabicohen22@yahoo.com>
2026-03-04 23:48:23 +02:00
glifocat
03f792bfce feat(skills): add use-local-whisper skill package (#702)
Thanks for the great contribution @glifocat! This is a really well-structured skill — clean package, thorough docs, and solid test coverage. Hope to see more skills like this from you!
2026-03-04 15:56:31 +02:00
glifocat
5e3d8b6c2c fix(whatsapp): add error handling to messages.upsert handler (#695)
Wrap the inner message processing loop in a try-catch to prevent a
single malformed or edge-case message from crashing the entire handler.
Logs the error with remoteJid for debugging while continuing to process
remaining messages in the batch.

Co-authored-by: Ethan M <ethan@nanoclaw.local>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:02:21 +02:00
Gabi Simons
0210aa9ef1 refactor: implement multi-channel architecture (#500)
* refactor: implement channel architecture and dynamic setup

- Introduced ChannelRegistry for dynamic channel loading
- Decoupled WhatsApp from core index.ts and config.ts
- Updated setup wizard to support ENABLED_CHANNELS selection
- Refactored IPC and group registration to be channel-aware
- Verified with 359 passing tests and clean typecheck

* style: fix formatting in config.ts to pass CI

* refactor(setup): full platform-agnostic transformation

- Harmonized all instructional text and help prompts
- Implemented conditional guards for WhatsApp-specific steps
- Normalized CLI terminology across all 4 initial channels
- Unified troubleshooting and verification logic
- Verified 369 tests pass with clean typecheck

* feat(skills): transform WhatsApp into a pluggable skill

- Created .claude/skills/add-whatsapp with full 5-phase interactive setup
- Fixed TS7006 'implicit any' error in IpcDeps
- Added auto-creation of STORE_DIR to prevent crashes on fresh installs
- Verified with 369 passing tests and clean typecheck

* refactor(skills): move WhatsApp from core to pluggable skill

- Move src/channels/whatsapp.ts to add-whatsapp skill add/ folder
- Move src/channels/whatsapp.test.ts to skill add/ folder
- Move src/whatsapp-auth.ts to skill add/ folder
- Create modify/ for barrel file (src/channels/index.ts)
- Create tests/ with skill package validation test
- Update manifest with adds/modifies lists
- Remove WhatsApp deps from core package.json (now skill-managed)
- Remove WhatsApp-specific ghost language from types.ts
- Update SKILL.md to reflect skill-apply workflow

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

* refactor(skills): move setup/whatsapp-auth.ts into WhatsApp skill

The WhatsApp auth setup step is channel-specific — move it from core
to the add-whatsapp skill so core stays minimal.

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

* refactor(skills): convert Telegram skill to pluggable channel pattern

Replace the old direct-integration approach (modifying src/index.ts,
src/config.ts, src/routing.test.ts) with self-registration via the
channel registry, matching the WhatsApp skill pattern.

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

* fix(skills): fix add-whatsapp build failure and improve auth flow

- Add missing @types/qrcode-terminal to manifest npm_dependencies
  (build failed after skill apply without it)
- Make QR-browser the recommended auth method (terminal QR too small,
  pairing codes expire too fast)
- Remove "replace vs alongside" question — channels are additive
- Add pairing code retry guidance and QR-browser fallback

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

* fix: remove hardcoded WhatsApp default and stale Baileys comment

- ENABLED_CHANNELS now defaults to empty (fresh installs must configure
  channels explicitly via /setup; existing installs already have .env)
- Remove Baileys-specific comment from storeMessageDirect() in db.ts

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

* refactor(skills): convert Discord, Slack, Gmail skills to pluggable channel pattern

All channel skills now use the same self-registration pattern:
- registerChannel() factory at module load time
- Barrel file append (src/channels/index.ts) instead of orchestrator modifications
- No more *_ONLY flags (DISCORD_ONLY, SLACK_ONLY) — use ENABLED_CHANNELS instead
- Removed ~2500 lines of old modify/ files (src/index.ts, src/config.ts, src/routing.test.ts)

Gmail retains its container-runner.ts and agent-runner modifications (MCP
mount + server config) since those are independent of channel wiring.

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

* refactor: use getRegisteredChannels instead of ENABLED_CHANNELS

Remove the ENABLED_CHANNELS env var entirely. The orchestrator now
iterates getRegisteredChannelNames() from the channel registry —
channels self-register via barrel imports and their factories return
null when credentials are missing, so unconfigured channels are
skipped automatically.

Deleted setup/channels.ts (and its tests) since its sole purpose was
writing ENABLED_CHANNELS to .env. Refactored verify, groups, and
environment setup steps to detect channels by credential presence
instead of reading ENABLED_CHANNELS.

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

* docs: add breaking change notice and whatsapp migration instructions

CHANGELOG.md documents the pluggable channel architecture shift and
provides migration steps for existing WhatsApp users.

CLAUDE.md updated: Quick Context reflects multi-channel architecture,
Key Files lists registry.ts instead of whatsapp.ts, and a new
Troubleshooting section directs users to /add-whatsapp if WhatsApp
stops connecting after upgrade.

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

* docs: rewrite READMEs for pluggable multi-channel architecture

Reflects the architectural shift from a hardcoded WhatsApp bot to a
pluggable channel platform. Adds upgrading notice, Mermaid architecture
diagram, CI/License/TypeScript/PRs badges, and clarifies that slash
commands run inside the Claude Code CLI.

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

* docs: move pluggable channel architecture details to SPEC.md

Revert READMEs to original tone with only two targeted changes:
- Add upgrading notice for WhatsApp breaking change
- Mention pluggable channels in "What It Supports"

Move Mermaid diagram, channel registry internals, factory pattern
explanation, and self-registration walkthrough into docs/SPEC.md.
Update stale WhatsApp-specific references in SPEC.md to be
channel-agnostic.

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

* docs: move upgrading notice to CHANGELOG, add changelog link

Remove the "Upgrading from Pre-Pluggable Versions" section from
README.md — breaking change details belong in the CHANGELOG. Add a
Changelog section linking to CHANGELOG.md.

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

* docs: expand CHANGELOG with full PR #500 changes

Cover all changes: channel registry, WhatsApp moved to skill, removed
core dependencies, all 5 skills simplified, orchestrator refactored,
setup decoupled. Use Claude Code CLI instructions for migration.

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

* chore: bump version to 1.2.0 for pluggable channel architecture

Minor version bump — new functionality (pluggable channels) with a
managed migration path for existing WhatsApp users. Update version
references in CHANGELOG and update skill.

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

* Fix skill application

* fix: use slotted barrel file to prevent channel merge conflicts

Pre-allocate a named comment slot for each channel in
src/channels/index.ts, separated by blank lines. Each skill's
modify file only touches its own slot, so three-way merges
never conflict when applying multiple channels.

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

* fix: resolve real chat ID during setup for token-based channels

Instead of registering with `pending@telegram` (which never matches
incoming messages), the setup skill now runs an inline bot that waits
for the user to send /chatid, capturing the real chat ID before
registration.

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

* fix: setup delegates to channel skills, fix group sync and Discord metadata

- Restructure setup SKILL.md to delegate channel setup to individual
  channel skills (/add-whatsapp, /add-telegram, etc.) instead of
  reimplementing auth/registration inline with broken placeholder JIDs
- Move channel selection to step 5 where it's immediately acted on
- Fix setup/groups.ts: write sync script to temp file instead of passing
  via node -e which broke on shell escaping of newlines
- Fix Discord onChatMetadata missing channel and isGroup parameters
- Add .tmp-* to .gitignore for temp sync script cleanup

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

* fix: align add-whatsapp skill with main setup patterns

Add headless detection for auth method selection, structured inline
error handling, dedicated number DM flow, and reorder questions to
match main's trigger-first flow.

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

* fix: add missing auth script to package.json

The add-whatsapp skill adds src/whatsapp-auth.ts but doesn't add
the corresponding npm script. Setup and SKILL.md reference `npm run auth`
for WhatsApp QR terminal authentication.

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

* fix: update Discord skill tests to match onChatMetadata signature

The onChatMetadata callback now takes 5 arguments (jid, timestamp,
name, channel, isGroup) but the Discord skill tests only expected 3.
This caused skill application to roll back on test failure.

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

* docs: replace 'pluggable' jargon with clearer language

User-facing text now says "multi-channel" or describes what it does.
Developer-facing text uses "self-registering" or "channel registry".
Also removes extra badge row from README.

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

* docs: align Chinese README with English version

Remove extra badges, replace pluggable jargon, remove upgrade section
(now in CHANGELOG), add missing intro line and changelog section,
fix setup FAQ answer.

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

* fix: warn on installed-but-unconfigured channels instead of silent skip

Channels with missing credentials now emit WARN logs naming the exact
missing variable, so misconfigurations surface instead of being hidden.

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

* docs: simplify changelog to one-liner with compare link

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

* feat: add isMain flag and channel-prefixed group folders

Replace MAIN_GROUP_FOLDER constant with explicit isMain boolean on
RegisteredGroup. Group folders now use channel prefix convention
(e.g., whatsapp_main, telegram_family-chat) to prevent cross-channel
collisions.

- Add isMain to RegisteredGroup type and SQLite schema (with migration)
- Replace all folder-based main group checks with group.isMain
- Add --is-main flag to setup/register.ts
- Strip isMain from IPC payload (defense in depth)
- Update MCP tool description for channel-prefixed naming
- Update all channel SKILL.md files and documentation

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: gavrielc <gabicohen22@yahoo.com>
Co-authored-by: Koshkoshinski <daniel.milliner@gmail.com>
2026-03-03 00:35:45 +02:00
gavrielc
d92e1754ca feat: check for breaking changes after update-nanoclaw
After validation, the update skill now diffs CHANGELOG.md against the
backup tag to detect [BREAKING] entries. If found, it shows each
breaking change and offers to run the referenced migration skill.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:32:53 +02:00
gavrielc
bae8538695 Fix/shadow env in container (#646)
* fix: shadow .env file in container to prevent agents from reading secrets

The main agent's container mounts the project root read-only, which
inadvertently exposed the .env file containing API keys. Mount /dev/null
over /workspace/project/.env to shadow it — secrets are already passed
via stdin and never need to be read from disk inside the container.

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

* fix: adapt .env shadowing and runtime for Apple Container

Apple Container (VirtioFS) only supports directory mounts, not file
mounts. The previous /dev/null host-side mount over .env crashes with
VZErrorDomain "A directory sharing device configuration is invalid".

- Dockerfile: entrypoint now shadows .env via mount --bind inside the
  container, then drops privileges via setpriv to the host UID/GID
- container-runner: main containers skip --user and pass RUN_UID/RUN_GID
  env vars so entrypoint starts as root for mount --bind
- container-runtime: switch to Apple Container CLI (container), fix
  cleanupOrphans to use container list --format json
- Skill: add Dockerfile and container-runner.ts to
  convert-to-apple-container skill (v1.1.0)

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

* fix: revert src to Docker runtime, keep Apple Container in skill only

The source files should remain Docker-compatible. The Apple Container
adaptations live in the convert-to-apple-container skill and are applied
on demand.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 13:28:28 +02:00
gavrielc
80cdd23c84 chore: remove old /update skill, replaced by /update-nanoclaw
The new /update-nanoclaw skill (PR #217) replaces the old update
mechanism. Delete the old skill, update module, CLI scripts, and tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 23:23:31 +02:00
Akshan Krithick
3475e89406 skill: add /update-nanoclaw for syncing customized installs with upstream (#217)
Merge-first workflow that previews upstream changes, lets user choose
full merge / cherry-pick / rebase / abort, resolves only conflicted
files, and validates with build + test.
2026-03-01 23:19:07 +02:00
gavrielc
e0cf045518 chore: remove .claude/settings.local.json
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 18:23:39 +02:00
Darrell O'Donnell
ee7f720617 /add-slack (#366)
* feat: add Slack channel skill (/add-slack)

Slack Bot integration via @slack/bolt with Socket Mode. Can replace
WhatsApp entirely (SLACK_ONLY=true) or run alongside it.

- SlackChannel implementing Channel interface (46 unit tests)
- Socket Mode connection (no public URL needed)
- @mention translation (Slack <@UBOTID> → TRIGGER_PATTERN)
- Message splitting at 4000-char Slack API limit
- Thread flattening (threaded replies delivered as channel messages)
- User name resolution with caching
- Outgoing message queue with flush-on-reconnect
- Channel metadata sync with pagination
- Proper Bolt types (GenericMessageEvent | BotMessageEvent)
- Multi-channel orchestrator changes (conditional channel creation)
- Setup guide (SLACK_SETUP.md) and known limitations documented

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

* local settings

* adjusted when installing

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:34:34 +02:00
gavrielc
bc05d5fbea fix(add-telegram): update test assertions for 5-arg onChatMetadata signature
The telegram.ts implementation was updated to pass channel and isGroup
to onChatMetadata but the test file still asserted the old 3-arg form.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:30:54 +02:00
Gabi Simons
ae2f339fcf chore(skills): rebase core skills (telegram, discord, voice) to latest main and fix db schema gaps 2026-02-25 00:26:01 +02:00
gavrielc
3dd48f311a fix(add-gmail): restore YAML frontmatter to SKILL.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:24:08 +02:00
gavrielc
8e164f608c fix(add-gmail): graceful startup when credentials missing + poll backoff
- connect() warns and returns instead of throwing when ~/.gmail-mcp/
  credentials are missing, preventing app crash
- index.ts wraps gmail.connect() in try/catch as a safety net
- Poll loop uses exponential backoff on consecutive errors (caps at 30m)
  instead of hammering the Gmail API every 60s on auth failures
- Switch from setInterval to setTimeout chain for proper backoff timing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 00:23:11 +02:00
Fritzzzz
df0e5fc26b remove unused 2026-02-24 23:52:53 +02:00
Fritzzzz
6dd90829e1 refactor: remove GMAIL_CHANNEL_ENABLED env flag from add-gmail skill
Channel vs tool-only is now a code-level decision at skill apply time.
If the user chose channel mode, GmailChannel is wired unconditionally.
If tool-only, no channel code is added. No runtime flag needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 23:46:24 +02:00
Fritzzzz
41e2424856 refactor: restructure add-gmail skill for new skill architecture
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 22:36:32 +02:00
Guy Vago
1ff1fd6bc7 feat: add official Qodo skills and codebase intelligence (#428) 2026-02-23 23:34:24 +02:00
gavrielc
1216b5b99c feat: add /update skill for pulling upstream changes (#372)
Interactive skill that guides Claude through fetching upstream NanoClaw,
previewing changes, merging with customizations, running migrations, and
verifying the result. Includes:

- SKILL.md with 9-step update flow
- fetch-upstream.sh: detects remote, fetches, extracts tracked paths
- run-migrations.ts: discovers and runs version-ordered migrations
- post-update.ts: clears backup after conflict resolution
- update-core.ts: adds --json and --preview-only flags
- BASE_INCLUDES moved to constants.ts as single source of truth
- 16 new tests covering fetch, migrations, and CLI flags

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 01:03:13 +02:00
Tom Sella
264f855566 Replace 'ask the user' with AskUserQuestion tool in skills (#389)
- Updated add-voice-transcription to use AskUserQuestion for API key prompt
- Updated add-gmail to use AskUserQuestion for mode selection and channel config
- Updated add-discord to use AskUserQuestion for mode and token prompts
- Updated add-parallel to use AskUserQuestion for API key and permission prompts
- Updated add-telegram to use AskUserQuestion for mode and token prompts
- Updated setup to use AskUserQuestion for Node.js, Docker, and container runtime prompts

The AskUserQuestion tool provides a structured way to collect user input during
skill execution, making the interaction pattern consistent across all skills.
2026-02-23 00:08:14 +02: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
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
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
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
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
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
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
gavrielc
88140ec1bb feat: add setup skill with scripted steps (#258)
Replace inline SKILL.md instructions with executable shell scripts
for each setup phase (environment check, deps, container, auth,
groups, channels, mounts, service, verify). Scripts emit structured
status blocks for reliable parsing.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 00:23:49 +02:00
Gavriel Cohen
acdc6454db fix: WhatsApp auth improvements and LID translation for DMs
- Add pairing code auth with 515 reconnect handling (Baileys stream
  error after pairing is now retried instead of failing)
- Use Browsers.macOS('Chrome') identifier for WhatsApp compatibility
- Fix LID-to-phone translation for DMs using signalRepository.getPNForLID
- Strip device suffix (:0) from resolved phone JIDs
- Update setup skill with three auth options (browser QR, pairing code,
  terminal QR), DM channel type, and LID troubleshooting

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 22:49:04 +02:00
gavrielc
2b56fecfdc Refactor index (#156)
* feat: add Telegram channel with agent swarm support

Add Telegram as a messaging channel that can run alongside WhatsApp
or standalone (TELEGRAM_ONLY mode). Includes bot pool support for
agent swarms where each subagent appears as a different bot identity
in the group.

- Add grammy dependency for Telegram Bot API
- Route messages through tg: JID prefix convention
- Add storeMessageDirect for non-Baileys channels
- Add sender field to IPC send_message for swarm identity
- Support TELEGRAM_BOT_TOKEN, TELEGRAM_ONLY, TELEGRAM_BOT_POOL config

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

* docs: add index.ts refactor plan

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

* refactor: extract channel abstraction, IPC, and router from index.ts

Break the 1088-line monolith into focused modules:
- src/channels/whatsapp.ts: WhatsAppChannel class implementing Channel interface
- src/ipc.ts: IPC watcher and task processing with dependency injection
- src/router.ts: message formatting, outbound routing, channel lookup
- src/types.ts: Channel interface, OnInboundMessage, OnChatMetadata types

Also adds regression test suite (98 tests), updates all documentation
and skill files to reflect the new architecture.

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

* ci: add test workflow for PRs

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

* chore: remove accidentally committed pool-bot assets

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

* fix(ci): remove grammy from base dependencies

Grammy is installed by the /add-telegram skill, not a base dependency.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 00:36:37 +02:00
Gavriel Cohen
196abf67cf docs: clarify agent swarms vs teams in Telegram skill
Agent Teams work without swarm support — they just operate behind
the scenes. Swarm support makes subagents visible as separate bots.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 10:15:22 +02:00
Gavriel Cohen
126b3f4a45 feat: add Telegram agent swarm skill
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 04:58:33 +02:00
gavrielc
6f02ee530b Adds Agent Swarms
* feat: streaming container mode, IPC messaging, agent teams support

Major architectural shift from single-shot container runs to long-lived
streaming containers with IPC-based message injection.

- Agent runner: query loop with AsyncIterable prompt to keep stdin open
  for agent teams (fixes isSingleUserTurn premature shutdown)
- New standalone stdio MCP server (ipc-mcp-stdio.ts) inheritable by
  subagents, with send_message and schedule_task tools
- Streaming output: parse OUTPUT_START/END markers in real-time, send
  results to WhatsApp as they arrive
- IPC file-based messaging: host writes to ipc/{group}/input/, agent
  polls for follow-up messages without respawning containers
- Per-group settings.json with CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
- SDK bumped to 0.2.34 for TeamCreate tool support
- Container idle timeout (30min) with _close sentinel for shutdown
- Orphaned container cleanup on startup
- alwaysRespond flag for groups that skip trigger pattern check
- Uncaught exception/rejection handlers with timestamps in logger
- Combined SDK documentation into single deep dive reference

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

* chore: remove unused ipc-mcp.ts (replaced by ipc-mcp-stdio.ts)

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

* fix: clarify agent communication model in docs and tool descriptions

- CLAUDE.md (main + global): split communication instructions into
  "responding to messages" vs "scheduled tasks" sections
- send_message tool: note that scheduled task output is not sent to user
- Remove structured output (outputFormat) — not needed with current flow
- Regular output is sent to WhatsApp; scheduled task output is only logged

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

* chore: ignore dynamic group data while preserving base structure

Only track groups/main/CLAUDE.md and groups/global/CLAUDE.md. All other
group directories and files are ignored to prevent tracking user-specific
session data.

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

* fix: resolve critical bugs in streaming container mode

Bug 1 (scheduled task hang): Task scheduler now passes onOutput callback
with idle timer that writes _close sentinel after IDLE_TIMEOUT, so
containers exit cleanly instead of blocking queue slots for 30 minutes.
Scheduled tasks stay alive for interactive follow-up via IPC.

Bug 2 (timeout disabled): Remove resetTimeout() from stderr handler.
SDK writes debug logs continuously, resetting the timer on every line.
Timeout now only resets on actual output markers in stdout.

Bug 3 (trigger bypass): Piped messages in startMessageLoop now check
trigger pattern for non-main groups. Non-trigger messages accumulate in
DB and are pulled as context via getMessagesSince when a trigger arrives.

Bug 7 (non-atomic IPC writes): GroupQueue.sendMessage uses temp file +
rename for atomic writes, matching ipc-mcp-stdio.ts pattern.

Also: flip isVerbose back to false (debug leftover), add isScheduledTask
to host-side ContainerInput interface.

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

* fix: idle timer not starting + scheduled task groupFolder missing

Two bugs that prevented the scheduled task idle timeout fix from working:

1. onOutput was only called when parsed.result !== null, but session
   update markers have result: null. The idle timer never started for
   "silent" query completions, leaving containers parked at
   waitForIpcMessage until hard timeout.

2. Scheduler's onProcess callback didn't pass groupFolder to
   queue.registerProcess, so closeStdin no-oped (groupFolder was null).
   The _close sentinel was never written even when the idle timer fired.

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

* fix: duplicate messages and timestamp rollback in piping path

Two bugs introduced by the trigger context accumulation change:

1. processGroupMessages didn't advance lastAgentTimestamp until after
   the container finished. The piping path's getMessagesSince(lastAgent
   Timestamp) re-fetched messages already sent as the initial prompt,
   causing duplicates.

2. processGroupMessages overwrote lastAgentTimestamp with the original
   batch timestamp on completion, rolling back any advancement made by
   the piping path while the container was running.

Fix: advance lastAgentTimestamp immediately after building the prompt,
before starting the container. This matches the piping path behavior
and eliminates both the overlap and the rollback.

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

* fix: container idles 30 extra minutes after _close during query

When _close was detected during pollIpcDuringQuery, it was consumed
(deleted) and stream.end() was called. But after runQuery returned,
main() still emitted a session-update marker (resetting the host's idle
timer) and called waitForIpcMessage (which polled forever since _close
was already gone). The container had to wait for a second _close.

Fix: runQuery now returns closedDuringQuery. When true, main() skips
the session-update marker and waitForIpcMessage, exiting immediately.

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

* fix: resume branching, internal tags, and output forwarding

- Fix resume branching: pass resumeSessionAt with last assistant UUID
  to anchor each query loop resume to the correct conversation tree
  position. Prevents agent responses landing on invisible branches
  when agent teams subagents create parallel JSONL entries.

- Add <internal> tag stripping: agent can wrap internal reasoning in
  <internal> tags which are logged but not sent to WhatsApp. Prevents
  duplicate messages and internal monologue reaching users.

- Forward scheduled task output: scheduled tasks now send result text
  to WhatsApp (with <internal> stripping), matching regular message
  behavior. No more special-case instructions.

- Update Communication guidance in CLAUDE.md: simplified to "your
  output is sent to the user or group" with soft guidance on
  <internal> tags and send_message usage.

- Add messaging behavior docs to schedule_task tool: prompts the
  scheduling agent to include guidance on whether the task should
  always/conditionally/never message the user.

- Mount security: containerPath now optional, defaults to basename
  of hostPath.

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

* fix: cursor rollback on error, flush guard, verbose logging

- Roll back lastAgentTimestamp on container error so retries can
  re-process the messages instead of silently losing them.

- Add guard flag to flushOutgoingQueue to prevent duplicate sends
  from concurrent flushes during rapid WA reconnects.

- Revert isVerbose from hardcoded false back to env-based check
  (LOG_LEVEL=debug|trace).

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

* fix: orphan container cleanup was silently failing

The startup cleanup used `container ls --format {{.Names}}` which is
Docker Go-template syntax. Apple Container only supports `--format json`
or `--format table`. The command errored with exit code 64, but the
catch block silently swallowed it — orphan containers were never cleaned
up on restart.

Fixed to use `--format json` and parse `configuration.id` from the
JSON output. Also filters by `status: running` and logs a warning on
failure instead of silently catching.

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

* docs: add Discord badge and community section

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

* fix: idle timer reset on null results and flush queue message loss

- Only reset idle timer on actual results (non-null), not session-update
  markers. Prevents containers staying alive 30 extra minutes after the
  agent finishes work.
- flushOutgoingQueue now uses shift() instead of splice(0) so unattempted
  messages stay in the queue if an unexpected error bails the loop.

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

* docs: add Agent Swarms to README

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

* fix: update Telegram skill for current architecture

Rewrite integration instructions to match the per-group queue/SQLite
architecture: remove onMessage callback pattern (store to DB, let
message loop pick up), fix startSchedulerLoop signature, add
TELEGRAM_ONLY service startup, SQLite registration, data/env/env sync,
@mention-to-trigger translation, and BotFather group privacy docs.

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

* fix: Telegram skill message chunking, media placeholders, chat discovery

- Split long messages at Telegram's 4096 char limit to prevent silent
  send failures
- Store placeholder text for non-text messages (photos, voice, stickers,
  etc.) so the agent knows media was sent
- Update getAvailableGroups filter to include tg: chats so the agent can
  discover and register Telegram chats via IPC
- Fix removal step numbering

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

* docs: update REQUIREMENTS.md and SPEC.md for SQLite architecture

- Replace all registered_groups.json / sessions.json / router_state.json
  references with SQLite equivalents
- Fix CONTAINER_TIMEOUT default (300000 → 1800000)
- Add missing config exports (IDLE_TIMEOUT, MAX_CONCURRENT_CONTAINERS)
- Update folder structure: add missing src files (logger, group-queue,
  mount-security), remove non-existent utils.ts, list all skills
- Fix agent-runner entry (ipc-mcp.ts → ipc-mcp-stdio.ts)
- Update startup sequence to reflect per-group queue architecture
- Fix env mounting description (data/env/env, not extracted vars)
- Update troubleshooting to use sqlite3 commands

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

* docs: fix README architecture description, revert SPEC.md env error

- README: update architecture blurb to mention per-group queue, add
  group-queue.ts to key files, update file descriptions
- SPEC.md: restore correct credential filtering description (only auth
  vars are extracted from .env, not the full file)

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 02:50:43 +02:00
jmr
6cd165f391 feat: Add /add-telegram skill for Telegram channel support (#83)
Add skill to guide users through adding Telegram as a messaging channel.

- Replace WhatsApp or run alongside it
- Support for private chats and groups
- Built-in /chatid command for easy registration
- Flexible triggering: main chat, respondToAll, @mentions, or trigger pattern
- Grammy library for modern TypeScript-first Telegram integration

No source code changes - skill provides step-by-step implementation guide.
2026-02-09 01:48:47 +02:00
Tom Granot
b2bd141650 Fix orphan container cleanup and update installation steps (#149)
Updated instructions for handling orphaned containers and Zod version conflicts. Added critical cleanup steps for the NanoClaw service.
2026-02-09 01:32:48 +02:00
gavrielc
f26468c9b0 fix: setup skill reliability, requiresTrigger option, agent-browser visibility
Setup skill fixes:
- Run QR auth in foreground with long timeout, not background
- Replace fragile message-based registration with DB group sync lookup
- Personal chats: ask for phone number instead of querying empty DB
- Consolidate trigger word + security model + channel selection into one step
- Remove `timeout` shell command (unavailable on macOS), use Bash tool timeout
- Query 40 groups, display 10 at a time, support name lookup

requiresTrigger support:
- Add requiresTrigger field to RegisteredGroup type and DB schema
- Skip trigger check when requiresTrigger is false (for solo/personal chats)
- Main group still always processes all messages (unchanged)

Agent-browser visibility:
- Append global CLAUDE.md to non-main agent system prompts via SDK
- Add browser tool docs to global and main CLAUDE.md
- Update skill description to be broader (not just "web testing")
- Reference agent-browser.md in root CLAUDE.md key files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 01:39:31 +02:00
Tom Granot
1f8cd26a83 Add voice transcription skill using OpenAI Whisper API (#77)
New skill `/add-voice-transcription` that guides users through adding
automatic voice message transcription to NanoClaw.

Features:
- Uses OpenAI Whisper API for transcription (~$0.006/min)
- Provider-agnostic architecture (supports future Groq/Deepgram/local)
- Secure API key storage in gitignored config file
- Graceful fallback when transcription unavailable
- Voice messages stored as `[Voice: <transcript>]` in database

The skill includes:
- Step-by-step implementation guide
- Database and message handler integration
- Configuration options (enable/disable, fallback message)
- Troubleshooting section
- Security notes and cost management tips
- Removal instructions

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 13:03:50 +02:00
BaiJunjie
c9ca34a518 Add X integration skill (#52) 2026-02-04 00:27:04 +02:00
Gavriel Cohen
7ff9a65792 Update setup skill to use claude setup-token for auth
Replace manual credential extraction with the proper CLI command.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 21:03:35 +02:00
Tom Granot
1a32bff6ec Improve setup UX with AskUserQuestion tool and security education (#60)
- Add UX note instructing Claude to use AskUserQuestion tool for better
  interactive experience during setup
- Add new Section 7 explaining the main channel's elevated privileges
  (admin control portal) before registration
- Include interactive security acknowledgment with follow-up for users
  choosing shared groups
- Renumber subsequent sections (7→8, 8→9, 9→10)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-03 17:04:45 +02:00
gavrielc
ff23125800 Add Docker support and integrate /convert-to-docker into setup flow
- Update setup skill to detect platform and offer Docker/Apple Container choice
- On Linux, automatically use Docker via /convert-to-docker skill
- On macOS, ask user preference if Apple Container not installed
- Update README to reflect Docker support and Linux compatibility
- Fix exact line number reference in convert-to-docker skill
- Add thank you to @dotsetgreg for the Docker skill contribution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:35:27 +02:00