happy-coder 0.2.3-beta.1 → 0.3.1-beta.1

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/index.cjs CHANGED
@@ -9,6 +9,8 @@ var node_readline = require('node:readline');
9
9
  var node_url = require('node:url');
10
10
  var node_fs = require('node:fs');
11
11
  var os = require('node:os');
12
+ var path = require('path');
13
+ var url = require('url');
12
14
  var promises$1 = require('node:fs/promises');
13
15
  var promises = require('fs/promises');
14
16
  var ink = require('ink');
@@ -25,8 +27,6 @@ var z = require('zod');
25
27
  var child_process = require('child_process');
26
28
  var util = require('util');
27
29
  var crypto = require('crypto');
28
- var path = require('path');
29
- var url = require('url');
30
30
  var qrcode = require('qrcode-terminal');
31
31
  var fs = require('fs');
32
32
  var os$1 = require('os');
@@ -118,7 +118,13 @@ function claudeCheckSession(sessionId, path) {
118
118
  return hasGoodMessage;
119
119
  }
120
120
 
121
- const __dirname$3 = node_path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
121
+ const __dirname$2 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
122
+ function projectPath() {
123
+ const path$1 = path.resolve(__dirname$2, "..");
124
+ return path$1;
125
+ }
126
+
127
+ node_path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
122
128
  async function claudeLocal(opts) {
123
129
  const projectDir = getProjectPath(opts.path);
124
130
  node_fs.mkdirSync(projectDir, { recursive: true });
@@ -168,7 +174,10 @@ async function claudeLocal(opts) {
168
174
  if (opts.claudeArgs) {
169
175
  args.push(...opts.claudeArgs);
170
176
  }
171
- const claudeCliPath = process.env.HAPPY_CLAUDE_CLI_PATH || node_path.resolve(node_path.join(__dirname$3, "..", "scripts", "claudeInteractiveLaunch.cjs"));
177
+ const claudeCliPath = node_path.resolve(node_path.join(projectPath(), "scripts", "claude_local_launcher.cjs"));
178
+ if (!claudeCliPath || !node_fs.existsSync(claudeCliPath)) {
179
+ throw new Error("Claude local launcher not found. Please ensure HAPPY_PROJECT_ROOT is set correctly for development.");
180
+ }
172
181
  const env = {
173
182
  ...process.env,
174
183
  ...opts.claudeEnvVars
@@ -845,9 +854,9 @@ class AbortError extends Error {
845
854
  }
846
855
 
847
856
  const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
848
- const __dirname$2 = node_path.join(__filename$1, "..");
857
+ const __dirname$1 = node_path.join(__filename$1, "..");
849
858
  function getDefaultClaudeCodePath() {
850
- return node_path.join(__dirname$2, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
859
+ return node_path.join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
851
860
  }
852
861
  function logDebug(message) {
853
862
  if (process.env.DEBUG) {
@@ -909,16 +918,20 @@ class Query {
909
918
  try {
910
919
  for await (const line of rl) {
911
920
  if (line.trim()) {
912
- const message = JSON.parse(line);
913
- if (message.type === "control_response") {
914
- const controlResponse = message;
915
- const handler = this.pendingControlResponses.get(controlResponse.response.request_id);
916
- if (handler) {
917
- handler(controlResponse.response);
921
+ try {
922
+ const message = JSON.parse(line);
923
+ if (message.type === "control_response") {
924
+ const controlResponse = message;
925
+ const handler = this.pendingControlResponses.get(controlResponse.response.request_id);
926
+ if (handler) {
927
+ handler(controlResponse.response);
928
+ }
929
+ continue;
918
930
  }
919
- continue;
931
+ this.inputStream.enqueue(message);
932
+ } catch (e) {
933
+ types$1.logger.debug(line);
920
934
  }
921
- this.inputStream.enqueue(message);
922
935
  }
923
936
  }
924
937
  await this.processExitPromise;
@@ -1240,7 +1253,10 @@ async function claudeRemote(opts) {
1240
1253
  permissionPromptToolName: opts.permissionPromptToolName,
1241
1254
  permissionMode: opts.permissionMode,
1242
1255
  executable: "node",
1243
- abort: opts.signal
1256
+ abort: opts.signal,
1257
+ pathToClaudeCodeExecutable: (() => {
1258
+ return node_path.resolve(node_path.join(projectPath(), "scripts", "claude_remote_launcher.cjs"));
1259
+ })()
1244
1260
  };
1245
1261
  if (opts.claudeArgs && opts.claudeArgs.length > 0) {
1246
1262
  sdkOptions.executableArgs = [...sdkOptions.executableArgs || [], ...opts.claudeArgs];
@@ -2232,7 +2248,7 @@ async function loop(opts) {
2232
2248
  }
2233
2249
 
2234
2250
  var name = "happy-coder";
2235
- var version = "0.2.3-beta.1";
2251
+ var version = "0.3.1-beta.1";
2236
2252
  var description = "Claude Code session sharing CLI";
2237
2253
  var author = "Kirill Dubovitskiy";
2238
2254
  var license = "MIT";
@@ -2241,7 +2257,7 @@ var homepage = "https://github.com/slopus/happy-cli";
2241
2257
  var bugs = "https://github.com/slopus/happy-cli/issues";
2242
2258
  var repository = "slopus/happy-cli";
2243
2259
  var bin = {
2244
- happy: "./bin/happy"
2260
+ happy: "./bin/happy.mjs"
2245
2261
  };
2246
2262
  var main = "./dist/index.cjs";
2247
2263
  var module$1 = "./dist/index.mjs";
@@ -2343,10 +2359,9 @@ var packageJson = {
2343
2359
  overrides: overrides
2344
2360
  };
2345
2361
 
2346
- const __dirname$1 = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
2347
- const RUNNER_PATH = path.join(__dirname$1, "..", "..", "scripts", "ripgrep_launcher.cjs");
2348
2362
  function run(args, options) {
2349
- return new Promise((resolve, reject) => {
2363
+ const RUNNER_PATH = path.resolve(path.join(projectPath(), "scripts", "ripgrep_launcher.cjs"));
2364
+ return new Promise((resolve2, reject) => {
2350
2365
  const child = child_process.spawn("node", [RUNNER_PATH, JSON.stringify(args)], {
2351
2366
  stdio: ["pipe", "pipe", "pipe"],
2352
2367
  cwd: options?.cwd
@@ -2360,7 +2375,7 @@ function run(args, options) {
2360
2375
  stderr += data.toString();
2361
2376
  });
2362
2377
  child.on("close", (code) => {
2363
- resolve({
2378
+ resolve2({
2364
2379
  exitCode: code || 0,
2365
2380
  stdout,
2366
2381
  stderr
@@ -2885,6 +2900,51 @@ function setupCleanupHandlers() {
2885
2900
  });
2886
2901
  }
2887
2902
 
2903
+ async function extractSDKMetadata() {
2904
+ const abortController = new AbortController();
2905
+ try {
2906
+ types$1.logger.debug("[metadataExtractor] Starting SDK metadata extraction");
2907
+ const sdkQuery = query({
2908
+ prompt: "hello",
2909
+ options: {
2910
+ allowedTools: ["Bash(echo)"],
2911
+ maxTurns: 1,
2912
+ abort: abortController.signal
2913
+ }
2914
+ });
2915
+ for await (const message of sdkQuery) {
2916
+ if (message.type === "system" && message.subtype === "init") {
2917
+ const systemMessage = message;
2918
+ const metadata = {
2919
+ tools: systemMessage.tools,
2920
+ slashCommands: systemMessage.slash_commands
2921
+ };
2922
+ types$1.logger.debug("[metadataExtractor] Captured SDK metadata:", metadata);
2923
+ abortController.abort();
2924
+ return metadata;
2925
+ }
2926
+ }
2927
+ types$1.logger.debug("[metadataExtractor] No init message received from SDK");
2928
+ return {};
2929
+ } catch (error) {
2930
+ if (error instanceof Error && error.name === "AbortError") {
2931
+ types$1.logger.debug("[metadataExtractor] SDK query aborted after capturing metadata");
2932
+ return {};
2933
+ }
2934
+ types$1.logger.debug("[metadataExtractor] Error extracting SDK metadata:", error);
2935
+ return {};
2936
+ }
2937
+ }
2938
+ function extractSDKMetadataAsync(onComplete) {
2939
+ extractSDKMetadata().then((metadata) => {
2940
+ if (metadata.tools || metadata.slashCommands) {
2941
+ onComplete(metadata);
2942
+ }
2943
+ }).catch((error) => {
2944
+ types$1.logger.debug("[metadataExtractor] Async extraction failed:", error);
2945
+ });
2946
+ }
2947
+
2888
2948
  async function start(credentials, options = {}) {
2889
2949
  const workingDirectory = process.cwd();
2890
2950
  const sessionTag = node_crypto.randomUUID();
@@ -2904,6 +2964,19 @@ async function start(credentials, options = {}) {
2904
2964
  };
2905
2965
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
2906
2966
  types$1.logger.debug(`Session created: ${response.id}`);
2967
+ extractSDKMetadataAsync(async (sdkMetadata) => {
2968
+ types$1.logger.debug("[start] SDK metadata extracted, updating session:", sdkMetadata);
2969
+ try {
2970
+ api.session(response).updateMetadata((currentMetadata) => ({
2971
+ ...currentMetadata,
2972
+ tools: sdkMetadata.tools,
2973
+ slashCommands: sdkMetadata.slashCommands
2974
+ }));
2975
+ types$1.logger.debug("[start] Session metadata updated with SDK capabilities");
2976
+ } catch (error) {
2977
+ types$1.logger.debug("[start] Failed to update session metadata:", error);
2978
+ }
2979
+ });
2907
2980
  if (options.daemonSpawn) {
2908
2981
  console.log(`daemon:sessionIdCreated:${response.id}`);
2909
2982
  }
package/dist/index.mjs CHANGED
@@ -2,11 +2,13 @@ import chalk from 'chalk';
2
2
  import { l as logger, d as backoff, e as delay, R as RawJSONLinesSchema, c as configuration, f as encodeBase64, A as ApiClient, g as encodeBase64Url, h as decodeBase64, b as initializeConfiguration, i as initLoggerWithGlobalConfiguration } from './types-BX4xv8Ty.mjs';
3
3
  import { randomUUID, randomBytes } from 'node:crypto';
4
4
  import { spawn, execSync } from 'node:child_process';
5
- import { resolve, join, dirname } from 'node:path';
5
+ import { resolve, join, dirname as dirname$1 } from 'node:path';
6
6
  import { createInterface } from 'node:readline';
7
- import { fileURLToPath } from 'node:url';
7
+ import { fileURLToPath as fileURLToPath$1 } from 'node:url';
8
8
  import { existsSync, readFileSync, mkdirSync, watch, rmSync } from 'node:fs';
9
9
  import os, { homedir } from 'node:os';
10
+ import { dirname, resolve as resolve$1, join as join$1 } from 'path';
11
+ import { fileURLToPath } from 'url';
10
12
  import { readFile, mkdir, writeFile as writeFile$1 } from 'node:fs/promises';
11
13
  import { watch as watch$1, access, readFile as readFile$1, stat, writeFile, readdir } from 'fs/promises';
12
14
  import { useStdout, useInput, Box, Text, render } from 'ink';
@@ -24,8 +26,6 @@ import { z as z$1 } from 'zod';
24
26
  import { spawn as spawn$1, exec, execSync as execSync$1 } from 'child_process';
25
27
  import { promisify } from 'util';
26
28
  import crypto, { createHash } from 'crypto';
27
- import { dirname as dirname$1, join as join$1 } from 'path';
28
- import { fileURLToPath as fileURLToPath$1 } from 'url';
29
29
  import qrcode from 'qrcode-terminal';
30
30
  import { existsSync as existsSync$1, readFileSync as readFileSync$1, writeFileSync, unlinkSync, mkdirSync as mkdirSync$1, chmodSync } from 'fs';
31
31
  import { hostname, homedir as homedir$1 } from 'os';
@@ -97,7 +97,13 @@ function claudeCheckSession(sessionId, path) {
97
97
  return hasGoodMessage;
98
98
  }
99
99
 
100
- const __dirname$2 = dirname(fileURLToPath(import.meta.url));
100
+ const __dirname$1 = dirname(fileURLToPath(import.meta.url));
101
+ function projectPath() {
102
+ const path = resolve$1(__dirname$1, "..");
103
+ return path;
104
+ }
105
+
106
+ dirname$1(fileURLToPath$1(import.meta.url));
101
107
  async function claudeLocal(opts) {
102
108
  const projectDir = getProjectPath(opts.path);
103
109
  mkdirSync(projectDir, { recursive: true });
@@ -147,7 +153,10 @@ async function claudeLocal(opts) {
147
153
  if (opts.claudeArgs) {
148
154
  args.push(...opts.claudeArgs);
149
155
  }
150
- const claudeCliPath = process.env.HAPPY_CLAUDE_CLI_PATH || resolve(join(__dirname$2, "..", "scripts", "claudeInteractiveLaunch.cjs"));
156
+ const claudeCliPath = resolve(join(projectPath(), "scripts", "claude_local_launcher.cjs"));
157
+ if (!claudeCliPath || !existsSync(claudeCliPath)) {
158
+ throw new Error("Claude local launcher not found. Please ensure HAPPY_PROJECT_ROOT is set correctly for development.");
159
+ }
151
160
  const env = {
152
161
  ...process.env,
153
162
  ...opts.claudeEnvVars
@@ -823,10 +832,10 @@ class AbortError extends Error {
823
832
  }
824
833
  }
825
834
 
826
- const __filename = fileURLToPath(import.meta.url);
827
- const __dirname$1 = join(__filename, "..");
835
+ const __filename = fileURLToPath$1(import.meta.url);
836
+ const __dirname = join(__filename, "..");
828
837
  function getDefaultClaudeCodePath() {
829
- return join(__dirname$1, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
838
+ return join(__dirname, "..", "..", "..", "node_modules", "@anthropic-ai", "claude-code", "cli.js");
830
839
  }
831
840
  function logDebug(message) {
832
841
  if (process.env.DEBUG) {
@@ -888,16 +897,20 @@ class Query {
888
897
  try {
889
898
  for await (const line of rl) {
890
899
  if (line.trim()) {
891
- const message = JSON.parse(line);
892
- if (message.type === "control_response") {
893
- const controlResponse = message;
894
- const handler = this.pendingControlResponses.get(controlResponse.response.request_id);
895
- if (handler) {
896
- handler(controlResponse.response);
900
+ try {
901
+ const message = JSON.parse(line);
902
+ if (message.type === "control_response") {
903
+ const controlResponse = message;
904
+ const handler = this.pendingControlResponses.get(controlResponse.response.request_id);
905
+ if (handler) {
906
+ handler(controlResponse.response);
907
+ }
908
+ continue;
897
909
  }
898
- continue;
910
+ this.inputStream.enqueue(message);
911
+ } catch (e) {
912
+ logger.debug(line);
899
913
  }
900
- this.inputStream.enqueue(message);
901
914
  }
902
915
  }
903
916
  await this.processExitPromise;
@@ -1219,7 +1232,10 @@ async function claudeRemote(opts) {
1219
1232
  permissionPromptToolName: opts.permissionPromptToolName,
1220
1233
  permissionMode: opts.permissionMode,
1221
1234
  executable: "node",
1222
- abort: opts.signal
1235
+ abort: opts.signal,
1236
+ pathToClaudeCodeExecutable: (() => {
1237
+ return resolve(join(projectPath(), "scripts", "claude_remote_launcher.cjs"));
1238
+ })()
1223
1239
  };
1224
1240
  if (opts.claudeArgs && opts.claudeArgs.length > 0) {
1225
1241
  sdkOptions.executableArgs = [...sdkOptions.executableArgs || [], ...opts.claudeArgs];
@@ -2211,7 +2227,7 @@ async function loop(opts) {
2211
2227
  }
2212
2228
 
2213
2229
  var name = "happy-coder";
2214
- var version = "0.2.3-beta.1";
2230
+ var version = "0.3.1-beta.1";
2215
2231
  var description = "Claude Code session sharing CLI";
2216
2232
  var author = "Kirill Dubovitskiy";
2217
2233
  var license = "MIT";
@@ -2220,7 +2236,7 @@ var homepage = "https://github.com/slopus/happy-cli";
2220
2236
  var bugs = "https://github.com/slopus/happy-cli/issues";
2221
2237
  var repository = "slopus/happy-cli";
2222
2238
  var bin = {
2223
- happy: "./bin/happy"
2239
+ happy: "./bin/happy.mjs"
2224
2240
  };
2225
2241
  var main = "./dist/index.cjs";
2226
2242
  var module = "./dist/index.mjs";
@@ -2322,10 +2338,9 @@ var packageJson = {
2322
2338
  overrides: overrides
2323
2339
  };
2324
2340
 
2325
- const __dirname = dirname$1(fileURLToPath$1(import.meta.url));
2326
- const RUNNER_PATH = join$1(__dirname, "..", "..", "scripts", "ripgrep_launcher.cjs");
2327
2341
  function run(args, options) {
2328
- return new Promise((resolve, reject) => {
2342
+ const RUNNER_PATH = resolve$1(join$1(projectPath(), "scripts", "ripgrep_launcher.cjs"));
2343
+ return new Promise((resolve2, reject) => {
2329
2344
  const child = spawn$1("node", [RUNNER_PATH, JSON.stringify(args)], {
2330
2345
  stdio: ["pipe", "pipe", "pipe"],
2331
2346
  cwd: options?.cwd
@@ -2339,7 +2354,7 @@ function run(args, options) {
2339
2354
  stderr += data.toString();
2340
2355
  });
2341
2356
  child.on("close", (code) => {
2342
- resolve({
2357
+ resolve2({
2343
2358
  exitCode: code || 0,
2344
2359
  stdout,
2345
2360
  stderr
@@ -2864,6 +2879,51 @@ function setupCleanupHandlers() {
2864
2879
  });
2865
2880
  }
2866
2881
 
2882
+ async function extractSDKMetadata() {
2883
+ const abortController = new AbortController();
2884
+ try {
2885
+ logger.debug("[metadataExtractor] Starting SDK metadata extraction");
2886
+ const sdkQuery = query({
2887
+ prompt: "hello",
2888
+ options: {
2889
+ allowedTools: ["Bash(echo)"],
2890
+ maxTurns: 1,
2891
+ abort: abortController.signal
2892
+ }
2893
+ });
2894
+ for await (const message of sdkQuery) {
2895
+ if (message.type === "system" && message.subtype === "init") {
2896
+ const systemMessage = message;
2897
+ const metadata = {
2898
+ tools: systemMessage.tools,
2899
+ slashCommands: systemMessage.slash_commands
2900
+ };
2901
+ logger.debug("[metadataExtractor] Captured SDK metadata:", metadata);
2902
+ abortController.abort();
2903
+ return metadata;
2904
+ }
2905
+ }
2906
+ logger.debug("[metadataExtractor] No init message received from SDK");
2907
+ return {};
2908
+ } catch (error) {
2909
+ if (error instanceof Error && error.name === "AbortError") {
2910
+ logger.debug("[metadataExtractor] SDK query aborted after capturing metadata");
2911
+ return {};
2912
+ }
2913
+ logger.debug("[metadataExtractor] Error extracting SDK metadata:", error);
2914
+ return {};
2915
+ }
2916
+ }
2917
+ function extractSDKMetadataAsync(onComplete) {
2918
+ extractSDKMetadata().then((metadata) => {
2919
+ if (metadata.tools || metadata.slashCommands) {
2920
+ onComplete(metadata);
2921
+ }
2922
+ }).catch((error) => {
2923
+ logger.debug("[metadataExtractor] Async extraction failed:", error);
2924
+ });
2925
+ }
2926
+
2867
2927
  async function start(credentials, options = {}) {
2868
2928
  const workingDirectory = process.cwd();
2869
2929
  const sessionTag = randomUUID();
@@ -2883,6 +2943,19 @@ async function start(credentials, options = {}) {
2883
2943
  };
2884
2944
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
2885
2945
  logger.debug(`Session created: ${response.id}`);
2946
+ extractSDKMetadataAsync(async (sdkMetadata) => {
2947
+ logger.debug("[start] SDK metadata extracted, updating session:", sdkMetadata);
2948
+ try {
2949
+ api.session(response).updateMetadata((currentMetadata) => ({
2950
+ ...currentMetadata,
2951
+ tools: sdkMetadata.tools,
2952
+ slashCommands: sdkMetadata.slashCommands
2953
+ }));
2954
+ logger.debug("[start] Session metadata updated with SDK capabilities");
2955
+ } catch (error) {
2956
+ logger.debug("[start] Failed to update session metadata:", error);
2957
+ }
2958
+ });
2886
2959
  if (options.daemonSpawn) {
2887
2960
  console.log(`daemon:sessionIdCreated:${response.id}`);
2888
2961
  }
package/dist/lib.d.cts CHANGED
@@ -381,6 +381,8 @@ type Metadata = {
381
381
  updatedAt: number;
382
382
  };
383
383
  machineId?: string;
384
+ tools?: string[];
385
+ slashCommands?: string[];
384
386
  };
385
387
  type AgentState = {
386
388
  controlledByUser?: boolean | null | undefined;
package/dist/lib.d.mts CHANGED
@@ -381,6 +381,8 @@ type Metadata = {
381
381
  updatedAt: number;
382
382
  };
383
383
  machineId?: string;
384
+ tools?: string[];
385
+ slashCommands?: string[];
384
386
  };
385
387
  type AgentState = {
386
388
  controlledByUser?: boolean | null | undefined;