happy-coder 0.7.1 → 0.8.0
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 +150 -41
- package/dist/index.mjs +150 -41
- package/dist/lib.cjs +1 -1
- package/dist/lib.d.cts +2 -2
- package/dist/lib.d.mts +2 -2
- package/dist/lib.mjs +1 -1
- package/dist/{types-CnqIfv9n.mjs → types-CJU8o8xd.mjs} +2 -0
- package/dist/{types-B4GgojGc.cjs → types-D8nxJTr8.cjs} +2 -0
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
|
-
var types$1 = require('./types-
|
|
4
|
+
var types$1 = require('./types-D8nxJTr8.cjs');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
6
|
var node_child_process = require('node:child_process');
|
|
7
7
|
var node_path = require('node:path');
|
|
@@ -63,6 +63,7 @@ class Session {
|
|
|
63
63
|
claudeEnvVars;
|
|
64
64
|
claudeArgs;
|
|
65
65
|
mcpServers;
|
|
66
|
+
allowedTools;
|
|
66
67
|
_onModeChange;
|
|
67
68
|
sessionId;
|
|
68
69
|
mode = "local";
|
|
@@ -77,6 +78,7 @@ class Session {
|
|
|
77
78
|
this.claudeEnvVars = opts.claudeEnvVars;
|
|
78
79
|
this.claudeArgs = opts.claudeArgs;
|
|
79
80
|
this.mcpServers = opts.mcpServers;
|
|
81
|
+
this.allowedTools = opts.allowedTools;
|
|
80
82
|
this._onModeChange = opts.onModeChange;
|
|
81
83
|
this.client.keepAlive(this.thinking, this.mode);
|
|
82
84
|
setInterval(() => {
|
|
@@ -135,6 +137,29 @@ function projectPath() {
|
|
|
135
137
|
return path$1;
|
|
136
138
|
}
|
|
137
139
|
|
|
140
|
+
function trimIdent(text) {
|
|
141
|
+
const lines = text.split("\n");
|
|
142
|
+
while (lines.length > 0 && lines[0].trim() === "") {
|
|
143
|
+
lines.shift();
|
|
144
|
+
}
|
|
145
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
146
|
+
lines.pop();
|
|
147
|
+
}
|
|
148
|
+
const minSpaces = lines.reduce((min, line) => {
|
|
149
|
+
if (line.trim() === "") {
|
|
150
|
+
return min;
|
|
151
|
+
}
|
|
152
|
+
const leadingSpaces = line.match(/^\s*/)[0].length;
|
|
153
|
+
return Math.min(min, leadingSpaces);
|
|
154
|
+
}, Infinity);
|
|
155
|
+
const trimmedLines = lines.map((line) => line.slice(minSpaces));
|
|
156
|
+
return trimmedLines.join("\n");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const systemPrompt = trimIdent(`
|
|
160
|
+
ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
|
|
161
|
+
`);
|
|
162
|
+
|
|
138
163
|
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))));
|
|
139
164
|
async function claudeLocal(opts) {
|
|
140
165
|
const projectDir = getProjectPath(opts.path);
|
|
@@ -182,6 +207,13 @@ async function claudeLocal(opts) {
|
|
|
182
207
|
if (startFrom) {
|
|
183
208
|
args.push("--resume", startFrom);
|
|
184
209
|
}
|
|
210
|
+
args.push("--append-system-prompt", systemPrompt);
|
|
211
|
+
if (opts.mcpServers && Object.keys(opts.mcpServers).length > 0) {
|
|
212
|
+
args.push("--mcp-config", JSON.stringify({ mcpServers: opts.mcpServers }));
|
|
213
|
+
}
|
|
214
|
+
if (opts.allowedTools && opts.allowedTools.length > 0) {
|
|
215
|
+
args.push("--allowedTools", opts.allowedTools.join(","));
|
|
216
|
+
}
|
|
185
217
|
if (opts.claudeArgs) {
|
|
186
218
|
args.push(...opts.claudeArgs);
|
|
187
219
|
}
|
|
@@ -526,7 +558,9 @@ async function claudeLocalLauncher(session) {
|
|
|
526
558
|
sessionId: session.sessionId,
|
|
527
559
|
workingDirectory: session.path,
|
|
528
560
|
onMessage: (message) => {
|
|
529
|
-
|
|
561
|
+
if (message.type !== "summary") {
|
|
562
|
+
session.client.sendClaudeSessionMessage(message);
|
|
563
|
+
}
|
|
530
564
|
}
|
|
531
565
|
});
|
|
532
566
|
let exitReason = null;
|
|
@@ -556,7 +590,9 @@ async function claudeLocalLauncher(session) {
|
|
|
556
590
|
}
|
|
557
591
|
session.client.setHandler("abort", doAbort);
|
|
558
592
|
session.client.setHandler("switch", doSwitch);
|
|
559
|
-
session.queue.setOnMessage(
|
|
593
|
+
session.queue.setOnMessage((message, mode) => {
|
|
594
|
+
doSwitch();
|
|
595
|
+
});
|
|
560
596
|
if (session.queue.size() > 0) {
|
|
561
597
|
return "switch";
|
|
562
598
|
}
|
|
@@ -577,7 +613,9 @@ async function claudeLocalLauncher(session) {
|
|
|
577
613
|
onThinkingChange: session.onThinkingChange,
|
|
578
614
|
abort: processAbortController.signal,
|
|
579
615
|
claudeEnvVars: session.claudeEnvVars,
|
|
580
|
-
claudeArgs: session.claudeArgs
|
|
616
|
+
claudeArgs: session.claudeArgs,
|
|
617
|
+
mcpServers: session.mcpServers,
|
|
618
|
+
allowedTools: session.allowedTools
|
|
581
619
|
});
|
|
582
620
|
if (!exitReason) {
|
|
583
621
|
exitReason = "exit";
|
|
@@ -1339,6 +1377,12 @@ async function claudeRemote(opts) {
|
|
|
1339
1377
|
types$1.logger.debug("[claudeRemote] /compact command detected - will process as normal but with compaction behavior");
|
|
1340
1378
|
}
|
|
1341
1379
|
const isCompactCommand = specialCommand.type === "compact";
|
|
1380
|
+
if (isCompactCommand) {
|
|
1381
|
+
types$1.logger.debug("[claudeRemote] Compaction started");
|
|
1382
|
+
if (opts.onCompletionEvent) {
|
|
1383
|
+
opts.onCompletionEvent("Compaction started");
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1342
1386
|
let message = new PushableAsyncIterable();
|
|
1343
1387
|
message.push({
|
|
1344
1388
|
type: "user",
|
|
@@ -1378,12 +1422,6 @@ async function claudeRemote(opts) {
|
|
|
1378
1422
|
types$1.logger.debug(`[claudeRemote] Session file found: ${systemInit.session_id} ${found}`);
|
|
1379
1423
|
opts.onSessionFound(systemInit.session_id);
|
|
1380
1424
|
}
|
|
1381
|
-
if (isCompactCommand) {
|
|
1382
|
-
types$1.logger.debug("[claudeRemote] Compaction started");
|
|
1383
|
-
if (opts.onCompletionEvent) {
|
|
1384
|
-
opts.onCompletionEvent("Compaction started");
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
1425
|
}
|
|
1388
1426
|
if (message2.type === "result") {
|
|
1389
1427
|
updateThinking(false);
|
|
@@ -2091,9 +2129,6 @@ async function claudeRemoteLauncher(session) {
|
|
|
2091
2129
|
sessionId: session.sessionId,
|
|
2092
2130
|
workingDirectory: session.path,
|
|
2093
2131
|
onMessage: (message) => {
|
|
2094
|
-
if (message.type === "summary") {
|
|
2095
|
-
session.client.sendClaudeSessionMessage(message);
|
|
2096
|
-
}
|
|
2097
2132
|
}
|
|
2098
2133
|
});
|
|
2099
2134
|
let exitReason = null;
|
|
@@ -2253,8 +2288,8 @@ async function claudeRemoteLauncher(session) {
|
|
|
2253
2288
|
model: messageData.mode.model,
|
|
2254
2289
|
fallbackModel: messageData.mode.fallbackModel,
|
|
2255
2290
|
customSystemPrompt: messageData.mode.customSystemPrompt,
|
|
2256
|
-
appendSystemPrompt: messageData.mode.appendSystemPrompt,
|
|
2257
|
-
allowedTools: messageData.mode.allowedTools,
|
|
2291
|
+
appendSystemPrompt: messageData.mode.appendSystemPrompt ? messageData.mode.appendSystemPrompt + "\n" + systemPrompt : systemPrompt,
|
|
2292
|
+
allowedTools: messageData.mode.allowedTools ? [...messageData.mode.allowedTools, ...session.allowedTools ? session.allowedTools : []] : session.allowedTools ? [...session.allowedTools] : void 0,
|
|
2258
2293
|
disallowedTools: messageData.mode.disallowedTools,
|
|
2259
2294
|
onSessionFound: (sessionId) => {
|
|
2260
2295
|
sdkToLogConverter.updateSessionId(sessionId);
|
|
@@ -2330,6 +2365,7 @@ async function loop(opts) {
|
|
|
2330
2365
|
mcpServers: opts.mcpServers,
|
|
2331
2366
|
logPath,
|
|
2332
2367
|
messageQueue: opts.messageQueue,
|
|
2368
|
+
allowedTools: opts.allowedTools,
|
|
2333
2369
|
onModeChange: opts.onModeChange
|
|
2334
2370
|
});
|
|
2335
2371
|
if (opts.onSessionReady) {
|
|
@@ -2364,7 +2400,7 @@ async function loop(opts) {
|
|
|
2364
2400
|
}
|
|
2365
2401
|
|
|
2366
2402
|
var name = "happy-coder";
|
|
2367
|
-
var version = "0.
|
|
2403
|
+
var version = "0.8.0";
|
|
2368
2404
|
var description = "Claude Code session sharing CLI";
|
|
2369
2405
|
var author = "Kirill Dubovitskiy";
|
|
2370
2406
|
var license = "MIT";
|
|
@@ -2414,7 +2450,7 @@ var scripts = {
|
|
|
2414
2450
|
test: "yarn build && vitest run",
|
|
2415
2451
|
"test:watch": "vitest",
|
|
2416
2452
|
"test:integration-test-env": "yarn build && tsx --env-file .env.integration-test node_modules/.bin/vitest run",
|
|
2417
|
-
dev: "yarn build && npx tsx src/index.ts",
|
|
2453
|
+
dev: "DEBUG=1 yarn build && npx tsx src/index.ts",
|
|
2418
2454
|
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
2419
2455
|
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
2420
2456
|
prepublishOnly: "yarn build && yarn test",
|
|
@@ -4042,7 +4078,7 @@ async function authAndSetupMachineIfNeeded() {
|
|
|
4042
4078
|
const settings = await updateSettings(async (s) => {
|
|
4043
4079
|
if (!s.machineId) {
|
|
4044
4080
|
const newMachineId = node_crypto.randomUUID();
|
|
4045
|
-
|
|
4081
|
+
types$1.logger.debug(`[AUTH] No machine ID found, generating new one: ${newMachineId}; We will not create machine on startup since we don't have api client intialized`);
|
|
4046
4082
|
return {
|
|
4047
4083
|
...s,
|
|
4048
4084
|
machineId: node_crypto.randomUUID()
|
|
@@ -4323,6 +4359,88 @@ async function startDaemon() {
|
|
|
4323
4359
|
}
|
|
4324
4360
|
}
|
|
4325
4361
|
|
|
4362
|
+
async function startHappyServer(client) {
|
|
4363
|
+
const handler = async (title) => {
|
|
4364
|
+
types$1.logger.debug("[happyMCP] Changing title to:", title);
|
|
4365
|
+
try {
|
|
4366
|
+
client.sendClaudeSessionMessage({
|
|
4367
|
+
type: "summary",
|
|
4368
|
+
summary: title,
|
|
4369
|
+
leafUuid: node_crypto.randomUUID()
|
|
4370
|
+
});
|
|
4371
|
+
return { success: true };
|
|
4372
|
+
} catch (error) {
|
|
4373
|
+
return { success: false, error: String(error) };
|
|
4374
|
+
}
|
|
4375
|
+
};
|
|
4376
|
+
const mcp = new mcp_js.McpServer({
|
|
4377
|
+
name: "Happy MCP",
|
|
4378
|
+
version: "1.0.0",
|
|
4379
|
+
description: "Happy CLI MCP server with chat session management tools"
|
|
4380
|
+
});
|
|
4381
|
+
mcp.registerTool("change_title", {
|
|
4382
|
+
description: "Change the title of the current chat session",
|
|
4383
|
+
title: "Change Chat Title",
|
|
4384
|
+
inputSchema: {
|
|
4385
|
+
title: z.z.string().describe("The new title for the chat session")
|
|
4386
|
+
}
|
|
4387
|
+
}, async (args) => {
|
|
4388
|
+
const response = await handler(args.title);
|
|
4389
|
+
types$1.logger.debug("[happyMCP] Response:", response);
|
|
4390
|
+
if (response.success) {
|
|
4391
|
+
return {
|
|
4392
|
+
content: [
|
|
4393
|
+
{
|
|
4394
|
+
type: "text",
|
|
4395
|
+
text: `Successfully changed chat title to: "${args.title}"`
|
|
4396
|
+
}
|
|
4397
|
+
],
|
|
4398
|
+
isError: false
|
|
4399
|
+
};
|
|
4400
|
+
} else {
|
|
4401
|
+
return {
|
|
4402
|
+
content: [
|
|
4403
|
+
{
|
|
4404
|
+
type: "text",
|
|
4405
|
+
text: `Failed to change chat title: ${response.error || "Unknown error"}`
|
|
4406
|
+
}
|
|
4407
|
+
],
|
|
4408
|
+
isError: true
|
|
4409
|
+
};
|
|
4410
|
+
}
|
|
4411
|
+
});
|
|
4412
|
+
const transport = new streamableHttp_js.StreamableHTTPServerTransport({
|
|
4413
|
+
// NOTE: Returning session id here will result in claude
|
|
4414
|
+
// sdk spawn to fail with `Invalid Request: Server already initialized`
|
|
4415
|
+
sessionIdGenerator: void 0
|
|
4416
|
+
});
|
|
4417
|
+
await mcp.connect(transport);
|
|
4418
|
+
const server = node_http.createServer(async (req, res) => {
|
|
4419
|
+
try {
|
|
4420
|
+
await transport.handleRequest(req, res);
|
|
4421
|
+
} catch (error) {
|
|
4422
|
+
types$1.logger.debug("Error handling request:", error);
|
|
4423
|
+
if (!res.headersSent) {
|
|
4424
|
+
res.writeHead(500).end();
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
});
|
|
4428
|
+
const baseUrl = await new Promise((resolve) => {
|
|
4429
|
+
server.listen(0, "127.0.0.1", () => {
|
|
4430
|
+
const addr = server.address();
|
|
4431
|
+
resolve(new URL(`http://127.0.0.1:${addr.port}`));
|
|
4432
|
+
});
|
|
4433
|
+
});
|
|
4434
|
+
return {
|
|
4435
|
+
url: baseUrl.toString(),
|
|
4436
|
+
toolNames: ["change_title"],
|
|
4437
|
+
stop: () => {
|
|
4438
|
+
mcp.close();
|
|
4439
|
+
server.close();
|
|
4440
|
+
}
|
|
4441
|
+
};
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4326
4444
|
async function start(credentials, options = {}) {
|
|
4327
4445
|
const workingDirectory = process.cwd();
|
|
4328
4446
|
const sessionTag = node_crypto.randomUUID();
|
|
@@ -4382,6 +4500,8 @@ async function start(credentials, options = {}) {
|
|
|
4382
4500
|
}
|
|
4383
4501
|
});
|
|
4384
4502
|
const session = api.sessionSyncClient(response);
|
|
4503
|
+
const happyServer = await startHappyServer(session);
|
|
4504
|
+
types$1.logger.debug(`[START] Happy MCP server started at ${happyServer.url}`);
|
|
4385
4505
|
const logPath = await types$1.logger.logFilePathPromise;
|
|
4386
4506
|
types$1.logger.infoDeveloper(`Session: ${response.id}`);
|
|
4387
4507
|
types$1.logger.infoDeveloper(`Logs: ${logPath}`);
|
|
@@ -4516,6 +4636,7 @@ async function start(credentials, options = {}) {
|
|
|
4516
4636
|
await session.close();
|
|
4517
4637
|
}
|
|
4518
4638
|
stopCaffeinate();
|
|
4639
|
+
happyServer.stop();
|
|
4519
4640
|
types$1.logger.debug("[START] Cleanup complete, exiting");
|
|
4520
4641
|
process.exit(0);
|
|
4521
4642
|
} catch (error) {
|
|
@@ -4540,6 +4661,7 @@ async function start(credentials, options = {}) {
|
|
|
4540
4661
|
startingMode: options.startingMode,
|
|
4541
4662
|
messageQueue,
|
|
4542
4663
|
api,
|
|
4664
|
+
allowedTools: happyServer.toolNames.map((toolName) => `mcp__happy__${toolName}`),
|
|
4543
4665
|
onModeChange: (newMode) => {
|
|
4544
4666
|
session.sendSessionEvent({ type: "switch", mode: newMode });
|
|
4545
4667
|
session.updateAgentState((currentState) => ({
|
|
@@ -4549,7 +4671,12 @@ async function start(credentials, options = {}) {
|
|
|
4549
4671
|
},
|
|
4550
4672
|
onSessionReady: (sessionInstance) => {
|
|
4551
4673
|
},
|
|
4552
|
-
mcpServers: {
|
|
4674
|
+
mcpServers: {
|
|
4675
|
+
"happy": {
|
|
4676
|
+
type: "http",
|
|
4677
|
+
url: happyServer.url
|
|
4678
|
+
}
|
|
4679
|
+
},
|
|
4553
4680
|
session,
|
|
4554
4681
|
claudeEnvVars: options.claudeEnvVars,
|
|
4555
4682
|
claudeArgs: options.claudeArgs
|
|
@@ -4561,28 +4688,11 @@ async function start(credentials, options = {}) {
|
|
|
4561
4688
|
await session.close();
|
|
4562
4689
|
stopCaffeinate();
|
|
4563
4690
|
types$1.logger.debug("Stopped sleep prevention");
|
|
4691
|
+
happyServer.stop();
|
|
4692
|
+
types$1.logger.debug("Stopped Happy MCP server");
|
|
4564
4693
|
process.exit(0);
|
|
4565
4694
|
}
|
|
4566
4695
|
|
|
4567
|
-
function trimIdent(text) {
|
|
4568
|
-
const lines = text.split("\n");
|
|
4569
|
-
while (lines.length > 0 && lines[0].trim() === "") {
|
|
4570
|
-
lines.shift();
|
|
4571
|
-
}
|
|
4572
|
-
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
4573
|
-
lines.pop();
|
|
4574
|
-
}
|
|
4575
|
-
const minSpaces = lines.reduce((min, line) => {
|
|
4576
|
-
if (line.trim() === "") {
|
|
4577
|
-
return min;
|
|
4578
|
-
}
|
|
4579
|
-
const leadingSpaces = line.match(/^\s*/)[0].length;
|
|
4580
|
-
return Math.min(min, leadingSpaces);
|
|
4581
|
-
}, Infinity);
|
|
4582
|
-
const trimmedLines = lines.map((line) => line.slice(minSpaces));
|
|
4583
|
-
return trimmedLines.join("\n");
|
|
4584
|
-
}
|
|
4585
|
-
|
|
4586
4696
|
const PLIST_LABEL$1 = "com.happy-cli.daemon";
|
|
4587
4697
|
const PLIST_FILE$1 = `/Library/LaunchDaemons/${PLIST_LABEL$1}.plist`;
|
|
4588
4698
|
async function install$1() {
|
|
@@ -5201,9 +5311,8 @@ ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
|
5201
5311
|
const result = await authAndSetupMachineIfNeeded();
|
|
5202
5312
|
credentials = result.credentials;
|
|
5203
5313
|
}
|
|
5204
|
-
const isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
|
|
5205
5314
|
let settings = await readSettings();
|
|
5206
|
-
if (
|
|
5315
|
+
if (settings && settings.daemonAutoStartWhenRunningHappy === void 0) {
|
|
5207
5316
|
const shouldAutoStart = await new Promise((resolve) => {
|
|
5208
5317
|
let hasResolved = false;
|
|
5209
5318
|
const onSelect = (autoStart) => {
|
|
@@ -5229,7 +5338,7 @@ ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
|
5229
5338
|
console.log(chalk.yellow("\n You can enable this later by running: happy daemon install"));
|
|
5230
5339
|
}
|
|
5231
5340
|
}
|
|
5232
|
-
if (
|
|
5341
|
+
if (settings && settings.daemonAutoStartWhenRunningHappy) {
|
|
5233
5342
|
types$1.logger.debug("Starting Happy background service...");
|
|
5234
5343
|
if (!await isDaemonRunning()) {
|
|
5235
5344
|
const daemonProcess = spawnHappyCLI(["daemon", "start-sync"], {
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { l as logger, b as backoff, d as delay, R as RawJSONLinesSchema, c as configuration, e as encodeBase64, f as encodeBase64Url, g as decodeBase64, A as ApiClient } from './types-
|
|
2
|
+
import { l as logger, b as backoff, d as delay, R as RawJSONLinesSchema, c as configuration, e as encodeBase64, f as encodeBase64Url, g as decodeBase64, A as ApiClient } from './types-CJU8o8xd.mjs';
|
|
3
3
|
import { randomUUID, randomBytes } from 'node:crypto';
|
|
4
4
|
import { spawn, execSync } from 'node:child_process';
|
|
5
5
|
import { resolve, join, dirname as dirname$1 } from 'node:path';
|
|
@@ -42,6 +42,7 @@ class Session {
|
|
|
42
42
|
claudeEnvVars;
|
|
43
43
|
claudeArgs;
|
|
44
44
|
mcpServers;
|
|
45
|
+
allowedTools;
|
|
45
46
|
_onModeChange;
|
|
46
47
|
sessionId;
|
|
47
48
|
mode = "local";
|
|
@@ -56,6 +57,7 @@ class Session {
|
|
|
56
57
|
this.claudeEnvVars = opts.claudeEnvVars;
|
|
57
58
|
this.claudeArgs = opts.claudeArgs;
|
|
58
59
|
this.mcpServers = opts.mcpServers;
|
|
60
|
+
this.allowedTools = opts.allowedTools;
|
|
59
61
|
this._onModeChange = opts.onModeChange;
|
|
60
62
|
this.client.keepAlive(this.thinking, this.mode);
|
|
61
63
|
setInterval(() => {
|
|
@@ -114,6 +116,29 @@ function projectPath() {
|
|
|
114
116
|
return path;
|
|
115
117
|
}
|
|
116
118
|
|
|
119
|
+
function trimIdent(text) {
|
|
120
|
+
const lines = text.split("\n");
|
|
121
|
+
while (lines.length > 0 && lines[0].trim() === "") {
|
|
122
|
+
lines.shift();
|
|
123
|
+
}
|
|
124
|
+
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
125
|
+
lines.pop();
|
|
126
|
+
}
|
|
127
|
+
const minSpaces = lines.reduce((min, line) => {
|
|
128
|
+
if (line.trim() === "") {
|
|
129
|
+
return min;
|
|
130
|
+
}
|
|
131
|
+
const leadingSpaces = line.match(/^\s*/)[0].length;
|
|
132
|
+
return Math.min(min, leadingSpaces);
|
|
133
|
+
}, Infinity);
|
|
134
|
+
const trimmedLines = lines.map((line) => line.slice(minSpaces));
|
|
135
|
+
return trimmedLines.join("\n");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const systemPrompt = trimIdent(`
|
|
139
|
+
ALWAYS when you start a new chat - you must call a tool "mcp__happy__change_title" to set a chat title. When you think chat title is not relevant anymore - call the tool again to change it. When chat name is too generic and you have a change to make it more specific - call the tool again to change it. This title is needed to easily find the chat in the future. Help human.
|
|
140
|
+
`);
|
|
141
|
+
|
|
117
142
|
dirname$1(fileURLToPath$1(import.meta.url));
|
|
118
143
|
async function claudeLocal(opts) {
|
|
119
144
|
const projectDir = getProjectPath(opts.path);
|
|
@@ -161,6 +186,13 @@ async function claudeLocal(opts) {
|
|
|
161
186
|
if (startFrom) {
|
|
162
187
|
args.push("--resume", startFrom);
|
|
163
188
|
}
|
|
189
|
+
args.push("--append-system-prompt", systemPrompt);
|
|
190
|
+
if (opts.mcpServers && Object.keys(opts.mcpServers).length > 0) {
|
|
191
|
+
args.push("--mcp-config", JSON.stringify({ mcpServers: opts.mcpServers }));
|
|
192
|
+
}
|
|
193
|
+
if (opts.allowedTools && opts.allowedTools.length > 0) {
|
|
194
|
+
args.push("--allowedTools", opts.allowedTools.join(","));
|
|
195
|
+
}
|
|
164
196
|
if (opts.claudeArgs) {
|
|
165
197
|
args.push(...opts.claudeArgs);
|
|
166
198
|
}
|
|
@@ -505,7 +537,9 @@ async function claudeLocalLauncher(session) {
|
|
|
505
537
|
sessionId: session.sessionId,
|
|
506
538
|
workingDirectory: session.path,
|
|
507
539
|
onMessage: (message) => {
|
|
508
|
-
|
|
540
|
+
if (message.type !== "summary") {
|
|
541
|
+
session.client.sendClaudeSessionMessage(message);
|
|
542
|
+
}
|
|
509
543
|
}
|
|
510
544
|
});
|
|
511
545
|
let exitReason = null;
|
|
@@ -535,7 +569,9 @@ async function claudeLocalLauncher(session) {
|
|
|
535
569
|
}
|
|
536
570
|
session.client.setHandler("abort", doAbort);
|
|
537
571
|
session.client.setHandler("switch", doSwitch);
|
|
538
|
-
session.queue.setOnMessage(
|
|
572
|
+
session.queue.setOnMessage((message, mode) => {
|
|
573
|
+
doSwitch();
|
|
574
|
+
});
|
|
539
575
|
if (session.queue.size() > 0) {
|
|
540
576
|
return "switch";
|
|
541
577
|
}
|
|
@@ -556,7 +592,9 @@ async function claudeLocalLauncher(session) {
|
|
|
556
592
|
onThinkingChange: session.onThinkingChange,
|
|
557
593
|
abort: processAbortController.signal,
|
|
558
594
|
claudeEnvVars: session.claudeEnvVars,
|
|
559
|
-
claudeArgs: session.claudeArgs
|
|
595
|
+
claudeArgs: session.claudeArgs,
|
|
596
|
+
mcpServers: session.mcpServers,
|
|
597
|
+
allowedTools: session.allowedTools
|
|
560
598
|
});
|
|
561
599
|
if (!exitReason) {
|
|
562
600
|
exitReason = "exit";
|
|
@@ -1318,6 +1356,12 @@ async function claudeRemote(opts) {
|
|
|
1318
1356
|
logger.debug("[claudeRemote] /compact command detected - will process as normal but with compaction behavior");
|
|
1319
1357
|
}
|
|
1320
1358
|
const isCompactCommand = specialCommand.type === "compact";
|
|
1359
|
+
if (isCompactCommand) {
|
|
1360
|
+
logger.debug("[claudeRemote] Compaction started");
|
|
1361
|
+
if (opts.onCompletionEvent) {
|
|
1362
|
+
opts.onCompletionEvent("Compaction started");
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1321
1365
|
let message = new PushableAsyncIterable();
|
|
1322
1366
|
message.push({
|
|
1323
1367
|
type: "user",
|
|
@@ -1357,12 +1401,6 @@ async function claudeRemote(opts) {
|
|
|
1357
1401
|
logger.debug(`[claudeRemote] Session file found: ${systemInit.session_id} ${found}`);
|
|
1358
1402
|
opts.onSessionFound(systemInit.session_id);
|
|
1359
1403
|
}
|
|
1360
|
-
if (isCompactCommand) {
|
|
1361
|
-
logger.debug("[claudeRemote] Compaction started");
|
|
1362
|
-
if (opts.onCompletionEvent) {
|
|
1363
|
-
opts.onCompletionEvent("Compaction started");
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
1404
|
}
|
|
1367
1405
|
if (message2.type === "result") {
|
|
1368
1406
|
updateThinking(false);
|
|
@@ -2070,9 +2108,6 @@ async function claudeRemoteLauncher(session) {
|
|
|
2070
2108
|
sessionId: session.sessionId,
|
|
2071
2109
|
workingDirectory: session.path,
|
|
2072
2110
|
onMessage: (message) => {
|
|
2073
|
-
if (message.type === "summary") {
|
|
2074
|
-
session.client.sendClaudeSessionMessage(message);
|
|
2075
|
-
}
|
|
2076
2111
|
}
|
|
2077
2112
|
});
|
|
2078
2113
|
let exitReason = null;
|
|
@@ -2232,8 +2267,8 @@ async function claudeRemoteLauncher(session) {
|
|
|
2232
2267
|
model: messageData.mode.model,
|
|
2233
2268
|
fallbackModel: messageData.mode.fallbackModel,
|
|
2234
2269
|
customSystemPrompt: messageData.mode.customSystemPrompt,
|
|
2235
|
-
appendSystemPrompt: messageData.mode.appendSystemPrompt,
|
|
2236
|
-
allowedTools: messageData.mode.allowedTools,
|
|
2270
|
+
appendSystemPrompt: messageData.mode.appendSystemPrompt ? messageData.mode.appendSystemPrompt + "\n" + systemPrompt : systemPrompt,
|
|
2271
|
+
allowedTools: messageData.mode.allowedTools ? [...messageData.mode.allowedTools, ...session.allowedTools ? session.allowedTools : []] : session.allowedTools ? [...session.allowedTools] : void 0,
|
|
2237
2272
|
disallowedTools: messageData.mode.disallowedTools,
|
|
2238
2273
|
onSessionFound: (sessionId) => {
|
|
2239
2274
|
sdkToLogConverter.updateSessionId(sessionId);
|
|
@@ -2309,6 +2344,7 @@ async function loop(opts) {
|
|
|
2309
2344
|
mcpServers: opts.mcpServers,
|
|
2310
2345
|
logPath,
|
|
2311
2346
|
messageQueue: opts.messageQueue,
|
|
2347
|
+
allowedTools: opts.allowedTools,
|
|
2312
2348
|
onModeChange: opts.onModeChange
|
|
2313
2349
|
});
|
|
2314
2350
|
if (opts.onSessionReady) {
|
|
@@ -2343,7 +2379,7 @@ async function loop(opts) {
|
|
|
2343
2379
|
}
|
|
2344
2380
|
|
|
2345
2381
|
var name = "happy-coder";
|
|
2346
|
-
var version = "0.
|
|
2382
|
+
var version = "0.8.0";
|
|
2347
2383
|
var description = "Claude Code session sharing CLI";
|
|
2348
2384
|
var author = "Kirill Dubovitskiy";
|
|
2349
2385
|
var license = "MIT";
|
|
@@ -2393,7 +2429,7 @@ var scripts = {
|
|
|
2393
2429
|
test: "yarn build && vitest run",
|
|
2394
2430
|
"test:watch": "vitest",
|
|
2395
2431
|
"test:integration-test-env": "yarn build && tsx --env-file .env.integration-test node_modules/.bin/vitest run",
|
|
2396
|
-
dev: "yarn build && npx tsx src/index.ts",
|
|
2432
|
+
dev: "DEBUG=1 yarn build && npx tsx src/index.ts",
|
|
2397
2433
|
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
2398
2434
|
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
2399
2435
|
prepublishOnly: "yarn build && yarn test",
|
|
@@ -4021,7 +4057,7 @@ async function authAndSetupMachineIfNeeded() {
|
|
|
4021
4057
|
const settings = await updateSettings(async (s) => {
|
|
4022
4058
|
if (!s.machineId) {
|
|
4023
4059
|
const newMachineId = randomUUID();
|
|
4024
|
-
|
|
4060
|
+
logger.debug(`[AUTH] No machine ID found, generating new one: ${newMachineId}; We will not create machine on startup since we don't have api client intialized`);
|
|
4025
4061
|
return {
|
|
4026
4062
|
...s,
|
|
4027
4063
|
machineId: randomUUID()
|
|
@@ -4302,6 +4338,88 @@ async function startDaemon() {
|
|
|
4302
4338
|
}
|
|
4303
4339
|
}
|
|
4304
4340
|
|
|
4341
|
+
async function startHappyServer(client) {
|
|
4342
|
+
const handler = async (title) => {
|
|
4343
|
+
logger.debug("[happyMCP] Changing title to:", title);
|
|
4344
|
+
try {
|
|
4345
|
+
client.sendClaudeSessionMessage({
|
|
4346
|
+
type: "summary",
|
|
4347
|
+
summary: title,
|
|
4348
|
+
leafUuid: randomUUID()
|
|
4349
|
+
});
|
|
4350
|
+
return { success: true };
|
|
4351
|
+
} catch (error) {
|
|
4352
|
+
return { success: false, error: String(error) };
|
|
4353
|
+
}
|
|
4354
|
+
};
|
|
4355
|
+
const mcp = new McpServer({
|
|
4356
|
+
name: "Happy MCP",
|
|
4357
|
+
version: "1.0.0",
|
|
4358
|
+
description: "Happy CLI MCP server with chat session management tools"
|
|
4359
|
+
});
|
|
4360
|
+
mcp.registerTool("change_title", {
|
|
4361
|
+
description: "Change the title of the current chat session",
|
|
4362
|
+
title: "Change Chat Title",
|
|
4363
|
+
inputSchema: {
|
|
4364
|
+
title: z$1.string().describe("The new title for the chat session")
|
|
4365
|
+
}
|
|
4366
|
+
}, async (args) => {
|
|
4367
|
+
const response = await handler(args.title);
|
|
4368
|
+
logger.debug("[happyMCP] Response:", response);
|
|
4369
|
+
if (response.success) {
|
|
4370
|
+
return {
|
|
4371
|
+
content: [
|
|
4372
|
+
{
|
|
4373
|
+
type: "text",
|
|
4374
|
+
text: `Successfully changed chat title to: "${args.title}"`
|
|
4375
|
+
}
|
|
4376
|
+
],
|
|
4377
|
+
isError: false
|
|
4378
|
+
};
|
|
4379
|
+
} else {
|
|
4380
|
+
return {
|
|
4381
|
+
content: [
|
|
4382
|
+
{
|
|
4383
|
+
type: "text",
|
|
4384
|
+
text: `Failed to change chat title: ${response.error || "Unknown error"}`
|
|
4385
|
+
}
|
|
4386
|
+
],
|
|
4387
|
+
isError: true
|
|
4388
|
+
};
|
|
4389
|
+
}
|
|
4390
|
+
});
|
|
4391
|
+
const transport = new StreamableHTTPServerTransport({
|
|
4392
|
+
// NOTE: Returning session id here will result in claude
|
|
4393
|
+
// sdk spawn to fail with `Invalid Request: Server already initialized`
|
|
4394
|
+
sessionIdGenerator: void 0
|
|
4395
|
+
});
|
|
4396
|
+
await mcp.connect(transport);
|
|
4397
|
+
const server = createServer(async (req, res) => {
|
|
4398
|
+
try {
|
|
4399
|
+
await transport.handleRequest(req, res);
|
|
4400
|
+
} catch (error) {
|
|
4401
|
+
logger.debug("Error handling request:", error);
|
|
4402
|
+
if (!res.headersSent) {
|
|
4403
|
+
res.writeHead(500).end();
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
});
|
|
4407
|
+
const baseUrl = await new Promise((resolve) => {
|
|
4408
|
+
server.listen(0, "127.0.0.1", () => {
|
|
4409
|
+
const addr = server.address();
|
|
4410
|
+
resolve(new URL(`http://127.0.0.1:${addr.port}`));
|
|
4411
|
+
});
|
|
4412
|
+
});
|
|
4413
|
+
return {
|
|
4414
|
+
url: baseUrl.toString(),
|
|
4415
|
+
toolNames: ["change_title"],
|
|
4416
|
+
stop: () => {
|
|
4417
|
+
mcp.close();
|
|
4418
|
+
server.close();
|
|
4419
|
+
}
|
|
4420
|
+
};
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4305
4423
|
async function start(credentials, options = {}) {
|
|
4306
4424
|
const workingDirectory = process.cwd();
|
|
4307
4425
|
const sessionTag = randomUUID();
|
|
@@ -4361,6 +4479,8 @@ async function start(credentials, options = {}) {
|
|
|
4361
4479
|
}
|
|
4362
4480
|
});
|
|
4363
4481
|
const session = api.sessionSyncClient(response);
|
|
4482
|
+
const happyServer = await startHappyServer(session);
|
|
4483
|
+
logger.debug(`[START] Happy MCP server started at ${happyServer.url}`);
|
|
4364
4484
|
const logPath = await logger.logFilePathPromise;
|
|
4365
4485
|
logger.infoDeveloper(`Session: ${response.id}`);
|
|
4366
4486
|
logger.infoDeveloper(`Logs: ${logPath}`);
|
|
@@ -4495,6 +4615,7 @@ async function start(credentials, options = {}) {
|
|
|
4495
4615
|
await session.close();
|
|
4496
4616
|
}
|
|
4497
4617
|
stopCaffeinate();
|
|
4618
|
+
happyServer.stop();
|
|
4498
4619
|
logger.debug("[START] Cleanup complete, exiting");
|
|
4499
4620
|
process.exit(0);
|
|
4500
4621
|
} catch (error) {
|
|
@@ -4519,6 +4640,7 @@ async function start(credentials, options = {}) {
|
|
|
4519
4640
|
startingMode: options.startingMode,
|
|
4520
4641
|
messageQueue,
|
|
4521
4642
|
api,
|
|
4643
|
+
allowedTools: happyServer.toolNames.map((toolName) => `mcp__happy__${toolName}`),
|
|
4522
4644
|
onModeChange: (newMode) => {
|
|
4523
4645
|
session.sendSessionEvent({ type: "switch", mode: newMode });
|
|
4524
4646
|
session.updateAgentState((currentState) => ({
|
|
@@ -4528,7 +4650,12 @@ async function start(credentials, options = {}) {
|
|
|
4528
4650
|
},
|
|
4529
4651
|
onSessionReady: (sessionInstance) => {
|
|
4530
4652
|
},
|
|
4531
|
-
mcpServers: {
|
|
4653
|
+
mcpServers: {
|
|
4654
|
+
"happy": {
|
|
4655
|
+
type: "http",
|
|
4656
|
+
url: happyServer.url
|
|
4657
|
+
}
|
|
4658
|
+
},
|
|
4532
4659
|
session,
|
|
4533
4660
|
claudeEnvVars: options.claudeEnvVars,
|
|
4534
4661
|
claudeArgs: options.claudeArgs
|
|
@@ -4540,28 +4667,11 @@ async function start(credentials, options = {}) {
|
|
|
4540
4667
|
await session.close();
|
|
4541
4668
|
stopCaffeinate();
|
|
4542
4669
|
logger.debug("Stopped sleep prevention");
|
|
4670
|
+
happyServer.stop();
|
|
4671
|
+
logger.debug("Stopped Happy MCP server");
|
|
4543
4672
|
process.exit(0);
|
|
4544
4673
|
}
|
|
4545
4674
|
|
|
4546
|
-
function trimIdent(text) {
|
|
4547
|
-
const lines = text.split("\n");
|
|
4548
|
-
while (lines.length > 0 && lines[0].trim() === "") {
|
|
4549
|
-
lines.shift();
|
|
4550
|
-
}
|
|
4551
|
-
while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
|
|
4552
|
-
lines.pop();
|
|
4553
|
-
}
|
|
4554
|
-
const minSpaces = lines.reduce((min, line) => {
|
|
4555
|
-
if (line.trim() === "") {
|
|
4556
|
-
return min;
|
|
4557
|
-
}
|
|
4558
|
-
const leadingSpaces = line.match(/^\s*/)[0].length;
|
|
4559
|
-
return Math.min(min, leadingSpaces);
|
|
4560
|
-
}, Infinity);
|
|
4561
|
-
const trimmedLines = lines.map((line) => line.slice(minSpaces));
|
|
4562
|
-
return trimmedLines.join("\n");
|
|
4563
|
-
}
|
|
4564
|
-
|
|
4565
4675
|
const PLIST_LABEL$1 = "com.happy-cli.daemon";
|
|
4566
4676
|
const PLIST_FILE$1 = `/Library/LaunchDaemons/${PLIST_LABEL$1}.plist`;
|
|
4567
4677
|
async function install$1() {
|
|
@@ -5180,9 +5290,8 @@ ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
|
5180
5290
|
const result = await authAndSetupMachineIfNeeded();
|
|
5181
5291
|
credentials = result.credentials;
|
|
5182
5292
|
}
|
|
5183
|
-
const isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
|
|
5184
5293
|
let settings = await readSettings();
|
|
5185
|
-
if (
|
|
5294
|
+
if (settings && settings.daemonAutoStartWhenRunningHappy === void 0) {
|
|
5186
5295
|
const shouldAutoStart = await new Promise((resolve) => {
|
|
5187
5296
|
let hasResolved = false;
|
|
5188
5297
|
const onSelect = (autoStart) => {
|
|
@@ -5208,7 +5317,7 @@ ${chalk.bold.cyan("Claude Code Options (from `claude --help`):")}
|
|
|
5208
5317
|
console.log(chalk.yellow("\n You can enable this later by running: happy daemon install"));
|
|
5209
5318
|
}
|
|
5210
5319
|
}
|
|
5211
|
-
if (
|
|
5320
|
+
if (settings && settings.daemonAutoStartWhenRunningHappy) {
|
|
5212
5321
|
logger.debug("Starting Happy background service...");
|
|
5213
5322
|
if (!await isDaemonRunning()) {
|
|
5214
5323
|
const daemonProcess = spawnHappyCLI(["daemon", "start-sync"], {
|
package/dist/lib.cjs
CHANGED
package/dist/lib.d.cts
CHANGED
|
@@ -746,9 +746,9 @@ declare class Configuration {
|
|
|
746
746
|
readonly settingsFile: string;
|
|
747
747
|
readonly privateKeyFile: string;
|
|
748
748
|
readonly daemonStateFile: string;
|
|
749
|
+
readonly isExperimentalEnabled: boolean;
|
|
749
750
|
constructor();
|
|
750
751
|
}
|
|
751
752
|
declare const configuration: Configuration;
|
|
752
753
|
|
|
753
|
-
export { ApiClient, ApiSessionClient, RawJSONLinesSchema, configuration, logger };
|
|
754
|
-
export type { RawJSONLines };
|
|
754
|
+
export { ApiClient, ApiSessionClient, type RawJSONLines, RawJSONLinesSchema, configuration, logger };
|
package/dist/lib.d.mts
CHANGED
|
@@ -746,9 +746,9 @@ declare class Configuration {
|
|
|
746
746
|
readonly settingsFile: string;
|
|
747
747
|
readonly privateKeyFile: string;
|
|
748
748
|
readonly daemonStateFile: string;
|
|
749
|
+
readonly isExperimentalEnabled: boolean;
|
|
749
750
|
constructor();
|
|
750
751
|
}
|
|
751
752
|
declare const configuration: Configuration;
|
|
752
753
|
|
|
753
|
-
export { ApiClient, ApiSessionClient, RawJSONLinesSchema, configuration, logger };
|
|
754
|
-
export type { RawJSONLines };
|
|
754
|
+
export { ApiClient, ApiSessionClient, type RawJSONLines, RawJSONLinesSchema, configuration, logger };
|
package/dist/lib.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-
|
|
1
|
+
export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-CJU8o8xd.mjs';
|
|
2
2
|
import 'axios';
|
|
3
3
|
import 'chalk';
|
|
4
4
|
import 'fs';
|
|
@@ -22,6 +22,7 @@ class Configuration {
|
|
|
22
22
|
settingsFile;
|
|
23
23
|
privateKeyFile;
|
|
24
24
|
daemonStateFile;
|
|
25
|
+
isExperimentalEnabled;
|
|
25
26
|
constructor() {
|
|
26
27
|
this.serverUrl = process.env.HAPPY_SERVER_URL || "https://handy-api.korshakov.org";
|
|
27
28
|
const args = process.argv.slice(2);
|
|
@@ -37,6 +38,7 @@ class Configuration {
|
|
|
37
38
|
this.settingsFile = join(this.happyHomeDir, "settings.json");
|
|
38
39
|
this.privateKeyFile = join(this.happyHomeDir, "access.key");
|
|
39
40
|
this.daemonStateFile = join(this.happyHomeDir, "daemon.state.json");
|
|
41
|
+
this.isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
const configuration = new Configuration();
|
|
@@ -24,6 +24,7 @@ class Configuration {
|
|
|
24
24
|
settingsFile;
|
|
25
25
|
privateKeyFile;
|
|
26
26
|
daemonStateFile;
|
|
27
|
+
isExperimentalEnabled;
|
|
27
28
|
constructor() {
|
|
28
29
|
this.serverUrl = process.env.HAPPY_SERVER_URL || "https://handy-api.korshakov.org";
|
|
29
30
|
const args = process.argv.slice(2);
|
|
@@ -39,6 +40,7 @@ class Configuration {
|
|
|
39
40
|
this.settingsFile = node_path.join(this.happyHomeDir, "settings.json");
|
|
40
41
|
this.privateKeyFile = node_path.join(this.happyHomeDir, "access.key");
|
|
41
42
|
this.daemonStateFile = node_path.join(this.happyHomeDir, "daemon.state.json");
|
|
43
|
+
this.isExperimentalEnabled = ["true", "1", "yes"].includes(process.env.HAPPY_EXPERIMENTAL?.toLowerCase() || "");
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
const configuration = new Configuration();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "happy-coder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Claude Code session sharing CLI",
|
|
5
5
|
"author": "Kirill Dubovitskiy",
|
|
6
6
|
"license": "MIT",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"test": "yarn build && vitest run",
|
|
51
51
|
"test:watch": "vitest",
|
|
52
52
|
"test:integration-test-env": "yarn build && tsx --env-file .env.integration-test node_modules/.bin/vitest run",
|
|
53
|
-
"dev": "yarn build && npx tsx src/index.ts",
|
|
53
|
+
"dev": "DEBUG=1 yarn build && npx tsx src/index.ts",
|
|
54
54
|
"dev:local-server": "yarn build && tsx --env-file .env.dev-local-server src/index.ts",
|
|
55
55
|
"dev:integration-test-env": "yarn build && tsx --env-file .env.integration-test src/index.ts",
|
|
56
56
|
"prepublishOnly": "yarn build && yarn test",
|