@zonease/happy 0.6.48 → 0.6.50

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.
Files changed (87) hide show
  1. package/dist/{api-lxlaOAan.cjs → api-BYlwkdNv.cjs} +4 -4
  2. package/dist/{api-BQORFd9R.mjs → api-DkLi_kVG.mjs} +3 -3
  3. package/dist/{auth-DO429YIh.cjs → auth-BN2MIzje.cjs} +2 -2
  4. package/dist/{auth-Cch5XGgf.mjs → auth-Br_PQMzx.mjs} +4 -4
  5. package/dist/{auth-DEvweBty.mjs → auth-CLoQ_EpI.mjs} +2 -2
  6. package/dist/{auth-D3n2c6ef.cjs → auth-CROR_1u-.cjs} +4 -4
  7. package/dist/{browser-ChToh76t.cjs → browser-B1v3MMco.cjs} +1 -1
  8. package/dist/{browser-C6JpWqen.mjs → browser-BH7Yz53y.mjs} +1 -1
  9. package/dist/{claudeLocal-BRkqm7Dv.cjs → claudeLocal-Clj5q_mB.cjs} +1 -1
  10. package/dist/{claudeLocal-D7uEQO2e.mjs → claudeLocal-DwVoD2YO.mjs} +1 -1
  11. package/dist/codex/happyMcpStdioBridge.cjs +8 -1
  12. package/dist/codex/happyMcpStdioBridge.mjs +8 -1
  13. package/dist/{config-oC3LtNiN.mjs → config-ClokJJ4W.mjs} +1 -1
  14. package/dist/{config-D1pd0h7A.cjs → config-Xgf8XiBz.cjs} +1 -1
  15. package/dist/{connect-CyFpNr11.mjs → connect-Bsi7BKyt.mjs} +5 -5
  16. package/dist/{connect-OAvNEIm0.cjs → connect-DPFMoUxr.cjs} +5 -5
  17. package/dist/{controlClient-CEliu-dY.mjs → controlClient-hW34LqD6.mjs} +1 -1
  18. package/dist/{controlClient-BAJAvvWO.cjs → controlClient-pqJuFXzr.cjs} +1 -1
  19. package/dist/{cursorApprovalHook-BWYkdy2I.cjs → cursorApprovalHook-BnC8NDyU.cjs} +2 -2
  20. package/dist/{cursorApprovalHook-P8q2-Ot5.mjs → cursorApprovalHook-OAGqnLby.mjs} +2 -2
  21. package/dist/{cursorApprovalServer-DNOUUpbJ.mjs → cursorApprovalServer-BxHnGAAd.mjs} +1 -1
  22. package/dist/{cursorApprovalServer-VR5ZdRqZ.cjs → cursorApprovalServer-DZbo7N_4.cjs} +1 -1
  23. package/dist/{deviceKeys-CFpY68YY.cjs → deviceKeys-Bx3Fr_eu.cjs} +2 -2
  24. package/dist/{deviceKeys-CxID9XkJ.mjs → deviceKeys-CkKNX4wJ.mjs} +2 -2
  25. package/dist/{doctor-CrBPDAJR.mjs → doctor-BJLbeVEF.mjs} +2 -2
  26. package/dist/{doctor-qquYvrNu.cjs → doctor-pPDqy8Yd.cjs} +2 -2
  27. package/dist/{downloadImage-DFwUJ1-t.mjs → downloadImage-0txRA95g.mjs} +1 -1
  28. package/dist/{downloadImage-Cqy7ZDq9.cjs → downloadImage-CzSm-976.cjs} +1 -1
  29. package/dist/{enroll-CPhX2YYW.cjs → enroll-6uJeBJWh.cjs} +3 -3
  30. package/dist/{enroll-BS4x3OUQ.mjs → enroll-tMntIbDf.mjs} +3 -3
  31. package/dist/{evidence-HQhiWcci.mjs → evidence-CW5gZkfi.mjs} +1 -1
  32. package/dist/{evidence-mn6vjFpr.cjs → evidence-DFm-wY1a.cjs} +1 -1
  33. package/dist/{formatImageMessage-DFfcusj_.cjs → formatImageMessage-CiGJLCW9.cjs} +1 -1
  34. package/dist/{formatImageMessage-cI_AEqo1.mjs → formatImageMessage-H_hYAW10.mjs} +1 -1
  35. package/dist/{index-BeKrqPYR.cjs → index-BNMjIKud.cjs} +34 -0
  36. package/dist/{index-DTV7drqs.mjs → index-Cd3VmMtg.mjs} +34 -0
  37. package/dist/index.cjs +46 -46
  38. package/dist/index.mjs +46 -46
  39. package/dist/{install-tBBR7NUn.cjs → install-D0TcS_eJ.cjs} +2 -2
  40. package/dist/{install-Bm5eNojk.cjs → install-DVjvG3OO.cjs} +1 -1
  41. package/dist/{install-sbpQfY_l.mjs → install-DWp3wQ7H.mjs} +1 -1
  42. package/dist/{install-Bn2199Kq.mjs → install-Djm2ft-n.mjs} +2 -2
  43. package/dist/lib.cjs +4 -4
  44. package/dist/lib.mjs +4 -4
  45. package/dist/{loadableToolOutput-BzTaDndK.cjs → loadableToolOutput-CSXVglfo.cjs} +2 -2
  46. package/dist/{loadableToolOutput-DbXbVycg.mjs → loadableToolOutput-c7sztMrh.mjs} +2 -2
  47. package/dist/{persistence-YyHJPrEJ.mjs → persistence-BcDMO5bT.mjs} +1 -1
  48. package/dist/{persistence-ACqGUEtH.cjs → persistence-CRIRAcLQ.cjs} +1 -1
  49. package/dist/{push-CICE33Pk.mjs → push-C5JhAM3c.mjs} +6 -6
  50. package/dist/{push-DN6X8puN.cjs → push-gclTT6Gj.cjs} +6 -6
  51. package/dist/{registerKillSessionHandler-CaFN7Ti_.mjs → registerKillSessionHandler-cbV2Ljf7.mjs} +61 -6
  52. package/dist/{registerKillSessionHandler-BvYc-FOQ.cjs → registerKillSessionHandler-wQuDB--t.cjs} +61 -6
  53. package/dist/{run-Djrrraug.cjs → run-BvX34xMy.cjs} +8 -8
  54. package/dist/{run-DEIpQXCf.mjs → run-UpOvLJEM.mjs} +8 -8
  55. package/dist/{runClaude-BuXbk4Hk.mjs → runClaude-D4D68U-b.mjs} +16 -16
  56. package/dist/{runClaude-DEnJA94E.cjs → runClaude-HVerV5KP.cjs} +17 -17
  57. package/dist/{runCodex-xfey8ybA.cjs → runCodex-BEuiGZkj.cjs} +18 -18
  58. package/dist/{runCodex-DDJ4ld9g.mjs → runCodex-Rrla8J-y.mjs} +18 -18
  59. package/dist/{runCursor-Ea-0SqPz.mjs → runCursor-CK_a623G.mjs} +16 -16
  60. package/dist/{runCursor-HDQXcY5a.cjs → runCursor-MN2kTiaV.cjs} +16 -16
  61. package/dist/{runGemini-DupL8lXI.cjs → runGemini-Bv7myj8x.cjs} +24 -20
  62. package/dist/{runGemini-DNXknLIG.mjs → runGemini-uaX3ydTl.mjs} +24 -20
  63. package/dist/{runOneShot-C3adjQdt.mjs → runOneShot-CVh2sOTm.mjs} +3 -3
  64. package/dist/{runOneShot-ncBtn_Gm.cjs → runOneShot-DkY4JIpR.cjs} +3 -3
  65. package/dist/{serve-DS7_dJ2V.cjs → serve-Bq4RYzeU.cjs} +6 -6
  66. package/dist/{serve-B8J5nW6c.mjs → serve-HcmoF0ld.mjs} +6 -6
  67. package/dist/{session-DqtoNEcE.cjs → session-BPOvcH-e.cjs} +5 -5
  68. package/dist/{session-DLX5ZdxL.mjs → session-DzfH_479.mjs} +5 -5
  69. package/dist/{sessionKeyCache-D0Y_V05P.mjs → sessionKeyCache-BuUtI1-I.mjs} +1 -1
  70. package/dist/{sessionKeyCache-BnR0ARqZ.cjs → sessionKeyCache-Cv83YxdE.cjs} +1 -1
  71. package/dist/{sessionList-D1jByB-c.mjs → sessionList-CvRpDuXi.mjs} +2 -2
  72. package/dist/{sessionList-B20S2H2v.cjs → sessionList-DFDLT9tB.cjs} +2 -2
  73. package/dist/{sessionSummary-HZWtQ6mW.mjs → sessionSummary-C7gSZAy6.mjs} +3 -3
  74. package/dist/{sessionSummary-BfkUnQew.cjs → sessionSummary-CbXjJeeZ.cjs} +3 -3
  75. package/dist/{setupOfflineReconnection-E5sAFKRm.mjs → setupOfflineReconnection-IzJm84kr.mjs} +3 -3
  76. package/dist/{setupOfflineReconnection-C8Ll2khg.cjs → setupOfflineReconnection-JAaRZvkV.cjs} +3 -3
  77. package/dist/{spawnHappyCLI-BabByXDO.cjs → spawnHappyCLI-Bx0fWHXA.cjs} +1 -1
  78. package/dist/{spawnHappyCLI-iEcKfItK.mjs → spawnHappyCLI-CNM_tqcu.mjs} +1 -1
  79. package/dist/{ssh-BMawo5IG.mjs → ssh-CPgIeMd5.mjs} +5 -5
  80. package/dist/{ssh-BWLfu_Bu.cjs → ssh-DWCo3_QV.cjs} +5 -5
  81. package/dist/{task-CmzA8KBB.cjs → task-BpEYajLx.cjs} +6 -6
  82. package/dist/{task-JIXYT6Ut.mjs → task-GHqmZme5.mjs} +6 -6
  83. package/dist/{uninstall-DE2hMEa8.mjs → uninstall-BKM0TWLi.mjs} +2 -2
  84. package/dist/{uninstall-CRICQerq.cjs → uninstall-izzXdPwS.cjs} +2 -2
  85. package/dist/{update-DYLwPQB5.cjs → update-B0YoHEA9.cjs} +3 -3
  86. package/dist/{update-Bo6YcGpg.mjs → update-DflmX6cr.mjs} +3 -3
  87. package/package.json +1 -1
package/dist/lib.cjs CHANGED
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- var api = require('./api-lxlaOAan.cjs');
4
- var logger = require('./persistence-ACqGUEtH.cjs');
3
+ var api = require('./api-BYlwkdNv.cjs');
4
+ var logger = require('./persistence-CRIRAcLQ.cjs');
5
5
  var types = require('./types-DWeB7aqh.cjs');
6
6
  require('axios');
7
7
  require('node:events');
8
8
  require('socket.io-client');
9
9
  require('zod');
10
- require('./index-BeKrqPYR.cjs');
10
+ require('./index-BNMjIKud.cjs');
11
11
  require('@paralleldrive/cuid2');
12
12
  require('./time-Bj2HRkKH.cjs');
13
13
  require('node:crypto');
@@ -30,7 +30,7 @@ require('tweetnacl');
30
30
  require('@stablelib/base64');
31
31
  require('node:readline');
32
32
  require('expo-server-sdk');
33
- require('./sessionKeyCache-BnR0ARqZ.cjs');
33
+ require('./sessionKeyCache-Cv83YxdE.cjs');
34
34
  require('chalk');
35
35
  require('fs');
36
36
 
package/dist/lib.mjs CHANGED
@@ -1,11 +1,11 @@
1
- export { A as ApiClient, a as ApiSessionClient } from './api-BQORFd9R.mjs';
2
- export { c as configuration, l as logger } from './persistence-YyHJPrEJ.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient } from './api-DkLi_kVG.mjs';
2
+ export { c as configuration, l as logger } from './persistence-BcDMO5bT.mjs';
3
3
  export { R as RawJSONLinesSchema } from './types-BoJh4Qfv.mjs';
4
4
  import 'axios';
5
5
  import 'node:events';
6
6
  import 'socket.io-client';
7
7
  import 'zod';
8
- import './index-DTV7drqs.mjs';
8
+ import './index-Cd3VmMtg.mjs';
9
9
  import '@paralleldrive/cuid2';
10
10
  import './time-DgsBLJA_.mjs';
11
11
  import 'node:crypto';
@@ -28,6 +28,6 @@ import 'tweetnacl';
28
28
  import '@stablelib/base64';
29
29
  import 'node:readline';
30
30
  import 'expo-server-sdk';
31
- import './sessionKeyCache-D0Y_V05P.mjs';
31
+ import './sessionKeyCache-BuUtI1-I.mjs';
32
32
  import 'chalk';
33
33
  import 'fs';
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var node_crypto = require('node:crypto');
4
- var logger = require('./persistence-ACqGUEtH.cjs');
5
- var api = require('./api-lxlaOAan.cjs');
4
+ var logger = require('./persistence-CRIRAcLQ.cjs');
5
+ var api = require('./api-BYlwkdNv.cjs');
6
6
 
7
7
  class BaseReasoningProcessor {
8
8
  accumulator = "";
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { l as logger } from './persistence-YyHJPrEJ.mjs';
3
- import { s as saveToolOutputRecord } from './api-BQORFd9R.mjs';
2
+ import { l as logger } from './persistence-BcDMO5bT.mjs';
3
+ import { s as saveToolOutputRecord } from './api-DkLi_kVG.mjs';
4
4
 
5
5
  class BaseReasoningProcessor {
6
6
  accumulator = "";
@@ -14,7 +14,7 @@ function isDebug() {
14
14
  }
15
15
 
16
16
  var name = "@zonease/happy";
17
- var version = "0.6.48";
17
+ var version = "0.6.50";
18
18
  var description = "Mobile and Web client for Claude Code, Codex, and Gemini";
19
19
  var author = "ZonEaseTech";
20
20
  var license = "MIT";
@@ -35,7 +35,7 @@ function isDebug() {
35
35
  }
36
36
 
37
37
  var name = "@zonease/happy";
38
- var version = "0.6.48";
38
+ var version = "0.6.50";
39
39
  var description = "Mobile and Web client for Claude Code, Codex, and Gemini";
40
40
  var author = "ZonEaseTech";
41
41
  var license = "MIT";
@@ -1,7 +1,7 @@
1
1
  import { stat, readFile } from 'node:fs/promises';
2
2
  import { io } from 'socket.io-client';
3
- import { c as configuration, e as encodeBase64, f as encrypt, g as decrypt, h as decodeBase64 } from './persistence-YyHJPrEJ.mjs';
4
- import { resolveDeviceForCommand } from './ssh-BMawo5IG.mjs';
3
+ import { c as configuration, e as encodeBase64, f as encrypt, g as decrypt, h as decodeBase64 } from './persistence-BcDMO5bT.mjs';
4
+ import { resolveDeviceForCommand } from './ssh-CPgIeMd5.mjs';
5
5
  import 'node:fs';
6
6
  import 'node:os';
7
7
  import 'node:path';
@@ -10,16 +10,16 @@ import 'node:crypto';
10
10
  import 'tweetnacl';
11
11
  import 'chalk';
12
12
  import 'fs';
13
- import './deviceKeys-CxID9XkJ.mjs';
13
+ import './deviceKeys-CkKNX4wJ.mjs';
14
14
  import 'axios';
15
- import './auth-DEvweBty.mjs';
15
+ import './auth-CLoQ_EpI.mjs';
16
16
  import 'qrcode-terminal';
17
17
  import './time-DgsBLJA_.mjs';
18
- import './browser-C6JpWqen.mjs';
18
+ import './browser-BH7Yz53y.mjs';
19
19
  import 'open';
20
20
  import 'react';
21
21
  import 'ink';
22
- import './controlClient-CEliu-dY.mjs';
22
+ import './controlClient-hW34LqD6.mjs';
23
23
  import './projectPath-BTBIMB57.mjs';
24
24
  import 'path';
25
25
  import 'url';
@@ -2,8 +2,8 @@
2
2
 
3
3
  var promises = require('node:fs/promises');
4
4
  var socket_ioClient = require('socket.io-client');
5
- var logger = require('./persistence-ACqGUEtH.cjs');
6
- var ssh = require('./ssh-BWLfu_Bu.cjs');
5
+ var logger = require('./persistence-CRIRAcLQ.cjs');
6
+ var ssh = require('./ssh-DWCo3_QV.cjs');
7
7
  require('node:fs');
8
8
  require('node:os');
9
9
  require('node:path');
@@ -12,16 +12,16 @@ require('node:crypto');
12
12
  require('tweetnacl');
13
13
  require('chalk');
14
14
  require('fs');
15
- require('./deviceKeys-CFpY68YY.cjs');
15
+ require('./deviceKeys-Bx3Fr_eu.cjs');
16
16
  require('axios');
17
- require('./auth-DO429YIh.cjs');
17
+ require('./auth-BN2MIzje.cjs');
18
18
  require('qrcode-terminal');
19
19
  require('./time-Bj2HRkKH.cjs');
20
- require('./browser-ChToh76t.cjs');
20
+ require('./browser-B1v3MMco.cjs');
21
21
  require('open');
22
22
  require('react');
23
23
  require('ink');
24
- require('./controlClient-BAJAvvWO.cjs');
24
+ require('./controlClient-pqJuFXzr.cjs');
25
25
  require('./projectPath-ChLhdOyt.cjs');
26
26
  require('path');
27
27
  require('url');
@@ -5,18 +5,18 @@ import { createServer } from 'node:http';
5
5
  import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
6
6
  import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
7
7
  import { z } from 'zod';
8
- import { c as configuration, l as logger, r as readCredentials } from './persistence-YyHJPrEJ.mjs';
9
- import { l as listDevices, d as deviceExec } from './deviceKeys-CxID9XkJ.mjs';
8
+ import { c as configuration, l as logger, r as readCredentials } from './persistence-BcDMO5bT.mjs';
9
+ import { l as listDevices, d as deviceExec } from './deviceKeys-CkKNX4wJ.mjs';
10
10
  import axios from 'axios';
11
11
  import { stat, readFile } from 'node:fs/promises';
12
12
  import { randomUUID } from 'node:crypto';
13
13
  import { s as shouldEnableOrchestratorTools } from './prompt-BsAZsPbo.mjs';
14
14
  import './mcpToolSchemas-CjeTfrAs.mjs';
15
- import './index-DTV7drqs.mjs';
15
+ import './index-Cd3VmMtg.mjs';
16
16
  import { execSync } from 'node:child_process';
17
17
  import 'node:events';
18
18
  import 'socket.io-client';
19
- import './api-BQORFd9R.mjs';
19
+ import './api-DkLi_kVG.mjs';
20
20
  import 'tweetnacl';
21
21
  import 'ws';
22
22
  import 'crypto';
@@ -52,6 +52,14 @@ async function listBugs(credentials, input = {}) {
52
52
  );
53
53
  return response.data;
54
54
  }
55
+ async function getBug(credentials, reference) {
56
+ const bugId = await resolveBugId(credentials, reference);
57
+ const response = await axios.get(
58
+ `${configuration.serverUrl}/v1/bugs/${bugId}`,
59
+ authHeaders(credentials)
60
+ );
61
+ return response.data.bug;
62
+ }
55
63
  async function resolveBugId(credentials, reference) {
56
64
  const trimmed = reference.trim();
57
65
  const displayNumber = trimmed.replace(/^#/, "").match(/^(?:bug-)?(\d+)$/i)?.[1];
@@ -160,6 +168,36 @@ function toToolError(message, details) {
160
168
  isError: true
161
169
  };
162
170
  }
171
+ const asDate = (ms) => new Date(ms).toISOString().slice(0, 10);
172
+ function formatBugDetail(bug) {
173
+ const sections = [
174
+ `${bug.displayId} [${bug.status}] ${bug.visibility}`,
175
+ `${bug.title}`,
176
+ `filed by ${bug.createdByNickname ?? "unknown"}${bug.lastActivityAt ? `, last activity ${asDate(bug.lastActivityAt)}` : ""}`,
177
+ `
178
+ --- description ---
179
+ ${bug.description}`
180
+ ];
181
+ if (bug.attachments.length) {
182
+ sections.push(`
183
+ --- attachments (${bug.attachments.length}) ---
184
+ ${bug.attachments.map((a) => `${a.mimeType} ${a.width ?? "?"}x${a.height ?? "?"} ${a.url}`).join("\n")}`);
185
+ }
186
+ if (bug.comments.length) {
187
+ sections.push(`
188
+ --- comments (${bug.comments.length}) ---
189
+ ${bug.comments.map((c) => [
190
+ `[${asDate(c.createdAt)}] ${c.authorNickname ?? "unknown"}: ${c.body}`,
191
+ ...c.attachments.map((a) => ` + ${a.mimeType} ${a.url}`)
192
+ ].join("\n")).join("\n")}`);
193
+ }
194
+ if (bug.statusHistory.length) {
195
+ sections.push(`
196
+ --- history (${bug.statusHistory.length}) ---
197
+ ${bug.statusHistory.map((h) => `${asDate(h.createdAt)} ${h.fromStatus ? `${h.fromStatus} \u2192 ${h.toStatus}` : h.action} by ${h.actorNickname ?? "unknown"}${h.note ? ` (${h.note})` : ""}`).join("\n")}`);
198
+ }
199
+ return sections.join("\n");
200
+ }
163
201
  function createMcpServer(client, options) {
164
202
  const mcp = new McpServer({
165
203
  name: "Happy MCP",
@@ -340,12 +378,29 @@ ${lines.join("\n")}` : header }] };
340
378
  return toToolError(`Failed to list bugs: ${error instanceof Error ? error.message : String(error)}`);
341
379
  }
342
380
  });
381
+ mcp.registerTool("get_bug", {
382
+ description: "Read one bug in full from the user's Happy bug board: its description, screenshots, every comment and how it moved between states. Use when the user asks about a specific bug, or before editing one so the rewrite keeps what is still true.",
383
+ title: "Get Bug",
384
+ inputSchema: {
385
+ bug: z.string().describe('Which bug, as the user refers to it: "BUG-236", "#236" or "236". An internal bug id also works.')
386
+ }
387
+ }, async (args) => {
388
+ const credentials = await readCredentials();
389
+ if (!credentials) {
390
+ return toToolError("No Happy credentials on this machine.");
391
+ }
392
+ try {
393
+ return { content: [{ type: "text", text: formatBugDetail(await getBug(credentials, args.bug)) }] };
394
+ } catch (error) {
395
+ return toToolError(`Failed to read bug: ${error instanceof Error ? error.message : String(error)}`);
396
+ }
397
+ });
343
398
  mcp.registerTool("edit_bug", {
344
399
  description: "Change a bug already on the user's Happy bug board: rewrite its description, add screenshots to it, or both. Use when the user wants to correct, expand or illustrate an existing bug.",
345
400
  title: "Edit Bug",
346
401
  inputSchema: {
347
402
  bug: z.string().describe('Which bug, as the user refers to it: "BUG-236", "#236" or "236". An internal bug id also works.'),
348
- description: z.string().optional().describe("The full replacement description. It replaces the old one outright, so carry over anything still true. Omit to leave the description alone."),
403
+ description: z.string().optional().describe("The full replacement description. It replaces the old one outright, so carry over anything still true \u2014 including any [[bug-image:N]] markers, which is where the Nth screenshot appears inline. Drop a marker and that image moves to the end. Omit this field to leave the description alone."),
349
404
  images: z.array(z.string()).optional().describe("Absolute paths to screenshots on this machine, added to any already on the bug. JPEG and PNG only, 10 per bug in total, 20MB each.")
350
405
  }
351
406
  }, async (args) => {
@@ -385,7 +440,7 @@ ${lines.join("\n")}` : header }] };
385
440
  async function startHappyServer(client) {
386
441
  const transports = /* @__PURE__ */ new Map();
387
442
  const enableOrchestratorTools = shouldEnableOrchestratorTools();
388
- const toolNames = ["change_title", "preview_html", "device_list", "device_exec", "list_bugs", "submit_bug", "edit_bug", "delete_bug"];
443
+ const toolNames = ["change_title", "preview_html", "device_list", "device_exec", "list_bugs", "get_bug", "submit_bug", "edit_bug", "delete_bug"];
389
444
  const originalConsoleError = console.error;
390
445
  console.error = (...args) => {
391
446
  logger.debug("[happyMCP] console.error:", ...args);
@@ -7,18 +7,18 @@ var node_http = require('node:http');
7
7
  var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
8
8
  var types_js = require('@modelcontextprotocol/sdk/types.js');
9
9
  var z = require('zod');
10
- var logger = require('./persistence-ACqGUEtH.cjs');
11
- var deviceKeys = require('./deviceKeys-CFpY68YY.cjs');
10
+ var logger = require('./persistence-CRIRAcLQ.cjs');
11
+ var deviceKeys = require('./deviceKeys-Bx3Fr_eu.cjs');
12
12
  var axios = require('axios');
13
13
  var promises = require('node:fs/promises');
14
14
  var node_crypto = require('node:crypto');
15
15
  var prompt = require('./prompt-BTWDZZO0.cjs');
16
16
  require('./mcpToolSchemas-DK42yoZ_.cjs');
17
- require('./index-BeKrqPYR.cjs');
17
+ require('./index-BNMjIKud.cjs');
18
18
  var node_child_process = require('node:child_process');
19
19
  require('node:events');
20
20
  require('socket.io-client');
21
- require('./api-lxlaOAan.cjs');
21
+ require('./api-BYlwkdNv.cjs');
22
22
  require('tweetnacl');
23
23
  require('ws');
24
24
  require('crypto');
@@ -54,6 +54,14 @@ async function listBugs(credentials, input = {}) {
54
54
  );
55
55
  return response.data;
56
56
  }
57
+ async function getBug(credentials, reference) {
58
+ const bugId = await resolveBugId(credentials, reference);
59
+ const response = await axios.get(
60
+ `${logger.configuration.serverUrl}/v1/bugs/${bugId}`,
61
+ authHeaders(credentials)
62
+ );
63
+ return response.data.bug;
64
+ }
57
65
  async function resolveBugId(credentials, reference) {
58
66
  const trimmed = reference.trim();
59
67
  const displayNumber = trimmed.replace(/^#/, "").match(/^(?:bug-)?(\d+)$/i)?.[1];
@@ -162,6 +170,36 @@ function toToolError(message, details) {
162
170
  isError: true
163
171
  };
164
172
  }
173
+ const asDate = (ms) => new Date(ms).toISOString().slice(0, 10);
174
+ function formatBugDetail(bug) {
175
+ const sections = [
176
+ `${bug.displayId} [${bug.status}] ${bug.visibility}`,
177
+ `${bug.title}`,
178
+ `filed by ${bug.createdByNickname ?? "unknown"}${bug.lastActivityAt ? `, last activity ${asDate(bug.lastActivityAt)}` : ""}`,
179
+ `
180
+ --- description ---
181
+ ${bug.description}`
182
+ ];
183
+ if (bug.attachments.length) {
184
+ sections.push(`
185
+ --- attachments (${bug.attachments.length}) ---
186
+ ${bug.attachments.map((a) => `${a.mimeType} ${a.width ?? "?"}x${a.height ?? "?"} ${a.url}`).join("\n")}`);
187
+ }
188
+ if (bug.comments.length) {
189
+ sections.push(`
190
+ --- comments (${bug.comments.length}) ---
191
+ ${bug.comments.map((c) => [
192
+ `[${asDate(c.createdAt)}] ${c.authorNickname ?? "unknown"}: ${c.body}`,
193
+ ...c.attachments.map((a) => ` + ${a.mimeType} ${a.url}`)
194
+ ].join("\n")).join("\n")}`);
195
+ }
196
+ if (bug.statusHistory.length) {
197
+ sections.push(`
198
+ --- history (${bug.statusHistory.length}) ---
199
+ ${bug.statusHistory.map((h) => `${asDate(h.createdAt)} ${h.fromStatus ? `${h.fromStatus} \u2192 ${h.toStatus}` : h.action} by ${h.actorNickname ?? "unknown"}${h.note ? ` (${h.note})` : ""}`).join("\n")}`);
200
+ }
201
+ return sections.join("\n");
202
+ }
165
203
  function createMcpServer(client, options) {
166
204
  const mcp = new mcp_js.McpServer({
167
205
  name: "Happy MCP",
@@ -342,12 +380,29 @@ ${lines.join("\n")}` : header }] };
342
380
  return toToolError(`Failed to list bugs: ${error instanceof Error ? error.message : String(error)}`);
343
381
  }
344
382
  });
383
+ mcp.registerTool("get_bug", {
384
+ description: "Read one bug in full from the user's Happy bug board: its description, screenshots, every comment and how it moved between states. Use when the user asks about a specific bug, or before editing one so the rewrite keeps what is still true.",
385
+ title: "Get Bug",
386
+ inputSchema: {
387
+ bug: z.z.string().describe('Which bug, as the user refers to it: "BUG-236", "#236" or "236". An internal bug id also works.')
388
+ }
389
+ }, async (args) => {
390
+ const credentials = await logger.readCredentials();
391
+ if (!credentials) {
392
+ return toToolError("No Happy credentials on this machine.");
393
+ }
394
+ try {
395
+ return { content: [{ type: "text", text: formatBugDetail(await getBug(credentials, args.bug)) }] };
396
+ } catch (error) {
397
+ return toToolError(`Failed to read bug: ${error instanceof Error ? error.message : String(error)}`);
398
+ }
399
+ });
345
400
  mcp.registerTool("edit_bug", {
346
401
  description: "Change a bug already on the user's Happy bug board: rewrite its description, add screenshots to it, or both. Use when the user wants to correct, expand or illustrate an existing bug.",
347
402
  title: "Edit Bug",
348
403
  inputSchema: {
349
404
  bug: z.z.string().describe('Which bug, as the user refers to it: "BUG-236", "#236" or "236". An internal bug id also works.'),
350
- description: z.z.string().optional().describe("The full replacement description. It replaces the old one outright, so carry over anything still true. Omit to leave the description alone."),
405
+ description: z.z.string().optional().describe("The full replacement description. It replaces the old one outright, so carry over anything still true \u2014 including any [[bug-image:N]] markers, which is where the Nth screenshot appears inline. Drop a marker and that image moves to the end. Omit this field to leave the description alone."),
351
406
  images: z.z.array(z.z.string()).optional().describe("Absolute paths to screenshots on this machine, added to any already on the bug. JPEG and PNG only, 10 per bug in total, 20MB each.")
352
407
  }
353
408
  }, async (args) => {
@@ -387,7 +442,7 @@ ${lines.join("\n")}` : header }] };
387
442
  async function startHappyServer(client) {
388
443
  const transports = /* @__PURE__ */ new Map();
389
444
  const enableOrchestratorTools = prompt.shouldEnableOrchestratorTools();
390
- const toolNames = ["change_title", "preview_html", "device_list", "device_exec", "list_bugs", "submit_bug", "edit_bug", "delete_bug"];
445
+ const toolNames = ["change_title", "preview_html", "device_list", "device_exec", "list_bugs", "get_bug", "submit_bug", "edit_bug", "delete_bug"];
391
446
  const originalConsoleError = console.error;
392
447
  console.error = (...args) => {
393
448
  logger.logger.debug("[happyMCP] console.error:", ...args);
@@ -3,15 +3,15 @@
3
3
  var fs$1 = require('fs/promises');
4
4
  var os = require('os');
5
5
  var tmp = require('tmp');
6
- var api = require('./api-lxlaOAan.cjs');
7
- var logger = require('./persistence-ACqGUEtH.cjs');
8
- var auth = require('./auth-DO429YIh.cjs');
6
+ var api = require('./api-BYlwkdNv.cjs');
7
+ var logger = require('./persistence-CRIRAcLQ.cjs');
8
+ var auth = require('./auth-BN2MIzje.cjs');
9
9
  var child_process = require('child_process');
10
- var doctor = require('./doctor-qquYvrNu.cjs');
11
- var spawnHappyCLI = require('./spawnHappyCLI-BabByXDO.cjs');
12
- var controlClient = require('./controlClient-BAJAvvWO.cjs');
13
- var deviceKeys = require('./deviceKeys-CFpY68YY.cjs');
14
- var install = require('./install-Bm5eNojk.cjs');
10
+ var doctor = require('./doctor-pPDqy8Yd.cjs');
11
+ var spawnHappyCLI = require('./spawnHappyCLI-Bx0fWHXA.cjs');
12
+ var controlClient = require('./controlClient-pqJuFXzr.cjs');
13
+ var deviceKeys = require('./deviceKeys-Bx3Fr_eu.cjs');
14
+ var install = require('./install-DVjvG3OO.cjs');
15
15
  var fastify = require('fastify');
16
16
  var fs = require('node:fs');
17
17
  var promises = require('node:fs/promises');
@@ -1,15 +1,15 @@
1
1
  import fs from 'fs/promises';
2
2
  import os from 'os';
3
3
  import * as tmp from 'tmp';
4
- import { A as ApiClient } from './api-BQORFd9R.mjs';
5
- import { c as configuration, l as logger, p as packageJson, v as acquireDaemonLock, a as readSettings, i as isDebug, e as encodeBase64, x as writeDaemonState, m as readDaemonState, y as releaseDaemonLock, z as validateProfileForAgent, A as getProfileEnvironmentVariables } from './persistence-YyHJPrEJ.mjs';
6
- import { authAndSetupMachineIfNeeded } from './auth-DEvweBty.mjs';
4
+ import { A as ApiClient } from './api-DkLi_kVG.mjs';
5
+ import { c as configuration, l as logger, p as packageJson, v as acquireDaemonLock, a as readSettings, i as isDebug, e as encodeBase64, x as writeDaemonState, m as readDaemonState, y as releaseDaemonLock, z as validateProfileForAgent, A as getProfileEnvironmentVariables } from './persistence-BcDMO5bT.mjs';
6
+ import { authAndSetupMachineIfNeeded } from './auth-CLoQ_EpI.mjs';
7
7
  import { spawn, execSync, exec } from 'child_process';
8
- import { getEnvironmentInfo } from './doctor-CrBPDAJR.mjs';
9
- import { s as spawnHappyCLI } from './spawnHappyCLI-iEcKfItK.mjs';
10
- import { isDaemonRunningCurrentlyInstalledHappyVersion, stopDaemon, cleanupDaemonState } from './controlClient-CEliu-dY.mjs';
11
- import { a as readCachedDeviceKey, d as deviceExec } from './deviceKeys-CxID9XkJ.mjs';
12
- import { P as PLIST_LABEL } from './install-sbpQfY_l.mjs';
8
+ import { getEnvironmentInfo } from './doctor-BJLbeVEF.mjs';
9
+ import { s as spawnHappyCLI } from './spawnHappyCLI-CNM_tqcu.mjs';
10
+ import { isDaemonRunningCurrentlyInstalledHappyVersion, stopDaemon, cleanupDaemonState } from './controlClient-hW34LqD6.mjs';
11
+ import { a as readCachedDeviceKey, d as deviceExec } from './deviceKeys-CkKNX4wJ.mjs';
12
+ import { P as PLIST_LABEL } from './install-DWp3wQ7H.mjs';
13
13
  import fastify from 'fastify';
14
14
  import { createReadStream } from 'node:fs';
15
15
  import { stat } from 'node:fs/promises';
@@ -2,20 +2,20 @@ import os, { homedir } from 'node:os';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { existsSync, mkdirSync, writeFileSync, unlinkSync, readdirSync } from 'node:fs';
4
4
  import { readFile, mkdir, writeFile, stat, copyFile } from 'node:fs/promises';
5
- import { I as InvalidateSync, k as AsyncLock, d as connectionState, A as ApiClient, r as resolveToolsDir, b as startOfflineReconnection, l as findClaudeProjectId } from './api-BQORFd9R.mjs';
6
- import { l as logger, i as isDebug, c as configuration, a as readSettings, p as packageJson } from './persistence-YyHJPrEJ.mjs';
7
- import { g as getProjectPath, a as claudeLocal, E as ExitCodeError, b as claudeCheckSession, s as systemPrompt } from './claudeLocal-D7uEQO2e.mjs';
5
+ import { I as InvalidateSync, k as AsyncLock, d as connectionState, A as ApiClient, r as resolveToolsDir, b as startOfflineReconnection, l as findClaudeProjectId } from './api-DkLi_kVG.mjs';
6
+ import { l as logger, i as isDebug, c as configuration, a as readSettings, p as packageJson } from './persistence-BcDMO5bT.mjs';
7
+ import { g as getProjectPath, a as claudeLocal, E as ExitCodeError, b as claudeCheckSession, s as systemPrompt } from './claudeLocal-DwVoD2YO.mjs';
8
8
  import { R as RawJSONLinesSchema } from './types-BoJh4Qfv.mjs';
9
9
  import { join, resolve } from 'node:path';
10
10
  import { d as delay } from './time-DgsBLJA_.mjs';
11
11
  import { watch, access } from 'fs/promises';
12
12
  import { useStdout, useInput, Box, Text, render } from 'ink';
13
- import { b as parseSpecialCommand, a as MessageBuffer, M as MessageQueue2, h as hashObject } from './downloadImage-DFwUJ1-t.mjs';
13
+ import { b as parseSpecialCommand, a as MessageBuffer, M as MessageQueue2, h as hashObject } from './downloadImage-0txRA95g.mjs';
14
14
  import React, { useState, useRef, useEffect, useCallback } from 'react';
15
15
  import { execSync, spawn } from 'node:child_process';
16
16
  import { createInterface } from 'node:readline';
17
17
  import { fileURLToPath } from 'node:url';
18
- import { i as isBun } from './spawnHappyCLI-iEcKfItK.mjs';
18
+ import { i as isBun } from './spawnHappyCLI-CNM_tqcu.mjs';
19
19
  import { p as projectPath } from './projectPath-BTBIMB57.mjs';
20
20
  import 'axios';
21
21
  import 'node:events';
@@ -26,21 +26,21 @@ import 'crypto';
26
26
  import '@stablelib/base64';
27
27
  import 'expo-server-sdk';
28
28
  import 'chalk';
29
- import { a as formatMessageForClaude } from './formatImageMessage-cI_AEqo1.mjs';
29
+ import { a as formatMessageForClaude } from './formatImageMessage-H_hYAW10.mjs';
30
30
  import { isDeepStrictEqual } from 'node:util';
31
- import { i as initialMachineMetadata, s as stopCaffeinate, a as startCaffeinate } from './run-DEIpQXCf.mjs';
32
- import { getEnvironmentInfo } from './doctor-CrBPDAJR.mjs';
33
- import { notifyDaemonSessionStarted } from './controlClient-CEliu-dY.mjs';
34
- import { c as createMcpContext, r as registerKillSessionHandler, d as detectGitWorktree } from './registerKillSessionHandler-CaFN7Ti_.mjs';
31
+ import { i as initialMachineMetadata, s as stopCaffeinate, a as startCaffeinate } from './run-UpOvLJEM.mjs';
32
+ import { getEnvironmentInfo } from './doctor-BJLbeVEF.mjs';
33
+ import { notifyDaemonSessionStarted } from './controlClient-hW34LqD6.mjs';
34
+ import { c as createMcpContext, r as registerKillSessionHandler, d as detectGitWorktree } from './registerKillSessionHandler-cbV2Ljf7.mjs';
35
35
  import { createServer } from 'node:http';
36
36
  import 'zod';
37
- import './index-DTV7drqs.mjs';
37
+ import './index-Cd3VmMtg.mjs';
38
38
  import '@paralleldrive/cuid2';
39
39
  import 'child_process';
40
40
  import 'util';
41
41
  import 'path';
42
42
  import 'node:module';
43
- import './sessionKeyCache-D0Y_V05P.mjs';
43
+ import './sessionKeyCache-BuUtI1-I.mjs';
44
44
  import 'fs';
45
45
  import './prompt-BsAZsPbo.mjs';
46
46
  import './trimIdent-BAjmv_TG.mjs';
@@ -48,12 +48,12 @@ import './common-BdNgghUJ.mjs';
48
48
  import 'url';
49
49
  import 'os';
50
50
  import 'tmp';
51
- import './auth-DEvweBty.mjs';
51
+ import './auth-CLoQ_EpI.mjs';
52
52
  import 'qrcode-terminal';
53
- import './browser-C6JpWqen.mjs';
53
+ import './browser-BH7Yz53y.mjs';
54
54
  import 'open';
55
- import './deviceKeys-CxID9XkJ.mjs';
56
- import './install-sbpQfY_l.mjs';
55
+ import './deviceKeys-CkKNX4wJ.mjs';
56
+ import './install-DWp3wQ7H.mjs';
57
57
  import 'fastify';
58
58
  import 'fastify-type-provider-zod';
59
59
  import './doctor-ON7pN5G5.mjs';
@@ -4,20 +4,20 @@ var os = require('node:os');
4
4
  var node_crypto = require('node:crypto');
5
5
  var fs$1 = require('node:fs');
6
6
  var promises = require('node:fs/promises');
7
- var api = require('./api-lxlaOAan.cjs');
8
- var logger = require('./persistence-ACqGUEtH.cjs');
9
- var claudeLocal = require('./claudeLocal-BRkqm7Dv.cjs');
7
+ var api = require('./api-BYlwkdNv.cjs');
8
+ var logger = require('./persistence-CRIRAcLQ.cjs');
9
+ var claudeLocal = require('./claudeLocal-Clj5q_mB.cjs');
10
10
  var types = require('./types-DWeB7aqh.cjs');
11
11
  var path = require('node:path');
12
12
  var time = require('./time-Bj2HRkKH.cjs');
13
13
  var fs = require('fs/promises');
14
14
  var ink = require('ink');
15
- var downloadImage = require('./downloadImage-Cqy7ZDq9.cjs');
15
+ var downloadImage = require('./downloadImage-CzSm-976.cjs');
16
16
  var React = require('react');
17
17
  var node_child_process = require('node:child_process');
18
18
  var node_readline = require('node:readline');
19
19
  var node_url = require('node:url');
20
- var spawnHappyCLI = require('./spawnHappyCLI-BabByXDO.cjs');
20
+ var spawnHappyCLI = require('./spawnHappyCLI-Bx0fWHXA.cjs');
21
21
  var projectPath = require('./projectPath-ChLhdOyt.cjs');
22
22
  require('axios');
23
23
  require('node:events');
@@ -28,21 +28,21 @@ require('crypto');
28
28
  require('@stablelib/base64');
29
29
  require('expo-server-sdk');
30
30
  require('chalk');
31
- var formatImageMessage = require('./formatImageMessage-DFfcusj_.cjs');
31
+ var formatImageMessage = require('./formatImageMessage-CiGJLCW9.cjs');
32
32
  var node_util = require('node:util');
33
- var run = require('./run-Djrrraug.cjs');
34
- var doctor = require('./doctor-qquYvrNu.cjs');
35
- var controlClient = require('./controlClient-BAJAvvWO.cjs');
36
- var registerKillSessionHandler = require('./registerKillSessionHandler-BvYc-FOQ.cjs');
33
+ var run = require('./run-BvX34xMy.cjs');
34
+ var doctor = require('./doctor-pPDqy8Yd.cjs');
35
+ var controlClient = require('./controlClient-pqJuFXzr.cjs');
36
+ var registerKillSessionHandler = require('./registerKillSessionHandler-wQuDB--t.cjs');
37
37
  var node_http = require('node:http');
38
38
  require('zod');
39
- require('./index-BeKrqPYR.cjs');
39
+ require('./index-BNMjIKud.cjs');
40
40
  require('@paralleldrive/cuid2');
41
41
  require('child_process');
42
42
  require('util');
43
43
  require('path');
44
44
  require('node:module');
45
- require('./sessionKeyCache-BnR0ARqZ.cjs');
45
+ require('./sessionKeyCache-Cv83YxdE.cjs');
46
46
  require('fs');
47
47
  require('./prompt-BTWDZZO0.cjs');
48
48
  require('./trimIdent-4gvk9Wk6.cjs');
@@ -50,12 +50,12 @@ require('./common-CZvSRU4p.cjs');
50
50
  require('url');
51
51
  require('os');
52
52
  require('tmp');
53
- require('./auth-DO429YIh.cjs');
53
+ require('./auth-BN2MIzje.cjs');
54
54
  require('qrcode-terminal');
55
- require('./browser-ChToh76t.cjs');
55
+ require('./browser-B1v3MMco.cjs');
56
56
  require('open');
57
- require('./deviceKeys-CFpY68YY.cjs');
58
- require('./install-Bm5eNojk.cjs');
57
+ require('./deviceKeys-Bx3Fr_eu.cjs');
58
+ require('./install-DVjvG3OO.cjs');
59
59
  require('fastify');
60
60
  require('fastify-type-provider-zod');
61
61
  require('./doctor-C08m5Z9f.cjs');
@@ -732,7 +732,7 @@ class AbortError extends Error {
732
732
  }
733
733
  }
734
734
 
735
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-DEnJA94E.cjs', document.baseURI).href)));
735
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('runClaude-HVerV5KP.cjs', document.baseURI).href)));
736
736
  const __dirname$1 = path.join(__filename$1, "..");
737
737
  function getGlobalClaudeVersion() {
738
738
  try {