Merge branch 'main' into fix/update-nanoclaw-skill-v2

This commit is contained in:
Gabi Simons
2026-05-03 17:47:20 +03:00
committed by GitHub
4 changed files with 30 additions and 6 deletions
+1
View File
@@ -16,6 +16,7 @@ Thanks to everyone who has contributed to NanoClaw!
- [flobo3](https://github.com/flobo3) — Flo
- [edwinwzhe](https://github.com/edwinwzhe) — Edwin He
- [scottgl9](https://github.com/scottgl9) — Scott Glover
- [ingyukoh](https://github.com/ingyukoh) — Ingyu Koh
- [cschmidt](https://github.com/cschmidt) — Carl Schmidt
- [leonalfredbot-ship-it](https://github.com/leonalfredbot-ship-it) — Alfred-the-buttler
- [moktamd](https://github.com/moktamd)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "nanoclaw",
"version": "2.0.27",
"version": "2.0.28",
"description": "Personal Claude assistant. Lightweight, secure, customizable.",
"type": "module",
"packageManager": "pnpm@10.33.0",
+25 -2
View File
@@ -60,7 +60,7 @@ import { isValidTimezone } from '../src/timezone.js';
const CLI_AGENT_NAME = 'Terminal Agent';
const RUN_START = Date.now();
type ChannelChoice = 'telegram' | 'discord' | 'whatsapp' | 'signal' | 'teams' | 'slack' | 'imessage' | 'skip';
type ChannelChoice = 'telegram' | 'discord' | 'whatsapp' | 'signal' | 'teams' | 'slack' | 'imessage' | 'other' | 'skip';
async function main(): Promise<void> {
// Make sure ~/.local/bin is on PATH for every child process we spawn.
@@ -441,7 +441,7 @@ async function main(): Promise<void> {
if (!skip.has('channel')) {
channelChoice = await askChannelChoice();
if (channelChoice !== 'skip') {
if (channelChoice !== 'skip' && channelChoice !== 'other') {
await resolveDisplayName();
}
if (channelChoice === 'telegram') {
@@ -458,6 +458,8 @@ async function main(): Promise<void> {
await runSlackChannel(displayName!);
} else if (channelChoice === 'imessage') {
await runIMessageChannel(displayName!);
} else if (channelChoice === 'other') {
await askOtherChannelName();
} else {
p.log.info(
brandBody(
@@ -1076,6 +1078,7 @@ async function askChannelChoice(): Promise<ChannelChoice> {
hint: 'needs public URL',
},
{ value: 'teams', label: 'Yes, connect Microsoft Teams', hint: 'complex setup' },
{ value: 'other', label: 'Other…', hint: 'install via /add-<name> after setup' },
{ value: 'skip', label: 'Skip for now', hint: "I'll just use the terminal" },
],
}),
@@ -1085,6 +1088,26 @@ async function askChannelChoice(): Promise<ChannelChoice> {
return choice;
}
async function askOtherChannelName(): Promise<void> {
const answer = ensureAnswer(
await p.text({
message: 'Which channel would you like to install?',
placeholder: 'e.g. matrix, github, linear, webex',
}),
);
const name = (answer as string).trim().toLowerCase().replace(/^\/?(add-)?/, '');
setupLog.userInput('other_channel', name);
phEmit('channel_other_named', { channel: name });
p.log.info(
brandBody(
wrapForGutter(
`No bash installer for ${k.bold(name)} — open Claude Code after setup and run ${k.bold(`/add-${name}`)} to install it.`,
4,
),
),
);
}
// ─── interactive / env helpers ─────────────────────────────────────────
function ensureLocalBinOnPath(): void {
+3 -3
View File
@@ -253,12 +253,12 @@ export function createChatSdkBridge(config: ChatSdkBridgeConfig): ChannelAdapter
// Chat SDK dispatch (handling-events.mdx §"Handler dispatch order") is
// exclusive: subscribed → onSubscribedMessage; unsubscribed+mention →
// onNewMention; unsubscribed+pattern-match → onNewMessage. Registering
// with `/./` lets the router see every plain message on every
// unsubscribed thread the bot can see. The router short-circuits via
// with `/[\s\S]*/` lets the router see every plain message (including
// media-only messages with empty text) on every unsubscribed thread the
// getMessagingGroupWithAgentCount (~1 DB read) for unwired channels,
// so forwarding every one is cheap enough to not need a bridge-side
// flood gate.
chat.onNewMessage(/./, async (thread, message) => {
chat.onNewMessage(/[\s\S]*/, async (thread, message) => {
const channelId = adapter.channelIdFromThreadId(thread.id);
await setupConfig.onInbound(channelId, thread.id, await messageToInbound(message, false, true));
});