claude-threads 1.40.0 → 1.40.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/CHANGELOG.md +12 -0
- package/dist/index.js +169 -102
- package/dist/mcp/mcp-server.js +3 -1
- package/docs/CONFIGURATION.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.40.1] - 2026-10-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **`bugReports: { repo: owner/name }` files `!bug` reports in your own repository** (#577). `true` and `false` keep their meaning, and a malformed `repo` disables bug reports rather than falling back to the public default.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **Screenshots no longer go to catbox.moe** (#577). They are attached to the GitHub issue with `gh issue create --attach`, which needs `gh` 2.99 or newer and write access to the target repository. Without those the report is filed without them and says so. Attachments on a public repository stay online permanently, even if the issue is deleted, and the preview warns about that.
|
|
15
|
+
- **Nothing leaves before approval.** Screenshots used to be uploaded while the preview was being built, so a cancelled report had already sent them. The preview now also names the destination repository and whether it is public.
|
|
16
|
+
|
|
17
|
+
### Security
|
|
18
|
+
- **The `!bug` title can no longer run commands on the host.** The title went into a shell string with only double quotes escaped, so `$(...)` and backticks in a bug description were executed. `gh` is now called with an argument list.
|
|
19
|
+
|
|
8
20
|
## [1.40.0] - 2026-10-30
|
|
9
21
|
|
|
10
22
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -2345,6 +2345,10 @@ function resolveWatchesEnabled(value, fieldPath) {
|
|
|
2345
2345
|
function resolveAuditLogEnabled(value, fieldPath) {
|
|
2346
2346
|
return resolveBooleanFeature(value, fieldPath ?? "auditLog", { default: false, verb: "audit log stays off" });
|
|
2347
2347
|
}
|
|
2348
|
+
function isBugReportRepoConfig(value) {
|
|
2349
|
+
const repo = value?.repo;
|
|
2350
|
+
return typeof repo === "string" && GITHUB_REPO_PATTERN.test(repo);
|
|
2351
|
+
}
|
|
2348
2352
|
function resolveBugReportsEnabled(value, fieldPath) {
|
|
2349
2353
|
if (value === undefined)
|
|
2350
2354
|
return true;
|
|
@@ -2352,9 +2356,14 @@ function resolveBugReportsEnabled(value, fieldPath) {
|
|
|
2352
2356
|
return true;
|
|
2353
2357
|
if (value === false)
|
|
2354
2358
|
return false;
|
|
2355
|
-
|
|
2359
|
+
if (isBugReportRepoConfig(value))
|
|
2360
|
+
return true;
|
|
2361
|
+
console.warn(`Invalid ${fieldPath ?? "bugReports"} config: expected boolean or { repo: owner/name }, got ${JSON.stringify(value)} — ` + `bug reports are DISABLED (this flag fails closed: it controls data leaving your infrastructure)`);
|
|
2356
2362
|
return false;
|
|
2357
2363
|
}
|
|
2364
|
+
function resolveBugReportRepo(value) {
|
|
2365
|
+
return isBugReportRepoConfig(value) ? value.repo : DEFAULT_BUG_REPORT_REPO;
|
|
2366
|
+
}
|
|
2358
2367
|
function isRemoteMcpServer(server) {
|
|
2359
2368
|
return server.type === "http" || server.type === "sse";
|
|
2360
2369
|
}
|
|
@@ -2473,7 +2482,7 @@ function effectivePermissionMode(input) {
|
|
|
2473
2482
|
return "default";
|
|
2474
2483
|
return input.botWideMode;
|
|
2475
2484
|
}
|
|
2476
|
-
var OVERHEAD_VISIBILITY_VALUES, DEFAULT_OVERHEAD_VISIBILITY = "full", RECONNECT_POLICY_VALUES, DEFAULT_RECONNECT_POLICY = "retry", PRESENTATION_MODE_VALUES, PRESENTATION_MODE_EXPANSIONS, TURN_MARKER_VALUES, DEFAULT_TURN_MARKER, DEFAULT_TURN_MARKER_EMOJI = "checkered_flag", TURN_COMPLETE_EVENT_TYPE = "claude_threads_turn_complete", TURN_COMPLETE_PAYLOAD_VERSION = 1, TOOL_ACTIVITY_VALUES, TOOL_DETAILS_VALUES, DEFAULT_TOOL_ACTIVITY, DEFAULT_MEMORY_CONFIG, MEMORY_DISABLED, BOT_MCP_SERVER_NAME = "claude-threads-mcp", STDIO_KEYS, REMOTE_KEYS, LIMITS_DEFAULTS, MODE_INFO;
|
|
2485
|
+
var OVERHEAD_VISIBILITY_VALUES, DEFAULT_OVERHEAD_VISIBILITY = "full", RECONNECT_POLICY_VALUES, DEFAULT_RECONNECT_POLICY = "retry", PRESENTATION_MODE_VALUES, PRESENTATION_MODE_EXPANSIONS, TURN_MARKER_VALUES, DEFAULT_TURN_MARKER, DEFAULT_TURN_MARKER_EMOJI = "checkered_flag", TURN_COMPLETE_EVENT_TYPE = "claude_threads_turn_complete", TURN_COMPLETE_PAYLOAD_VERSION = 1, TOOL_ACTIVITY_VALUES, TOOL_DETAILS_VALUES, DEFAULT_TOOL_ACTIVITY, DEFAULT_MEMORY_CONFIG, MEMORY_DISABLED, DEFAULT_BUG_REPORT_REPO = "anneschuth/claude-threads", GITHUB_REPO_PATTERN, BOT_MCP_SERVER_NAME = "claude-threads-mcp", STDIO_KEYS, REMOTE_KEYS, LIMITS_DEFAULTS, MODE_INFO;
|
|
2477
2486
|
var init_types = __esm(() => {
|
|
2478
2487
|
OVERHEAD_VISIBILITY_VALUES = ["full", "minimal", "hidden"];
|
|
2479
2488
|
RECONNECT_POLICY_VALUES = ["retry", "exit"];
|
|
@@ -2503,6 +2512,7 @@ var init_types = __esm(() => {
|
|
|
2503
2512
|
channelLayer: false,
|
|
2504
2513
|
distillation: false
|
|
2505
2514
|
};
|
|
2515
|
+
GITHUB_REPO_PATTERN = /^[A-Za-z0-9][\w.-]*\/[A-Za-z0-9][\w.-]*$/;
|
|
2506
2516
|
STDIO_KEYS = new Set(["type", "command", "args", "env"]);
|
|
2507
2517
|
REMOTE_KEYS = new Set(["type", "url", "headers"]);
|
|
2508
2518
|
LIMITS_DEFAULTS = {
|
|
@@ -57530,8 +57540,8 @@ function getUpdateInfo() {
|
|
|
57530
57540
|
init_emoji();
|
|
57531
57541
|
|
|
57532
57542
|
// src/operations/bug-report/handler.ts
|
|
57533
|
-
import { execSync as execSync2 } from "child_process";
|
|
57534
|
-
import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync3 } from "fs";
|
|
57543
|
+
import { execSync as execSync2, execFileSync } from "child_process";
|
|
57544
|
+
import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync3, mkdtempSync as mkdtempSync2, rmSync } from "fs";
|
|
57535
57545
|
import { tmpdir as tmpdir4 } from "os";
|
|
57536
57546
|
import { join as join9 } from "path";
|
|
57537
57547
|
|
|
@@ -57791,58 +57801,67 @@ class Redactor {
|
|
|
57791
57801
|
}
|
|
57792
57802
|
|
|
57793
57803
|
// src/operations/bug-report/handler.ts
|
|
57804
|
+
var import_semver4 = __toESM(require_semver2(), 1);
|
|
57794
57805
|
init_version_check();
|
|
57795
57806
|
var piiRedactor = new Redactor({ aggressive: true });
|
|
57796
|
-
var
|
|
57797
|
-
|
|
57798
|
-
|
|
57799
|
-
|
|
57800
|
-
|
|
57801
|
-
|
|
57802
|
-
const response = await fetch(CATBOX_API_URL, {
|
|
57803
|
-
method: "POST",
|
|
57804
|
-
body: formData
|
|
57805
|
-
});
|
|
57806
|
-
if (!response.ok) {
|
|
57807
|
-
throw new Error(`Catbox upload failed: ${response.status} ${response.statusText}`);
|
|
57808
|
-
}
|
|
57809
|
-
const responseText = await response.text();
|
|
57810
|
-
if (responseText.startsWith("https://")) {
|
|
57811
|
-
return responseText.trim();
|
|
57812
|
-
}
|
|
57813
|
-
throw new Error(`Catbox upload failed: ${responseText}`);
|
|
57814
|
-
}
|
|
57815
|
-
async function uploadImages(files, downloadFile) {
|
|
57816
|
-
const results = [];
|
|
57807
|
+
var GH_MIN_ATTACH_VERSION = "2.99.0";
|
|
57808
|
+
var MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024;
|
|
57809
|
+
var ATTACH_PERMISSIONS = ["ADMIN", "MAINTAIN", "WRITE"];
|
|
57810
|
+
function selectScreenshots(files, target) {
|
|
57811
|
+
const screenshots = [];
|
|
57812
|
+
const skipped = [];
|
|
57817
57813
|
for (const file of files) {
|
|
57818
57814
|
if (!file.mimeType.startsWith("image/")) {
|
|
57819
|
-
|
|
57820
|
-
|
|
57821
|
-
|
|
57822
|
-
|
|
57823
|
-
});
|
|
57824
|
-
|
|
57815
|
+
skipped.push(`${file.name}: not an image`);
|
|
57816
|
+
} else if (file.size > MAX_SCREENSHOT_BYTES) {
|
|
57817
|
+
skipped.push(`${file.name}: larger than 10 MB`);
|
|
57818
|
+
} else if (!target.canAttach) {
|
|
57819
|
+
skipped.push(`${file.name}: ${target.attachBlocker}`);
|
|
57820
|
+
} else {
|
|
57821
|
+
screenshots.push(file);
|
|
57825
57822
|
}
|
|
57823
|
+
}
|
|
57824
|
+
return { screenshots, skipped };
|
|
57825
|
+
}
|
|
57826
|
+
async function saveScreenshots(files, downloadFile) {
|
|
57827
|
+
if (files.length === 0) {
|
|
57828
|
+
return { paths: [], failed: 0, cleanup: () => {} };
|
|
57829
|
+
}
|
|
57830
|
+
const dir = mkdtempSync2(join9(tmpdir4(), "bug-screenshots-"));
|
|
57831
|
+
const paths = [];
|
|
57832
|
+
let failed = 0;
|
|
57833
|
+
for (const [index, file] of files.entries()) {
|
|
57826
57834
|
try {
|
|
57835
|
+
if (!downloadFile)
|
|
57836
|
+
throw new Error("Platform cannot download files");
|
|
57827
57837
|
const buffer = await downloadFile(file.id);
|
|
57828
|
-
const
|
|
57829
|
-
|
|
57830
|
-
|
|
57831
|
-
|
|
57832
|
-
|
|
57833
|
-
|
|
57834
|
-
} catch (err) {
|
|
57835
|
-
results.push({
|
|
57836
|
-
success: false,
|
|
57837
|
-
error: err instanceof Error ? err.message : String(err),
|
|
57838
|
-
originalFile: file
|
|
57839
|
-
});
|
|
57838
|
+
const extension = (file.extension ?? file.mimeType.split("/")[1] ?? "").replace(/^\./, "").toLowerCase();
|
|
57839
|
+
const path = join9(dir, `screenshot-${index + 1}.${/^[a-z0-9]+$/.test(extension) ? extension : "png"}`);
|
|
57840
|
+
writeFileSync5(path, buffer, { mode: 384 });
|
|
57841
|
+
paths.push(path);
|
|
57842
|
+
} catch {
|
|
57843
|
+
failed++;
|
|
57840
57844
|
}
|
|
57841
57845
|
}
|
|
57842
|
-
return
|
|
57846
|
+
return {
|
|
57847
|
+
paths,
|
|
57848
|
+
failed,
|
|
57849
|
+
cleanup: () => {
|
|
57850
|
+
try {
|
|
57851
|
+
rmSync(dir, { recursive: true, force: true });
|
|
57852
|
+
} catch {}
|
|
57853
|
+
}
|
|
57854
|
+
};
|
|
57855
|
+
}
|
|
57856
|
+
function omittedScreenshotsNote(count) {
|
|
57857
|
+
return `${count} screenshot(s) were shared in the chat thread but are not attached to this issue.`;
|
|
57858
|
+
}
|
|
57859
|
+
function withOmittedScreenshots(body, count) {
|
|
57860
|
+
return count > 0 ? `${body}
|
|
57861
|
+
|
|
57862
|
+
${omittedScreenshotsNote(count)}` : body;
|
|
57843
57863
|
}
|
|
57844
57864
|
var MAX_RECENT_EVENTS = 10;
|
|
57845
|
-
var GITHUB_REPO = "anneschuth/claude-threads";
|
|
57846
57865
|
function trackEvent(session, type, summary) {
|
|
57847
57866
|
if (!session.recentEvents) {
|
|
57848
57867
|
session.recentEvents = [];
|
|
@@ -58033,18 +58052,15 @@ function formatLogEntries(entries) {
|
|
|
58033
58052
|
}).join(`
|
|
58034
58053
|
`);
|
|
58035
58054
|
}
|
|
58036
|
-
function formatIssueBody(context, userDescription,
|
|
58055
|
+
function formatIssueBody(context, userDescription, omittedScreenshots = 0) {
|
|
58037
58056
|
const sections = [];
|
|
58038
58057
|
sections.push(`## Description
|
|
58039
58058
|
|
|
58040
58059
|
${sanitizeText(userDescription)}`);
|
|
58041
|
-
if (
|
|
58042
|
-
const imageSection = imageUrls.map((url, i) => ``).join(`
|
|
58043
|
-
|
|
58044
|
-
`);
|
|
58060
|
+
if (omittedScreenshots > 0) {
|
|
58045
58061
|
sections.push(`## Screenshots
|
|
58046
58062
|
|
|
58047
|
-
${
|
|
58063
|
+
${omittedScreenshotsNote(omittedScreenshots)}`);
|
|
58048
58064
|
}
|
|
58049
58065
|
sections.push(`## Environment
|
|
58050
58066
|
|
|
@@ -58104,16 +58120,15 @@ _Reported via claude-threads bug report feature_`);
|
|
|
58104
58120
|
|
|
58105
58121
|
`);
|
|
58106
58122
|
}
|
|
58107
|
-
|
|
58108
|
-
|
|
58109
|
-
|
|
58110
|
-
|
|
58123
|
+
var runGh = (args, options) => execFileSync("gh", args, options);
|
|
58124
|
+
var GH_CHECK_OPTIONS = {
|
|
58125
|
+
encoding: "utf-8",
|
|
58126
|
+
timeout: 5000,
|
|
58127
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
58128
|
+
};
|
|
58129
|
+
function checkGitHubCli(exec = runGh) {
|
|
58111
58130
|
try {
|
|
58112
|
-
exec("
|
|
58113
|
-
encoding: "utf-8",
|
|
58114
|
-
timeout: 5000,
|
|
58115
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
58116
|
-
});
|
|
58131
|
+
exec(["--version"], GH_CHECK_OPTIONS);
|
|
58117
58132
|
} catch {
|
|
58118
58133
|
return {
|
|
58119
58134
|
installed: false,
|
|
@@ -58122,11 +58137,7 @@ function checkGitHubCli(exec = execSync2) {
|
|
|
58122
58137
|
};
|
|
58123
58138
|
}
|
|
58124
58139
|
try {
|
|
58125
|
-
exec("
|
|
58126
|
-
encoding: "utf-8",
|
|
58127
|
-
timeout: 5000,
|
|
58128
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
58129
|
-
});
|
|
58140
|
+
exec(["auth", "status"], GH_CHECK_OPTIONS);
|
|
58130
58141
|
} catch {
|
|
58131
58142
|
return {
|
|
58132
58143
|
installed: true,
|
|
@@ -58136,29 +58147,79 @@ function checkGitHubCli(exec = execSync2) {
|
|
|
58136
58147
|
}
|
|
58137
58148
|
return { installed: true, authenticated: true };
|
|
58138
58149
|
}
|
|
58139
|
-
|
|
58150
|
+
function inspectBugReportTarget(repo, exec = runGh) {
|
|
58151
|
+
let visibility;
|
|
58152
|
+
let viewerPermission;
|
|
58153
|
+
try {
|
|
58154
|
+
const info = JSON.parse(exec(["repo", "view", repo, "--json", "viewerPermission,visibility"], GH_CHECK_OPTIONS));
|
|
58155
|
+
visibility = info.visibility;
|
|
58156
|
+
viewerPermission = info.viewerPermission;
|
|
58157
|
+
} catch {
|
|
58158
|
+
return { repo, isPublic: true, canAttach: false, attachBlocker: `could not read ${repo}` };
|
|
58159
|
+
}
|
|
58160
|
+
const isPublic = visibility !== "PRIVATE" && visibility !== "INTERNAL";
|
|
58161
|
+
if (viewerPermission && !ATTACH_PERMISSIONS.includes(viewerPermission)) {
|
|
58162
|
+
return { repo, isPublic, canAttach: false, attachBlocker: `attaching needs write access to ${repo}` };
|
|
58163
|
+
}
|
|
58164
|
+
let ghVersion = null;
|
|
58165
|
+
try {
|
|
58166
|
+
ghVersion = import_semver4.coerce(exec(["--version"], GH_CHECK_OPTIONS));
|
|
58167
|
+
} catch {}
|
|
58168
|
+
if (ghVersion && import_semver4.lt(ghVersion, GH_MIN_ATTACH_VERSION)) {
|
|
58169
|
+
return { repo, isPublic, canAttach: false, attachBlocker: `attaching needs gh ${GH_MIN_ATTACH_VERSION} or newer` };
|
|
58170
|
+
}
|
|
58171
|
+
return { repo, isPublic, canAttach: true };
|
|
58172
|
+
}
|
|
58173
|
+
async function createGitHubIssue(title, body, workingDir, options) {
|
|
58174
|
+
const { repo, attachments = [], exec = runGh } = options;
|
|
58140
58175
|
const ghStatus = checkGitHubCli(exec);
|
|
58141
58176
|
if (!ghStatus.installed || !ghStatus.authenticated) {
|
|
58142
58177
|
throw new Error(ghStatus.error);
|
|
58143
58178
|
}
|
|
58144
58179
|
const bodyFile = join9(tmpdir4(), `bug-body-${Date.now()}.md`);
|
|
58145
|
-
|
|
58146
|
-
writeFileSync5(bodyFile,
|
|
58147
|
-
|
|
58148
|
-
|
|
58180
|
+
const create = (issueBody, paths) => {
|
|
58181
|
+
writeFileSync5(bodyFile, issueBody, "utf-8");
|
|
58182
|
+
return exec([
|
|
58183
|
+
"issue",
|
|
58184
|
+
"create",
|
|
58185
|
+
"--repo",
|
|
58186
|
+
repo,
|
|
58187
|
+
"--title",
|
|
58188
|
+
title,
|
|
58189
|
+
"--body-file",
|
|
58190
|
+
bodyFile,
|
|
58191
|
+
...paths.flatMap((path, index) => ["--attach", `${path}#Screenshot ${index + 1}`])
|
|
58192
|
+
], {
|
|
58149
58193
|
cwd: workingDir,
|
|
58150
58194
|
encoding: "utf-8",
|
|
58151
|
-
timeout: 30000,
|
|
58195
|
+
timeout: paths.length > 0 ? 120000 : 30000,
|
|
58152
58196
|
stdio: ["pipe", "pipe", "pipe"]
|
|
58153
|
-
});
|
|
58154
|
-
|
|
58197
|
+
}).trim();
|
|
58198
|
+
};
|
|
58199
|
+
try {
|
|
58200
|
+
try {
|
|
58201
|
+
return { url: create(body, attachments), attachmentsIncomplete: false };
|
|
58202
|
+
} catch (err) {
|
|
58203
|
+
if (attachments.length === 0)
|
|
58204
|
+
throw err;
|
|
58205
|
+
const failure = err;
|
|
58206
|
+
const url = String(failure.stdout ?? "").match(/https:\/\/\S+\/issues\/\d+/)?.[0];
|
|
58207
|
+
if (url)
|
|
58208
|
+
return { url, attachmentsIncomplete: true };
|
|
58209
|
+
if (failure.code === "ETIMEDOUT")
|
|
58210
|
+
throw err;
|
|
58211
|
+
return {
|
|
58212
|
+
url: create(withOmittedScreenshots(body, attachments.length), []),
|
|
58213
|
+
attachmentsIncomplete: true
|
|
58214
|
+
};
|
|
58215
|
+
}
|
|
58155
58216
|
} finally {
|
|
58156
58217
|
try {
|
|
58157
58218
|
unlinkSync3(bodyFile);
|
|
58158
58219
|
} catch {}
|
|
58159
58220
|
}
|
|
58160
58221
|
}
|
|
58161
|
-
function formatBugPreview(title, description, context,
|
|
58222
|
+
function formatBugPreview(title, description, context, target, screenshotCount, skippedScreenshots, formatter) {
|
|
58162
58223
|
const lines = [];
|
|
58163
58224
|
lines.push(`${formatter.formatBold("Bug Report Preview")}`);
|
|
58164
58225
|
lines.push("");
|
|
@@ -58185,14 +58246,20 @@ function formatBugPreview(title, description, context, imageUrls, imageErrors, f
|
|
|
58185
58246
|
lines.push(formatter.formatListItem(`${event.type}: ${event.summary.substring(0, 40)}...`));
|
|
58186
58247
|
}
|
|
58187
58248
|
}
|
|
58188
|
-
|
|
58249
|
+
lines.push("");
|
|
58250
|
+
lines.push(formatter.formatBold("Destination:"));
|
|
58251
|
+
lines.push(formatter.formatListItem(`Issue on ${formatter.formatCode(target.repo)}${target.isPublic ? " (public: anyone can read it)" : ""}`));
|
|
58252
|
+
if (screenshotCount > 0 || skippedScreenshots.length > 0) {
|
|
58189
58253
|
lines.push("");
|
|
58190
58254
|
lines.push(formatter.formatBold("Screenshots:"));
|
|
58191
|
-
if (
|
|
58192
|
-
lines.push(formatter.formatListItem(
|
|
58255
|
+
if (screenshotCount > 0) {
|
|
58256
|
+
lines.push(formatter.formatListItem(`\uD83D\uDCCE ${screenshotCount} image(s) will be attached when you approve`));
|
|
58257
|
+
if (target.isPublic) {
|
|
58258
|
+
lines.push(formatter.formatListItem("⚠️ Attachments on a public repository stay online permanently, even if the issue is deleted. Check them for personal data first."));
|
|
58259
|
+
}
|
|
58193
58260
|
}
|
|
58194
|
-
if (
|
|
58195
|
-
lines.push(formatter.formatListItem(`⚠️ ${
|
|
58261
|
+
if (skippedScreenshots.length > 0) {
|
|
58262
|
+
lines.push(formatter.formatListItem(`⚠️ ${skippedScreenshots.length} image(s) not attached (${skippedScreenshots[0]}). Add them to the issue by hand if needed.`));
|
|
58196
58263
|
}
|
|
58197
58264
|
}
|
|
58198
58265
|
lines.push("");
|
|
@@ -67420,7 +67487,7 @@ async function deferUpdate(session, username, updateManager) {
|
|
|
67420
67487
|
await post(session, "success", `⏸️ ${formatter.formatBold("Update deferred")} for 1 hour
|
|
67421
67488
|
` + formatter.formatItalic("Use !update now to apply earlier"));
|
|
67422
67489
|
}
|
|
67423
|
-
async function reportBug(session, description, username, ctx, errorContext, attachedFiles) {
|
|
67490
|
+
async function reportBug(session, description, username, ctx, errorContext, attachedFiles, exec) {
|
|
67424
67491
|
const formatter = session.platform.getFormatter();
|
|
67425
67492
|
if (!ctx.config.bugReportsEnabled) {
|
|
67426
67493
|
await post(session, "info", `${formatter.formatBold("Bug reporting is disabled")} by this server's configuration ` + `(${formatter.formatCode("bugReports: false")}).
|
|
@@ -67435,25 +67502,18 @@ async function reportBug(session, description, username, ctx, errorContext, atta
|
|
|
67435
67502
|
` + `Or react with \uD83D\uDC1B on any error message to report it.`);
|
|
67436
67503
|
return;
|
|
67437
67504
|
}
|
|
67438
|
-
const ghStatus = checkGitHubCli();
|
|
67505
|
+
const ghStatus = checkGitHubCli(exec);
|
|
67439
67506
|
if (!ghStatus.installed || !ghStatus.authenticated) {
|
|
67440
67507
|
await postError(session, ghStatus.error || "GitHub CLI not configured");
|
|
67441
67508
|
return;
|
|
67442
67509
|
}
|
|
67443
67510
|
const bugDescription = description || (errorContext ? `Error: ${errorContext.message.substring(0, 200)}` : "Unknown error");
|
|
67444
67511
|
const context = await collectBugReportContext(session, errorContext);
|
|
67445
|
-
|
|
67446
|
-
|
|
67447
|
-
const downloadFile = session.platform.downloadFile?.bind(session.platform);
|
|
67448
|
-
if (attachedFiles && attachedFiles.length > 0 && downloadFile) {
|
|
67449
|
-
await post(session, "info", `\uD83D\uDCE4 Uploading ${attachedFiles.length} image(s)...`);
|
|
67450
|
-
const uploadResults = await uploadImages(attachedFiles, downloadFile);
|
|
67451
|
-
imageUrls = uploadResults.filter((r) => r.success && typeof r.url === "string").map((r) => r.url);
|
|
67452
|
-
imageErrors = uploadResults.filter((r) => !r.success).map((r) => `${r.originalFile.name}: ${r.error}`);
|
|
67453
|
-
}
|
|
67512
|
+
const target = inspectBugReportTarget(ctx.config.bugReportRepo, exec);
|
|
67513
|
+
const { screenshots, skipped } = selectScreenshots(attachedFiles ?? [], target);
|
|
67454
67514
|
const title = generateIssueTitle(bugDescription);
|
|
67455
|
-
const body = formatIssueBody(context, bugDescription,
|
|
67456
|
-
const preview = formatBugPreview(title, bugDescription, context,
|
|
67515
|
+
const body = formatIssueBody(context, bugDescription, skipped.length);
|
|
67516
|
+
const preview = formatBugPreview(title, bugDescription, context, target, screenshots.length, skipped, formatter);
|
|
67457
67517
|
const previewMessage = `\uD83D\uDC1B ${preview}`;
|
|
67458
67518
|
const bugReportPost = await postInteractiveAndRegister(session, previewMessage, [APPROVAL_EMOJIS[0], DENIAL_EMOJIS[0]], ctx.ops.registerPost);
|
|
67459
67519
|
session.messageManager?.setPendingBugReport({
|
|
@@ -67461,8 +67521,7 @@ async function reportBug(session, description, username, ctx, errorContext, atta
|
|
|
67461
67521
|
title,
|
|
67462
67522
|
body,
|
|
67463
67523
|
userDescription: bugDescription,
|
|
67464
|
-
|
|
67465
|
-
imageErrors,
|
|
67524
|
+
screenshots,
|
|
67466
67525
|
errorContext
|
|
67467
67526
|
});
|
|
67468
67527
|
sessionLog8(session).info(`\uD83D\uDC1B Bug report preview created by @${username}: ${title}`);
|
|
@@ -67476,14 +67535,19 @@ async function handleBugReportApproval(session, isApproved, username, ctx, exec)
|
|
|
67476
67535
|
}
|
|
67477
67536
|
const formatter = session.platform.getFormatter();
|
|
67478
67537
|
if (isApproved) {
|
|
67538
|
+
const saved = await saveScreenshots(pending.screenshots ?? [], session.platform.downloadFile?.bind(session.platform));
|
|
67479
67539
|
try {
|
|
67480
|
-
const
|
|
67481
|
-
|
|
67482
|
-
|
|
67540
|
+
const issue = await createGitHubIssue(pending.title, withOmittedScreenshots(pending.body, saved.failed), session.workingDir, { repo: ctx.config.bugReportRepo, attachments: saved.paths, exec });
|
|
67541
|
+
const incomplete = issue.attachmentsIncomplete || saved.failed > 0;
|
|
67542
|
+
await updatePostSuccess(session, pending.postId, `${formatter.formatBold("Bug report submitted")}: ${issue.url}` + (incomplete ? `
|
|
67543
|
+
⚠️ Not every screenshot could be attached. Add the missing ones to the issue by hand.` : ""));
|
|
67544
|
+
sessionLog8(session).info(`\uD83D\uDC1B Bug report created by @${username}: ${issue.url}`);
|
|
67483
67545
|
} catch (err) {
|
|
67484
67546
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
67485
67547
|
await updatePostError(session, pending.postId, `${formatter.formatBold("Failed to create bug report")}: ${errorMessage}`);
|
|
67486
67548
|
sessionLog8(session).error(`Failed to create bug report: ${errorMessage}`);
|
|
67549
|
+
} finally {
|
|
67550
|
+
saved.cleanup();
|
|
67487
67551
|
}
|
|
67488
67552
|
} else {
|
|
67489
67553
|
await updatePostCancelled(session, pending.postId, `${formatter.formatBold("Bug report cancelled")} by ${formatter.formatUserMention(username)}`);
|
|
@@ -73915,6 +73979,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
73915
73979
|
userAttribution;
|
|
73916
73980
|
threadLogsEnabled;
|
|
73917
73981
|
bugReportsEnabled;
|
|
73982
|
+
bugReportRepo;
|
|
73918
73983
|
usageShowEmails;
|
|
73919
73984
|
threadLogsRetentionDays;
|
|
73920
73985
|
limits;
|
|
@@ -73944,7 +74009,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
73944
74009
|
connectorsOff = null;
|
|
73945
74010
|
usageRefreshInFlight = null;
|
|
73946
74011
|
usageRefreshedAt = 0;
|
|
73947
|
-
constructor(workingDir, permissionModeOrSkipFlag = "default", chromeEnabled = false, worktreeMode = "prompt", sessionsPath, threadLogsEnabled = true, threadLogsRetentionDays = 30, limits, claudeAccounts, respondOnlyWhenMentioned = false, userAttribution = true, bugReportsEnabled = true, usage) {
|
|
74012
|
+
constructor(workingDir, permissionModeOrSkipFlag = "default", chromeEnabled = false, worktreeMode = "prompt", sessionsPath, threadLogsEnabled = true, threadLogsRetentionDays = 30, limits, claudeAccounts, respondOnlyWhenMentioned = false, userAttribution = true, bugReportsEnabled = true, usage, bugReportRepo = DEFAULT_BUG_REPORT_REPO) {
|
|
73948
74013
|
super();
|
|
73949
74014
|
this.workingDir = workingDir;
|
|
73950
74015
|
this.permissionMode = typeof permissionModeOrSkipFlag === "boolean" ? permissionModeOrSkipFlag ? "bypass" : "default" : permissionModeOrSkipFlag;
|
|
@@ -73953,6 +74018,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
73953
74018
|
this.respondOnlyWhenMentioned = respondOnlyWhenMentioned;
|
|
73954
74019
|
this.userAttribution = userAttribution;
|
|
73955
74020
|
this.bugReportsEnabled = bugReportsEnabled;
|
|
74021
|
+
this.bugReportRepo = bugReportRepo;
|
|
73956
74022
|
this.threadLogsEnabled = threadLogsEnabled;
|
|
73957
74023
|
this.threadLogsRetentionDays = threadLogsRetentionDays;
|
|
73958
74024
|
this.limits = resolveLimits(limits);
|
|
@@ -74097,7 +74163,8 @@ class SessionManager extends EventEmitter4 {
|
|
|
74097
74163
|
threadLogsRetentionDays: this.threadLogsRetentionDays,
|
|
74098
74164
|
permissionTimeoutMs: this.limits.permissionTimeoutSeconds * 1000,
|
|
74099
74165
|
flushDelayMs: this.limits.flushDelayMs,
|
|
74100
|
-
bugReportsEnabled: this.bugReportsEnabled
|
|
74166
|
+
bugReportsEnabled: this.bugReportsEnabled,
|
|
74167
|
+
bugReportRepo: this.bugReportRepo
|
|
74101
74168
|
};
|
|
74102
74169
|
const state = {
|
|
74103
74170
|
sessions: this.registry.getSessions(),
|
|
@@ -77817,12 +77884,12 @@ function wrapAnsi2(string, columns, options) {
|
|
|
77817
77884
|
|
|
77818
77885
|
// node_modules/terminal-size/index.js
|
|
77819
77886
|
import process12 from "node:process";
|
|
77820
|
-
import { execFileSync } from "node:child_process";
|
|
77887
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
77821
77888
|
import fs6 from "node:fs";
|
|
77822
77889
|
import tty2 from "node:tty";
|
|
77823
77890
|
var defaultColumns = 80;
|
|
77824
77891
|
var defaultRows = 24;
|
|
77825
|
-
var exec5 = (command, arguments_, { shell, env } = {}) =>
|
|
77892
|
+
var exec5 = (command, arguments_, { shell, env } = {}) => execFileSync2(command, arguments_, {
|
|
77826
77893
|
encoding: "utf8",
|
|
77827
77894
|
stdio: ["ignore", "pipe", "ignore"],
|
|
77828
77895
|
timeout: 500,
|
|
@@ -91137,7 +91204,7 @@ async function startWithoutDaemon() {
|
|
|
91137
91204
|
const threadLogsRetentionDays = config.threadLogs?.retentionDays ?? 30;
|
|
91138
91205
|
const bugReportsEnabled = resolveBugReportsEnabled(config.bugReports);
|
|
91139
91206
|
configureBugReports(bugReportsEnabled);
|
|
91140
|
-
const session = new SessionManager(workingDir, initialPermissionMode, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits, config.claudeAccounts, config.respondOnlyWhenMentioned, config.userAttribution, bugReportsEnabled, config.usage);
|
|
91207
|
+
const session = new SessionManager(workingDir, initialPermissionMode, config.chrome, config.worktreeMode, undefined, threadLogsEnabled, threadLogsRetentionDays, config.limits, config.claudeAccounts, config.respondOnlyWhenMentioned, config.userAttribution, bugReportsEnabled, config.usage, resolveBugReportRepo(config.bugReports));
|
|
91141
91208
|
if (config.stickyMessage) {
|
|
91142
91209
|
session.setStickyMessageCustomization(config.stickyMessage.description, config.stickyMessage.footer);
|
|
91143
91210
|
}
|
package/dist/mcp/mcp-server.js
CHANGED
|
@@ -38835,6 +38835,7 @@ class Redactor {
|
|
|
38835
38835
|
}
|
|
38836
38836
|
|
|
38837
38837
|
// src/operations/bug-report/handler.ts
|
|
38838
|
+
var import_semver2 = __toESM(require_semver2(), 1);
|
|
38838
38839
|
init_version_check();
|
|
38839
38840
|
|
|
38840
38841
|
// src/persistence/thread-logger.ts
|
|
@@ -38845,6 +38846,7 @@ var LOGS_BASE_DIR = join4(stateHome(), ".claude-threads", "logs");
|
|
|
38845
38846
|
|
|
38846
38847
|
// src/operations/bug-report/handler.ts
|
|
38847
38848
|
var piiRedactor = new Redactor({ aggressive: true });
|
|
38849
|
+
var MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024;
|
|
38848
38850
|
// src/mcp/decision-bridge.ts
|
|
38849
38851
|
import { createServer, createConnection } from "node:net";
|
|
38850
38852
|
var MAX_BRIDGE_REQUEST_BYTES = 1024 * 1024;
|
|
@@ -39812,7 +39814,7 @@ var CHAT_PLATFORM_PROMPT = generateChatPlatformPrompt();
|
|
|
39812
39814
|
init_cli();
|
|
39813
39815
|
|
|
39814
39816
|
// src/update-notifier.ts
|
|
39815
|
-
var
|
|
39817
|
+
var import_semver3 = __toESM(require_semver2(), 1);
|
|
39816
39818
|
|
|
39817
39819
|
// src/operations/commands/handler.ts
|
|
39818
39820
|
init_emoji();
|
package/docs/CONFIGURATION.md
CHANGED
|
@@ -54,7 +54,7 @@ platforms:
|
|
|
54
54
|
| `respondOnlyWhenMentioned` | Start new threads in quiet mode, where the bot only replies to messages that @mention it. Users can still toggle per-thread with `!mentions`. | `false` |
|
|
55
55
|
| `userAttribution` | Prefix each user turn sent to Claude with the sender's `[@username]:` so Claude can tell who is speaking in multi-user threads. Only applied once a thread has more than one participant (after `!invite`); solo threads are left untouched. Set `false` to disable. Applies to new sessions. | `true` |
|
|
56
56
|
| `keepAlive` | Prevent system sleep while sessions are active | `true` |
|
|
57
|
-
| `bugReports` | Whether `!bug` may file a report. ⚠️ A bug report leaves your infrastructure:
|
|
57
|
+
| `bugReports` | Whether `!bug` may file a report, and where. ⚠️ A bug report leaves your infrastructure: the report body — session context plus recent daemon log lines — is filed as an issue on the project's public GitHub repository, behind best-effort redaction, unless you set `bugReports: { repo: owner/name }` to file in your own repository. Attached screenshots go along with the issue once someone approves the preview; this needs `gh` 2.99 or newer and write access to the target repository, and without those the report is filed without them. An attachment on a public repository stays online permanently, even if the issue is deleted. Claude can also invoke `!bug` itself, but a person still has to approve the preview. Set `false` and the whole path is removed: the command is refused, Claude cannot invoke it either, the 🐛 error reaction does nothing, the approval that would file the issue is refused, and `!bug` disappears from `!help`. **Fails closed** — a malformed value disables it rather than defaulting to on. | `true` |
|
|
58
58
|
| `limits` | Resource limits and timeouts (see below) | see below |
|
|
59
59
|
| `threadLogs` | Thread logging (see below) | enabled |
|
|
60
60
|
| `stickyMessage` | Sticky message text customization (see below) | none |
|
package/package.json
CHANGED