chat-heimerdinger 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -118686,37 +118686,51 @@ import { basename, join as join2 } from "node:path";
118686
118686
  class ClaudeCodeService {
118687
118687
  claudeConfigPath;
118688
118688
  projectsDir;
118689
- claudeBinaryPath;
118689
+ claudeBinaryPath = null;
118690
118690
  constructor(claudeConfigPath = CLAUDE_CONFIG_FILE) {
118691
118691
  this.claudeConfigPath = claudeConfigPath;
118692
118692
  this.projectsDir = CLAUDE_PROJECTS_DIR;
118693
+ }
118694
+ getClaudeBinary() {
118695
+ if (this.claudeBinaryPath)
118696
+ return this.claudeBinaryPath;
118693
118697
  this.claudeBinaryPath = this.findClaudeBinary();
118698
+ return this.claudeBinaryPath;
118694
118699
  }
118695
118700
  findClaudeBinary() {
118701
+ const home = process.env.HOME || "";
118702
+ const extraPaths = [
118703
+ process.env.NVM_BIN,
118704
+ "/usr/local/bin",
118705
+ "/usr/bin",
118706
+ "/bin",
118707
+ `${home}/.local/bin`,
118708
+ `${home}/.local/share/pnpm`,
118709
+ `${home}/.bun/bin`
118710
+ ].filter(Boolean);
118711
+ const fullPath = [...extraPaths, ...process.env.PATH?.split(":") || []];
118712
+ const dedupedPath = [...new Set(fullPath)].join(":");
118696
118713
  try {
118697
- const shellPaths = [
118698
- "/usr/local/bin",
118699
- "/usr/bin",
118700
- "/bin",
118701
- `${process.env.HOME}/.local/bin`,
118702
- ...process.env.PATH?.split(":") || []
118703
- ];
118704
118714
  const path = execSync("which claude", {
118705
118715
  encoding: "utf-8",
118706
- env: { ...process.env, PATH: shellPaths.join(":") }
118716
+ env: { ...process.env, PATH: dedupedPath }
118707
118717
  }).trim();
118708
118718
  if (path && existsSync3(path)) {
118709
- consola.debug("Found claude binary at:", path);
118719
+ consola.info("Found claude binary at:", path);
118710
118720
  return path;
118711
118721
  }
118712
- } catch {}
118722
+ } catch {
118723
+ consola.warn("which claude failed, trying fallback paths...");
118724
+ }
118713
118725
  const commonPaths = [
118714
118726
  "/usr/local/bin/claude",
118715
118727
  "/usr/bin/claude",
118716
- `${process.env.HOME}/.local/bin/claude`,
118717
- `${process.env.HOME}/.nvm/versions/node/${process.version}/bin/claude`
118728
+ `${home}/.local/bin/claude`,
118729
+ `${home}/.local/share/pnpm/claude`,
118730
+ `${home}/.bun/bin/claude`,
118731
+ `${home}/.nvm/versions/node/${process.version}/bin/claude`
118718
118732
  ];
118719
- const nvmDir = process.env.NVM_DIR || `${process.env.HOME}/.nvm`;
118733
+ const nvmDir = process.env.NVM_DIR || `${home}/.nvm`;
118720
118734
  if (existsSync3(nvmDir)) {
118721
118735
  try {
118722
118736
  const nvmVersionsDir = join2(nvmDir, "versions", "node");
@@ -118730,11 +118744,11 @@ class ClaudeCodeService {
118730
118744
  }
118731
118745
  for (const p2 of commonPaths) {
118732
118746
  if (existsSync3(p2)) {
118733
- consola.debug("Found claude binary at:", p2);
118747
+ consola.info("Found claude binary at fallback path:", p2);
118734
118748
  return p2;
118735
118749
  }
118736
118750
  }
118737
- consola.warn('Could not find claude binary, using "claude" and relying on PATH');
118751
+ consola.error("Could not find claude binary! Searched PATH:", dedupedPath, "and common paths:", commonPaths.join(", "));
118738
118752
  return "claude";
118739
118753
  }
118740
118754
  async isAvailable() {
@@ -118823,10 +118837,14 @@ class ClaudeCodeService {
118823
118837
  consola.info("Starting new session");
118824
118838
  }
118825
118839
  args.push(prompt2);
118826
- const spawnInfo = `Spawning claude: binary=${this.claudeBinaryPath}, cwd=${projectDir}`;
118827
- consola.info(spawnInfo);
118828
- console.log(spawnInfo);
118829
- const proc = spawn2(this.claudeBinaryPath, args, {
118840
+ let binaryPath = this.getClaudeBinary();
118841
+ if (binaryPath !== "claude" && !existsSync3(binaryPath)) {
118842
+ consola.warn(`Claude binary no longer exists at ${binaryPath}, re-finding...`);
118843
+ this.claudeBinaryPath = null;
118844
+ binaryPath = this.getClaudeBinary();
118845
+ }
118846
+ consola.info(`Spawning claude: binary=${binaryPath}, cwd=${projectDir}`);
118847
+ const proc = spawn2(binaryPath, args, {
118830
118848
  cwd: projectDir,
118831
118849
  stdio: ["ignore", "pipe", "pipe"],
118832
118850
  env: { ...process.env },
@@ -118889,7 +118907,7 @@ class ClaudeCodeService {
118889
118907
  }
118890
118908
  });
118891
118909
  proc.on("error", (err) => {
118892
- const errorMsg = `Claude process error: ${err.message}, binary path was: ${this.claudeBinaryPath}`;
118910
+ const errorMsg = `Claude process error: ${err.message}, binary path was: ${binaryPath}`;
118893
118911
  consola.error(errorMsg);
118894
118912
  console.error(errorMsg, err);
118895
118913
  reject(err);
@@ -119826,9 +119844,11 @@ class SlackAdapter {
119826
119844
  name = "slack";
119827
119845
  app;
119828
119846
  config;
119847
+ botUserId = null;
119829
119848
  messageHandlers = [];
119830
119849
  audioMessageHandlers = [];
119831
119850
  interactionHandlers = [];
119851
+ processedMessages = new Set;
119832
119852
  constructor(config) {
119833
119853
  this.config = config;
119834
119854
  this.app = new import_bolt.App({
@@ -119895,7 +119915,14 @@ class SlackAdapter {
119895
119915
  async start() {
119896
119916
  consola.info("Slack: Starting Socket Mode...");
119897
119917
  await this.app.start();
119898
- consola.success("Slack adapter connected");
119918
+ try {
119919
+ const authResult = await this.app.client.auth.test();
119920
+ this.botUserId = authResult.user_id || null;
119921
+ consola.info(`Bot user ID: ${this.botUserId}`);
119922
+ } catch (error) {
119923
+ consola.warn("Failed to get bot user ID:", error);
119924
+ }
119925
+ consola.success("Slack adapter connected and ready");
119899
119926
  }
119900
119927
  async stop() {
119901
119928
  await this.app.stop();
@@ -119929,9 +119956,20 @@ class SlackAdapter {
119929
119956
  setupEventHandlers() {
119930
119957
  this.app.event("message", async ({ event, client }) => {
119931
119958
  const msg = event;
119932
- if (msg.bot_id)
119959
+ console.log(`[slack:message] ts=${msg.ts} user=${msg.user} channel=${msg.channel} channel_type=${msg.channel_type} subtype=${msg.subtype} bot_id=${msg.bot_id} text="${(msg.text || "").slice(0, 40)}"`);
119960
+ if (msg.bot_id) {
119961
+ consola.info(`[msg filter] skip: bot_id=${msg.bot_id}`);
119962
+ return;
119963
+ }
119964
+ if (this.botUserId && msg.user === this.botUserId) {
119965
+ consola.info(`[msg filter] skip: bot user ${msg.user}`);
119966
+ return;
119967
+ }
119968
+ if (msg.edited) {
119969
+ consola.info(`[msg filter] skip: edited msg from ${msg.user}`);
119933
119970
  return;
119934
- consola.debug(`Message event: subtype=${msg.subtype}, has_files=${!!msg.files?.length}`);
119971
+ }
119972
+ consola.info(`[msg event] user=${msg.user} subtype=${msg.subtype} isDM=${msg.channel_type === "im"} text="${(msg.text || "").slice(0, 50)}"`);
119935
119973
  const isDM = msg.channel_type === "im";
119936
119974
  const hasMention = /<@[A-Z0-9]+>/i.test(msg.text || "");
119937
119975
  const isFileShare = msg.subtype === "file_share";
@@ -119965,18 +120003,38 @@ class SlackAdapter {
119965
120003
  const text = (msg.text || "").replace(/<@[A-Z0-9]+>/gi, "").trim();
119966
120004
  if (!text)
119967
120005
  return;
120006
+ this.processedMessages.add(msg.ts);
120007
+ if (this.processedMessages.size > 100) {
120008
+ const entries = [...this.processedMessages];
120009
+ this.processedMessages = new Set(entries.slice(-50));
120010
+ }
119968
120011
  for (const handler of this.messageHandlers) {
119969
120012
  await handler({ text, context });
119970
120013
  }
119971
120014
  });
119972
120015
  this.app.event("app_mention", async ({ event }) => {
120016
+ console.log(`[slack:app_mention] ts=${event.ts} user=${event.user} channel=${event.channel} text="${(event.text || "").slice(0, 40)}"`);
120017
+ consola.info(`[app_mention] user=${event.user} channel=${event.channel} text="${(event.text || "").slice(0, 50)}"`);
120018
+ if (this.processedMessages.has(event.ts)) {
120019
+ consola.info(`[app_mention] skip: already processed by message handler ts=${event.ts}`);
120020
+ return;
120021
+ }
120022
+ if (this.botUserId && event.user === this.botUserId) {
120023
+ consola.info(`[app_mention] skip: bot user ${event.user}`);
120024
+ return;
120025
+ }
119973
120026
  const text = (event.text || "").replace(/<@[A-Z0-9]+>/gi, "").trim();
120027
+ if (!text) {
120028
+ consola.info("[app_mention] skip: empty text after removing mentions");
120029
+ return;
120030
+ }
119974
120031
  const context = {
119975
120032
  channelId: event.channel,
119976
120033
  userId: event.user || "",
119977
120034
  threadTs: event.thread_ts || event.ts,
119978
120035
  messageTs: event.ts
119979
120036
  };
120037
+ this.processedMessages.add(event.ts);
119980
120038
  for (const handler of this.messageHandlers) {
119981
120039
  await handler({ text, context });
119982
120040
  }
@@ -120415,32 +120473,56 @@ class MessageProcessor {
120415
120473
  }
120416
120474
  return;
120417
120475
  }
120476
+ getChannelIds() {
120477
+ return [...this.userStates.keys()];
120478
+ }
120418
120479
  async handleMessage(message, adapter) {
120419
120480
  const { text, context } = message;
120420
120481
  const command = text.trim().toLowerCase();
120421
- consola.debug(`Received message: ${text}`);
120482
+ consola.info(`[handleMessage] text="${text}" command="${command}" channel=${context.channelId}`);
120422
120483
  if (command === "help" || command === "/help") {
120484
+ consola.info("[handleMessage] -> help");
120423
120485
  await this.sendHelp(adapter, context);
120424
120486
  return;
120425
120487
  }
120426
120488
  if (command === "/projects" || command === "projects") {
120489
+ consola.info("[handleMessage] -> projects");
120427
120490
  await this.sendProjectList(adapter, context);
120428
120491
  return;
120429
120492
  }
120430
120493
  if (command === "/status" || command === "status") {
120494
+ consola.info("[handleMessage] -> status");
120431
120495
  await this.sendStatus(adapter, context);
120432
120496
  return;
120433
120497
  }
120434
- if (command.startsWith("/project ")) {
120435
- const projectName = text.slice(9).trim();
120498
+ if (command === "/project" || command === "project") {
120499
+ consola.info("[handleMessage] -> showProjectSelector");
120500
+ await this.showProjectSelector(adapter, context);
120501
+ return;
120502
+ }
120503
+ if (command.startsWith("/project ") || command.startsWith("project ")) {
120504
+ const projectName = command.startsWith("/") ? text.slice(9).trim() : text.slice(8).trim();
120505
+ consola.info(`[handleMessage] -> selectProject: ${projectName}`);
120436
120506
  await this.selectProject(adapter, context, projectName);
120437
120507
  return;
120438
120508
  }
120439
- if (command.startsWith("/session ")) {
120440
- const sessionId = text.slice(9).trim();
120509
+ if (command.startsWith("/session ") || command.startsWith("session ")) {
120510
+ const sessionId = command.startsWith("/") ? text.slice(9).trim() : text.slice(8).trim();
120511
+ consola.info(`[handleMessage] -> selectSession: ${sessionId}`);
120441
120512
  await this.selectSession(adapter, context, sessionId);
120442
120513
  return;
120443
120514
  }
120515
+ if (command === "/stop" || command === "stop") {
120516
+ consola.info("[handleMessage] -> stop");
120517
+ await this.handleStopExecution(adapter, context);
120518
+ return;
120519
+ }
120520
+ if (command === "/clear" || command === "clear") {
120521
+ consola.info("[handleMessage] -> clear");
120522
+ await this.handleClearSession(adapter, context);
120523
+ return;
120524
+ }
120525
+ consola.info("[handleMessage] -> handlePrompt");
120444
120526
  await this.handlePrompt(adapter, context, text);
120445
120527
  }
120446
120528
  async handleAudioMessage(message, adapter) {
@@ -120650,11 +120732,14 @@ _Cost: $${cost.toFixed(4)}_`;
120650
120732
  const help = `*Heimerdinger - Claude Code Bridge*
120651
120733
 
120652
120734
  Available commands:
120653
- \`/projects\` - List available projects
120654
- \`/project <name>\` - Select a project
120655
- \`/session <id>\` - Resume a session
120656
- \`/status\` - Show current status
120657
- \`/help\` - Show this help
120735
+ • \`project\` - Select a project (interactive)
120736
+ \`project <name>\` - Select a project by name
120737
+ \`projects\` - List available projects
120738
+ • \`session <id>\` - Resume a session
120739
+ • \`stop\` - Stop current execution
120740
+ • \`clear\` - Clear session
120741
+ • \`status\` - Show current status
120742
+ • \`help\` - Show this help
120658
120743
 
120659
120744
  Or just send a message to start coding with Claude!`;
120660
120745
  await adapter.sendMessage(context.channelId, help);
@@ -121419,6 +121504,12 @@ class HeimerdingerServer {
121419
121504
  });
121420
121505
  await adapter.start();
121421
121506
  consola.success("Slack adapter started");
121507
+ const channelStates = this.messageProcessor.getChannelIds();
121508
+ for (const channelId of channelStates) {
121509
+ try {
121510
+ await adapter.sendMessage(channelId, `\uD83D\uDFE2 Heimerdinger online (${new Date().toLocaleTimeString()})`);
121511
+ } catch {}
121512
+ }
121422
121513
  } catch (error) {
121423
121514
  consola.error("Failed to initialize Slack adapter:", error);
121424
121515
  }
@@ -121525,13 +121616,17 @@ Shutting down...`);
121525
121616
  if (!existsSync8(daemonScript)) {
121526
121617
  throw new Error(`Daemon script not found at: ${daemonScript}`);
121527
121618
  }
121619
+ const home = process.env.HOME || "";
121528
121620
  const pathDirs = [
121621
+ process.env.NVM_BIN,
121529
121622
  "/usr/local/bin",
121530
121623
  "/usr/bin",
121531
121624
  "/bin",
121532
- `${process.env.HOME}/.local/bin`,
121625
+ `${home}/.local/bin`,
121626
+ `${home}/.local/share/pnpm`,
121627
+ `${home}/.bun/bin`,
121533
121628
  ...process.env.PATH?.split(":") || []
121534
- ];
121629
+ ].filter(Boolean);
121535
121630
  const fullPath = [...new Set(pathDirs)].join(":");
121536
121631
  const proc = spawn4("sh", ["-c", `node "${daemonScript}" >> "${LOG_FILE}" 2>&1`], {
121537
121632
  cwd: process.cwd(),
@@ -116259,9 +116259,11 @@ class SlackAdapter {
116259
116259
  name = "slack";
116260
116260
  app;
116261
116261
  config;
116262
+ botUserId = null;
116262
116263
  messageHandlers = [];
116263
116264
  audioMessageHandlers = [];
116264
116265
  interactionHandlers = [];
116266
+ processedMessages = new Set;
116265
116267
  constructor(config) {
116266
116268
  this.config = config;
116267
116269
  this.app = new import_bolt.App({
@@ -116328,7 +116330,14 @@ class SlackAdapter {
116328
116330
  async start() {
116329
116331
  consola.info("Slack: Starting Socket Mode...");
116330
116332
  await this.app.start();
116331
- consola.success("Slack adapter connected");
116333
+ try {
116334
+ const authResult = await this.app.client.auth.test();
116335
+ this.botUserId = authResult.user_id || null;
116336
+ consola.info(`Bot user ID: ${this.botUserId}`);
116337
+ } catch (error) {
116338
+ consola.warn("Failed to get bot user ID:", error);
116339
+ }
116340
+ consola.success("Slack adapter connected and ready");
116332
116341
  }
116333
116342
  async stop() {
116334
116343
  await this.app.stop();
@@ -116362,9 +116371,20 @@ class SlackAdapter {
116362
116371
  setupEventHandlers() {
116363
116372
  this.app.event("message", async ({ event, client }) => {
116364
116373
  const msg = event;
116365
- if (msg.bot_id)
116374
+ console.log(`[slack:message] ts=${msg.ts} user=${msg.user} channel=${msg.channel} channel_type=${msg.channel_type} subtype=${msg.subtype} bot_id=${msg.bot_id} text="${(msg.text || "").slice(0, 40)}"`);
116375
+ if (msg.bot_id) {
116376
+ consola.info(`[msg filter] skip: bot_id=${msg.bot_id}`);
116377
+ return;
116378
+ }
116379
+ if (this.botUserId && msg.user === this.botUserId) {
116380
+ consola.info(`[msg filter] skip: bot user ${msg.user}`);
116366
116381
  return;
116367
- consola.debug(`Message event: subtype=${msg.subtype}, has_files=${!!msg.files?.length}`);
116382
+ }
116383
+ if (msg.edited) {
116384
+ consola.info(`[msg filter] skip: edited msg from ${msg.user}`);
116385
+ return;
116386
+ }
116387
+ consola.info(`[msg event] user=${msg.user} subtype=${msg.subtype} isDM=${msg.channel_type === "im"} text="${(msg.text || "").slice(0, 50)}"`);
116368
116388
  const isDM = msg.channel_type === "im";
116369
116389
  const hasMention = /<@[A-Z0-9]+>/i.test(msg.text || "");
116370
116390
  const isFileShare = msg.subtype === "file_share";
@@ -116398,18 +116418,38 @@ class SlackAdapter {
116398
116418
  const text = (msg.text || "").replace(/<@[A-Z0-9]+>/gi, "").trim();
116399
116419
  if (!text)
116400
116420
  return;
116421
+ this.processedMessages.add(msg.ts);
116422
+ if (this.processedMessages.size > 100) {
116423
+ const entries = [...this.processedMessages];
116424
+ this.processedMessages = new Set(entries.slice(-50));
116425
+ }
116401
116426
  for (const handler of this.messageHandlers) {
116402
116427
  await handler({ text, context });
116403
116428
  }
116404
116429
  });
116405
116430
  this.app.event("app_mention", async ({ event }) => {
116431
+ console.log(`[slack:app_mention] ts=${event.ts} user=${event.user} channel=${event.channel} text="${(event.text || "").slice(0, 40)}"`);
116432
+ consola.info(`[app_mention] user=${event.user} channel=${event.channel} text="${(event.text || "").slice(0, 50)}"`);
116433
+ if (this.processedMessages.has(event.ts)) {
116434
+ consola.info(`[app_mention] skip: already processed by message handler ts=${event.ts}`);
116435
+ return;
116436
+ }
116437
+ if (this.botUserId && event.user === this.botUserId) {
116438
+ consola.info(`[app_mention] skip: bot user ${event.user}`);
116439
+ return;
116440
+ }
116406
116441
  const text = (event.text || "").replace(/<@[A-Z0-9]+>/gi, "").trim();
116442
+ if (!text) {
116443
+ consola.info("[app_mention] skip: empty text after removing mentions");
116444
+ return;
116445
+ }
116407
116446
  const context = {
116408
116447
  channelId: event.channel,
116409
116448
  userId: event.user || "",
116410
116449
  threadTs: event.thread_ts || event.ts,
116411
116450
  messageTs: event.ts
116412
116451
  };
116452
+ this.processedMessages.add(event.ts);
116413
116453
  for (const handler of this.messageHandlers) {
116414
116454
  await handler({ text, context });
116415
116455
  }
@@ -116643,37 +116683,51 @@ import { basename, join as join2 } from "node:path";
116643
116683
  class ClaudeCodeService {
116644
116684
  claudeConfigPath;
116645
116685
  projectsDir;
116646
- claudeBinaryPath;
116686
+ claudeBinaryPath = null;
116647
116687
  constructor(claudeConfigPath = CLAUDE_CONFIG_FILE) {
116648
116688
  this.claudeConfigPath = claudeConfigPath;
116649
116689
  this.projectsDir = CLAUDE_PROJECTS_DIR;
116690
+ }
116691
+ getClaudeBinary() {
116692
+ if (this.claudeBinaryPath)
116693
+ return this.claudeBinaryPath;
116650
116694
  this.claudeBinaryPath = this.findClaudeBinary();
116695
+ return this.claudeBinaryPath;
116651
116696
  }
116652
116697
  findClaudeBinary() {
116698
+ const home = process.env.HOME || "";
116699
+ const extraPaths = [
116700
+ process.env.NVM_BIN,
116701
+ "/usr/local/bin",
116702
+ "/usr/bin",
116703
+ "/bin",
116704
+ `${home}/.local/bin`,
116705
+ `${home}/.local/share/pnpm`,
116706
+ `${home}/.bun/bin`
116707
+ ].filter(Boolean);
116708
+ const fullPath = [...extraPaths, ...process.env.PATH?.split(":") || []];
116709
+ const dedupedPath = [...new Set(fullPath)].join(":");
116653
116710
  try {
116654
- const shellPaths = [
116655
- "/usr/local/bin",
116656
- "/usr/bin",
116657
- "/bin",
116658
- `${process.env.HOME}/.local/bin`,
116659
- ...process.env.PATH?.split(":") || []
116660
- ];
116661
116711
  const path = execSync("which claude", {
116662
116712
  encoding: "utf-8",
116663
- env: { ...process.env, PATH: shellPaths.join(":") }
116713
+ env: { ...process.env, PATH: dedupedPath }
116664
116714
  }).trim();
116665
116715
  if (path && existsSync2(path)) {
116666
- consola.debug("Found claude binary at:", path);
116716
+ consola.info("Found claude binary at:", path);
116667
116717
  return path;
116668
116718
  }
116669
- } catch {}
116719
+ } catch {
116720
+ consola.warn("which claude failed, trying fallback paths...");
116721
+ }
116670
116722
  const commonPaths = [
116671
116723
  "/usr/local/bin/claude",
116672
116724
  "/usr/bin/claude",
116673
- `${process.env.HOME}/.local/bin/claude`,
116674
- `${process.env.HOME}/.nvm/versions/node/${process.version}/bin/claude`
116725
+ `${home}/.local/bin/claude`,
116726
+ `${home}/.local/share/pnpm/claude`,
116727
+ `${home}/.bun/bin/claude`,
116728
+ `${home}/.nvm/versions/node/${process.version}/bin/claude`
116675
116729
  ];
116676
- const nvmDir = process.env.NVM_DIR || `${process.env.HOME}/.nvm`;
116730
+ const nvmDir = process.env.NVM_DIR || `${home}/.nvm`;
116677
116731
  if (existsSync2(nvmDir)) {
116678
116732
  try {
116679
116733
  const nvmVersionsDir = join2(nvmDir, "versions", "node");
@@ -116687,11 +116741,11 @@ class ClaudeCodeService {
116687
116741
  }
116688
116742
  for (const p of commonPaths) {
116689
116743
  if (existsSync2(p)) {
116690
- consola.debug("Found claude binary at:", p);
116744
+ consola.info("Found claude binary at fallback path:", p);
116691
116745
  return p;
116692
116746
  }
116693
116747
  }
116694
- consola.warn('Could not find claude binary, using "claude" and relying on PATH');
116748
+ consola.error("Could not find claude binary! Searched PATH:", dedupedPath, "and common paths:", commonPaths.join(", "));
116695
116749
  return "claude";
116696
116750
  }
116697
116751
  async isAvailable() {
@@ -116780,10 +116834,14 @@ class ClaudeCodeService {
116780
116834
  consola.info("Starting new session");
116781
116835
  }
116782
116836
  args.push(prompt2);
116783
- const spawnInfo = `Spawning claude: binary=${this.claudeBinaryPath}, cwd=${projectDir}`;
116784
- consola.info(spawnInfo);
116785
- console.log(spawnInfo);
116786
- const proc = spawn(this.claudeBinaryPath, args, {
116837
+ let binaryPath = this.getClaudeBinary();
116838
+ if (binaryPath !== "claude" && !existsSync2(binaryPath)) {
116839
+ consola.warn(`Claude binary no longer exists at ${binaryPath}, re-finding...`);
116840
+ this.claudeBinaryPath = null;
116841
+ binaryPath = this.getClaudeBinary();
116842
+ }
116843
+ consola.info(`Spawning claude: binary=${binaryPath}, cwd=${projectDir}`);
116844
+ const proc = spawn(binaryPath, args, {
116787
116845
  cwd: projectDir,
116788
116846
  stdio: ["ignore", "pipe", "pipe"],
116789
116847
  env: { ...process.env },
@@ -116846,7 +116904,7 @@ class ClaudeCodeService {
116846
116904
  }
116847
116905
  });
116848
116906
  proc.on("error", (err) => {
116849
- const errorMsg = `Claude process error: ${err.message}, binary path was: ${this.claudeBinaryPath}`;
116907
+ const errorMsg = `Claude process error: ${err.message}, binary path was: ${binaryPath}`;
116850
116908
  consola.error(errorMsg);
116851
116909
  console.error(errorMsg, err);
116852
116910
  reject(err);
@@ -117153,32 +117211,56 @@ class MessageProcessor {
117153
117211
  }
117154
117212
  return;
117155
117213
  }
117214
+ getChannelIds() {
117215
+ return [...this.userStates.keys()];
117216
+ }
117156
117217
  async handleMessage(message, adapter) {
117157
117218
  const { text, context } = message;
117158
117219
  const command = text.trim().toLowerCase();
117159
- consola.debug(`Received message: ${text}`);
117220
+ consola.info(`[handleMessage] text="${text}" command="${command}" channel=${context.channelId}`);
117160
117221
  if (command === "help" || command === "/help") {
117222
+ consola.info("[handleMessage] -> help");
117161
117223
  await this.sendHelp(adapter, context);
117162
117224
  return;
117163
117225
  }
117164
117226
  if (command === "/projects" || command === "projects") {
117227
+ consola.info("[handleMessage] -> projects");
117165
117228
  await this.sendProjectList(adapter, context);
117166
117229
  return;
117167
117230
  }
117168
117231
  if (command === "/status" || command === "status") {
117232
+ consola.info("[handleMessage] -> status");
117169
117233
  await this.sendStatus(adapter, context);
117170
117234
  return;
117171
117235
  }
117172
- if (command.startsWith("/project ")) {
117173
- const projectName = text.slice(9).trim();
117236
+ if (command === "/project" || command === "project") {
117237
+ consola.info("[handleMessage] -> showProjectSelector");
117238
+ await this.showProjectSelector(adapter, context);
117239
+ return;
117240
+ }
117241
+ if (command.startsWith("/project ") || command.startsWith("project ")) {
117242
+ const projectName = command.startsWith("/") ? text.slice(9).trim() : text.slice(8).trim();
117243
+ consola.info(`[handleMessage] -> selectProject: ${projectName}`);
117174
117244
  await this.selectProject(adapter, context, projectName);
117175
117245
  return;
117176
117246
  }
117177
- if (command.startsWith("/session ")) {
117178
- const sessionId = text.slice(9).trim();
117247
+ if (command.startsWith("/session ") || command.startsWith("session ")) {
117248
+ const sessionId = command.startsWith("/") ? text.slice(9).trim() : text.slice(8).trim();
117249
+ consola.info(`[handleMessage] -> selectSession: ${sessionId}`);
117179
117250
  await this.selectSession(adapter, context, sessionId);
117180
117251
  return;
117181
117252
  }
117253
+ if (command === "/stop" || command === "stop") {
117254
+ consola.info("[handleMessage] -> stop");
117255
+ await this.handleStopExecution(adapter, context);
117256
+ return;
117257
+ }
117258
+ if (command === "/clear" || command === "clear") {
117259
+ consola.info("[handleMessage] -> clear");
117260
+ await this.handleClearSession(adapter, context);
117261
+ return;
117262
+ }
117263
+ consola.info("[handleMessage] -> handlePrompt");
117182
117264
  await this.handlePrompt(adapter, context, text);
117183
117265
  }
117184
117266
  async handleAudioMessage(message, adapter) {
@@ -117388,11 +117470,14 @@ _Cost: $${cost.toFixed(4)}_`;
117388
117470
  const help = `*Heimerdinger - Claude Code Bridge*
117389
117471
 
117390
117472
  Available commands:
117391
- \`/projects\` - List available projects
117392
- \`/project <name>\` - Select a project
117393
- \`/session <id>\` - Resume a session
117394
- \`/status\` - Show current status
117395
- \`/help\` - Show this help
117473
+ • \`project\` - Select a project (interactive)
117474
+ \`project <name>\` - Select a project by name
117475
+ \`projects\` - List available projects
117476
+ • \`session <id>\` - Resume a session
117477
+ • \`stop\` - Stop current execution
117478
+ • \`clear\` - Clear session
117479
+ • \`status\` - Show current status
117480
+ • \`help\` - Show this help
117396
117481
 
117397
117482
  Or just send a message to start coding with Claude!`;
117398
117483
  await adapter.sendMessage(context.channelId, help);
@@ -118157,6 +118242,12 @@ class HeimerdingerServer {
118157
118242
  });
118158
118243
  await adapter.start();
118159
118244
  consola.success("Slack adapter started");
118245
+ const channelStates = this.messageProcessor.getChannelIds();
118246
+ for (const channelId of channelStates) {
118247
+ try {
118248
+ await adapter.sendMessage(channelId, `\uD83D\uDFE2 Heimerdinger online (${new Date().toLocaleTimeString()})`);
118249
+ } catch {}
118250
+ }
118160
118251
  } catch (error) {
118161
118252
  consola.error("Failed to initialize Slack adapter:", error);
118162
118253
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chat-heimerdinger",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "Bridge IM tools (Slack/Lark) with Claude Code for vibe coding",
5
5
  "type": "module",
6
6
  "bin": {