claude-threads 0.49.0 → 0.51.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/CHANGELOG.md +15 -0
- package/README.md +4 -0
- package/dist/index.js +553 -73
- package/dist/mcp/permission-server.js +4 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.51.0] - 2026-01-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Image upload for bug reports** - Bug reports can now include screenshots uploaded to Catbox.moe. Use `!bug <description>` with an attached image or paste a screenshot (#153)
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- **Duplicate task lists** - Fixed issue where multiple task lists would appear in threads due to race conditions (#152, #151)
|
|
17
|
+
- **Code block rendering** - Fixed issues with code blocks not rendering correctly, including improved handling of language tags and empty blocks (#154)
|
|
18
|
+
- **Website logo rendering** - Improved SVG logo rendering on the project website (#155)
|
|
19
|
+
|
|
20
|
+
## [0.50.0] - 2026-01-09
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- **Bug reporting feature** - Users can now report bugs with `!bug <description>` command. The bot collects recent conversation context, session state, and system info into a markdown report posted as a file attachment (#150)
|
|
24
|
+
|
|
10
25
|
## [0.49.0] - 2026-01-09
|
|
11
26
|
|
|
12
27
|
### Added
|
package/README.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
✴ ▀█▄ █ ✴
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://claude-threads.run"><strong>claude-threads.run</strong></a>
|
|
11
|
+
</p>
|
|
12
|
+
|
|
9
13
|
[](https://www.npmjs.com/package/claude-threads)
|
|
10
14
|
[](https://www.npmjs.com/package/claude-threads)
|
|
11
15
|
[](https://github.com/anneschuth/claude-threads/actions/workflows/ci.yml)
|
package/dist/index.js
CHANGED
|
@@ -42763,9 +42763,11 @@ ${code}
|
|
|
42763
42763
|
`);
|
|
42764
42764
|
}
|
|
42765
42765
|
formatMarkdown(content) {
|
|
42766
|
-
|
|
42766
|
+
let processed = content.replace(/(?<=\n)```(?=\S)(?![a-zA-Z]*\n)/g, "```\n");
|
|
42767
|
+
processed = processed.replace(/\n{3,}/g, `
|
|
42767
42768
|
|
|
42768
42769
|
`);
|
|
42770
|
+
return processed;
|
|
42769
42771
|
}
|
|
42770
42772
|
}
|
|
42771
42773
|
|
|
@@ -43407,6 +43409,7 @@ function convertMarkdownToSlack(content) {
|
|
|
43407
43409
|
for (let i = 0;i < codeBlocks.length; i++) {
|
|
43408
43410
|
preserved = preserved.replace(`${CODE_BLOCK_PLACEHOLDER}${i}\x00`, codeBlocks[i]);
|
|
43409
43411
|
}
|
|
43412
|
+
preserved = preserved.replace(/(?<=\n)```(?=\S)(?![a-zA-Z]*\n)/g, "```\n");
|
|
43410
43413
|
return preserved;
|
|
43411
43414
|
}
|
|
43412
43415
|
function convertMarkdownTablesToSlack(content) {
|
|
@@ -44639,7 +44642,7 @@ class SlackPermissionApi {
|
|
|
44639
44642
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
44640
44643
|
import { existsSync as existsSync9 } from "fs";
|
|
44641
44644
|
import { readdir, rm } from "fs/promises";
|
|
44642
|
-
import { join as
|
|
44645
|
+
import { join as join5 } from "path";
|
|
44643
44646
|
|
|
44644
44647
|
// src/persistence/session-store.ts
|
|
44645
44648
|
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync } from "fs";
|
|
@@ -44894,6 +44897,7 @@ var CANCEL_EMOJIS = ["x", "octagonal_sign", "stop_sign", "stop"];
|
|
|
44894
44897
|
var ESCAPE_EMOJIS = ["double_vertical_bar", "pause_button", "pause"];
|
|
44895
44898
|
var RESUME_EMOJIS = ["arrows_counterclockwise", "arrow_forward", "repeat"];
|
|
44896
44899
|
var TASK_TOGGLE_EMOJIS = ["arrow_down_small", "small_red_triangle_down"];
|
|
44900
|
+
var BUG_REPORT_EMOJI = "bug";
|
|
44897
44901
|
function isApprovalEmoji(emoji) {
|
|
44898
44902
|
return APPROVAL_EMOJIS.includes(emoji);
|
|
44899
44903
|
}
|
|
@@ -44915,6 +44919,9 @@ function isResumeEmoji(emoji) {
|
|
|
44915
44919
|
function isTaskToggleEmoji(emoji) {
|
|
44916
44920
|
return TASK_TOGGLE_EMOJIS.includes(emoji);
|
|
44917
44921
|
}
|
|
44922
|
+
function isBugReportEmoji(emoji) {
|
|
44923
|
+
return emoji === BUG_REPORT_EMOJI || emoji === "\uD83D\uDC1B";
|
|
44924
|
+
}
|
|
44918
44925
|
var UNICODE_NUMBER_EMOJIS = {
|
|
44919
44926
|
"1\uFE0F\u20E3": 0,
|
|
44920
44927
|
"2\uFE0F\u20E3": 1,
|
|
@@ -45284,8 +45291,19 @@ async function postSuccess(session, message) {
|
|
|
45284
45291
|
async function postWarning(session, message) {
|
|
45285
45292
|
return createPostAndTrack(session, `\u26A0\uFE0F ${message}`);
|
|
45286
45293
|
}
|
|
45287
|
-
async function postError(session, message) {
|
|
45288
|
-
|
|
45294
|
+
async function postError(session, message, addBugReaction = true) {
|
|
45295
|
+
const post = await createPostAndTrack(session, `\u274C ${message}`);
|
|
45296
|
+
if (addBugReaction) {
|
|
45297
|
+
try {
|
|
45298
|
+
await session.platform.addReaction(post.id, BUG_REPORT_EMOJI);
|
|
45299
|
+
session.lastError = {
|
|
45300
|
+
postId: post.id,
|
|
45301
|
+
message,
|
|
45302
|
+
timestamp: new Date
|
|
45303
|
+
};
|
|
45304
|
+
} catch {}
|
|
45305
|
+
}
|
|
45306
|
+
return post;
|
|
45289
45307
|
}
|
|
45290
45308
|
async function postSecure(session, message) {
|
|
45291
45309
|
return createPostAndTrack(session, `\uD83D\uDD10 ${message}`);
|
|
@@ -46388,14 +46406,15 @@ function formatToolUse(toolName, input, formatter, options = {}) {
|
|
|
46388
46406
|
for (const line of lines) {
|
|
46389
46407
|
if (lineCount >= maxLines)
|
|
46390
46408
|
break;
|
|
46409
|
+
const escapedLine = line.replace(/```/g, "` ``");
|
|
46391
46410
|
if (change.added) {
|
|
46392
|
-
diffLines2.push(`+ ${
|
|
46411
|
+
diffLines2.push(`+ ${escapedLine}`);
|
|
46393
46412
|
lineCount++;
|
|
46394
46413
|
} else if (change.removed) {
|
|
46395
|
-
diffLines2.push(`- ${
|
|
46414
|
+
diffLines2.push(`- ${escapedLine}`);
|
|
46396
46415
|
lineCount++;
|
|
46397
46416
|
} else {
|
|
46398
|
-
diffLines2.push(` ${
|
|
46417
|
+
diffLines2.push(` ${escapedLine}`);
|
|
46399
46418
|
lineCount++;
|
|
46400
46419
|
}
|
|
46401
46420
|
}
|
|
@@ -46426,7 +46445,7 @@ ${formatter.formatCodeBlock(diffLines2.join(`
|
|
|
46426
46445
|
const lineCount = lines.length;
|
|
46427
46446
|
if (detailed && content && lineCount > 0) {
|
|
46428
46447
|
const maxLines = 6;
|
|
46429
|
-
const previewLines = lines.slice(0, maxLines);
|
|
46448
|
+
const previewLines = lines.slice(0, maxLines).map((line) => line.replace(/```/g, "` ``"));
|
|
46430
46449
|
let preview = `\uD83D\uDCDD ${formatter.formatBold("Write")} ${formatter.formatCode(filePath)} ${formatter.formatItalic(`(${lineCount} lines)`)}
|
|
46431
46450
|
`;
|
|
46432
46451
|
if (lineCount > maxLines) {
|
|
@@ -50488,6 +50507,353 @@ function getLogo(version) {
|
|
|
50488
50507
|
\`\`\``;
|
|
50489
50508
|
}
|
|
50490
50509
|
|
|
50510
|
+
// src/session/bug-report.ts
|
|
50511
|
+
import { execSync as execSync2 } from "child_process";
|
|
50512
|
+
import { writeFileSync as writeFileSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
50513
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
50514
|
+
import { join as join4 } from "path";
|
|
50515
|
+
|
|
50516
|
+
// src/claude/version-check.ts
|
|
50517
|
+
var import_semver3 = __toESM(require_semver2(), 1);
|
|
50518
|
+
import { execSync } from "child_process";
|
|
50519
|
+
var CLAUDE_CLI_VERSION_RANGE = ">=2.0.74 <2.2.0";
|
|
50520
|
+
function getClaudeCliVersion() {
|
|
50521
|
+
const claudePath = process.env.CLAUDE_PATH || "claude";
|
|
50522
|
+
try {
|
|
50523
|
+
const output = execSync(`${claudePath} --version`, {
|
|
50524
|
+
encoding: "utf8",
|
|
50525
|
+
timeout: 5000,
|
|
50526
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
50527
|
+
}).trim();
|
|
50528
|
+
const match = output.match(/^([\d.]+)/);
|
|
50529
|
+
return match ? match[1] : null;
|
|
50530
|
+
} catch {
|
|
50531
|
+
return null;
|
|
50532
|
+
}
|
|
50533
|
+
}
|
|
50534
|
+
function isVersionCompatible(version) {
|
|
50535
|
+
const semverVersion = import_semver3.coerce(version);
|
|
50536
|
+
if (!semverVersion)
|
|
50537
|
+
return false;
|
|
50538
|
+
return import_semver3.satisfies(semverVersion, CLAUDE_CLI_VERSION_RANGE);
|
|
50539
|
+
}
|
|
50540
|
+
function validateClaudeCli() {
|
|
50541
|
+
const version = getClaudeCliVersion();
|
|
50542
|
+
if (!version) {
|
|
50543
|
+
return {
|
|
50544
|
+
installed: false,
|
|
50545
|
+
version: null,
|
|
50546
|
+
compatible: false,
|
|
50547
|
+
message: "Claude CLI not found. Install it with: bun install -g @anthropic-ai/claude-code"
|
|
50548
|
+
};
|
|
50549
|
+
}
|
|
50550
|
+
const compatible = isVersionCompatible(version);
|
|
50551
|
+
if (!compatible) {
|
|
50552
|
+
return {
|
|
50553
|
+
installed: true,
|
|
50554
|
+
version,
|
|
50555
|
+
compatible: false,
|
|
50556
|
+
message: `Claude CLI version ${version} is not compatible. Required: ${CLAUDE_CLI_VERSION_RANGE}
|
|
50557
|
+
` + `Install a compatible version: bun install -g @anthropic-ai/claude-code@2.0.76`
|
|
50558
|
+
};
|
|
50559
|
+
}
|
|
50560
|
+
return {
|
|
50561
|
+
installed: true,
|
|
50562
|
+
version,
|
|
50563
|
+
compatible: true,
|
|
50564
|
+
message: `Claude CLI ${version} \u2713`
|
|
50565
|
+
};
|
|
50566
|
+
}
|
|
50567
|
+
|
|
50568
|
+
// src/session/bug-report.ts
|
|
50569
|
+
var CATBOX_API_URL = "https://catbox.moe/user/api.php";
|
|
50570
|
+
async function uploadImageToCatbox(imageBuffer, filename) {
|
|
50571
|
+
const arrayBuffer = imageBuffer.buffer.slice(imageBuffer.byteOffset, imageBuffer.byteOffset + imageBuffer.byteLength);
|
|
50572
|
+
const formData = new FormData;
|
|
50573
|
+
formData.append("reqtype", "fileupload");
|
|
50574
|
+
formData.append("fileToUpload", new Blob([arrayBuffer]), filename);
|
|
50575
|
+
const response = await fetch(CATBOX_API_URL, {
|
|
50576
|
+
method: "POST",
|
|
50577
|
+
body: formData
|
|
50578
|
+
});
|
|
50579
|
+
if (!response.ok) {
|
|
50580
|
+
throw new Error(`Catbox upload failed: ${response.status} ${response.statusText}`);
|
|
50581
|
+
}
|
|
50582
|
+
const responseText = await response.text();
|
|
50583
|
+
if (responseText.startsWith("https://")) {
|
|
50584
|
+
return responseText.trim();
|
|
50585
|
+
}
|
|
50586
|
+
throw new Error(`Catbox upload failed: ${responseText}`);
|
|
50587
|
+
}
|
|
50588
|
+
async function uploadImages(files, downloadFile) {
|
|
50589
|
+
const results = [];
|
|
50590
|
+
for (const file of files) {
|
|
50591
|
+
if (!file.mimeType.startsWith("image/")) {
|
|
50592
|
+
results.push({
|
|
50593
|
+
success: false,
|
|
50594
|
+
error: "Not an image file",
|
|
50595
|
+
originalFile: file
|
|
50596
|
+
});
|
|
50597
|
+
continue;
|
|
50598
|
+
}
|
|
50599
|
+
try {
|
|
50600
|
+
const buffer = await downloadFile(file.id);
|
|
50601
|
+
const url = await uploadImageToCatbox(buffer, file.name);
|
|
50602
|
+
results.push({
|
|
50603
|
+
success: true,
|
|
50604
|
+
url,
|
|
50605
|
+
originalFile: file
|
|
50606
|
+
});
|
|
50607
|
+
} catch (err) {
|
|
50608
|
+
results.push({
|
|
50609
|
+
success: false,
|
|
50610
|
+
error: err instanceof Error ? err.message : String(err),
|
|
50611
|
+
originalFile: file
|
|
50612
|
+
});
|
|
50613
|
+
}
|
|
50614
|
+
}
|
|
50615
|
+
return results;
|
|
50616
|
+
}
|
|
50617
|
+
var MAX_RECENT_EVENTS = 10;
|
|
50618
|
+
var GITHUB_REPO = "anneschuth/claude-threads";
|
|
50619
|
+
function trackEvent(session, type, summary) {
|
|
50620
|
+
if (!session.recentEvents) {
|
|
50621
|
+
session.recentEvents = [];
|
|
50622
|
+
}
|
|
50623
|
+
session.recentEvents.push({
|
|
50624
|
+
type,
|
|
50625
|
+
timestamp: Date.now(),
|
|
50626
|
+
summary: summary.substring(0, 100)
|
|
50627
|
+
});
|
|
50628
|
+
if (session.recentEvents.length > MAX_RECENT_EVENTS) {
|
|
50629
|
+
session.recentEvents.shift();
|
|
50630
|
+
}
|
|
50631
|
+
}
|
|
50632
|
+
function getRecentEvents(session) {
|
|
50633
|
+
return session.recentEvents || [];
|
|
50634
|
+
}
|
|
50635
|
+
function sanitizePath(path10) {
|
|
50636
|
+
let sanitized = path10.replace(/^\/Users\/[^/]+/, "~").replace(/^\/home\/[^/]+/, "~").replace(/^C:\\Users\\[^\\]+/i, "~");
|
|
50637
|
+
sanitized = sanitized.replace(/\/(tokens?|secrets?|credentials?|\.env)/gi, "/[REDACTED]").replace(/\\(tokens?|secrets?|credentials?|\.env)/gi, "\\[REDACTED]");
|
|
50638
|
+
return sanitized;
|
|
50639
|
+
}
|
|
50640
|
+
function sanitizeText(text) {
|
|
50641
|
+
return text.replace(/xoxb-[\w-]+/gi, "[SLACK_TOKEN]").replace(/xoxp-[\w-]+/gi, "[SLACK_TOKEN]").replace(/xapp-[\w-]+/gi, "[SLACK_TOKEN]").replace(/xoxa-[\w-]+/gi, "[SLACK_TOKEN]").replace(/ghp_[\w]+/gi, "[GITHUB_TOKEN]").replace(/gho_[\w]+/gi, "[GITHUB_TOKEN]").replace(/github_pat_[\w]+/gi, "[GITHUB_TOKEN]").replace(/sk_live_[\w]+/gi, "[STRIPE_KEY]").replace(/sk_test_[\w]+/gi, "[STRIPE_KEY]").replace(/pk_live_[\w]+/gi, "[STRIPE_KEY]").replace(/pk_test_[\w]+/gi, "[STRIPE_KEY]").replace(/AKIA[\w]{16}/g, "[AWS_KEY]").replace(/['"]?[a-zA-Z_]*(?:api[_-]?key|secret|token|password|credential)['":]?\s*['"]?[\w-]{20,}['"]?/gi, "[REDACTED_KEY]").replace(/\/Users\/[\w.-]+/g, "~").replace(/\/home\/[\w.-]+/g, "~").replace(/C:\\Users\\[\w.-]+/gi, "~");
|
|
50642
|
+
}
|
|
50643
|
+
async function getCurrentBranch2(workingDir) {
|
|
50644
|
+
try {
|
|
50645
|
+
const output = execSync2("git rev-parse --abbrev-ref HEAD", {
|
|
50646
|
+
cwd: workingDir,
|
|
50647
|
+
encoding: "utf-8",
|
|
50648
|
+
timeout: 5000,
|
|
50649
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
50650
|
+
}).trim();
|
|
50651
|
+
return output || null;
|
|
50652
|
+
} catch {
|
|
50653
|
+
return null;
|
|
50654
|
+
}
|
|
50655
|
+
}
|
|
50656
|
+
async function collectBugReportContext(session, errorContext) {
|
|
50657
|
+
const branch = await getCurrentBranch2(session.workingDir);
|
|
50658
|
+
let usageStats;
|
|
50659
|
+
if (session.usageStats) {
|
|
50660
|
+
const stats = session.usageStats;
|
|
50661
|
+
const contextPercent = stats.contextWindowSize > 0 ? Math.round(stats.contextTokens / stats.contextWindowSize * 100) : 0;
|
|
50662
|
+
usageStats = {
|
|
50663
|
+
model: stats.modelDisplayName || stats.primaryModel,
|
|
50664
|
+
contextPercent,
|
|
50665
|
+
cost: stats.totalCostUSD.toFixed(2)
|
|
50666
|
+
};
|
|
50667
|
+
}
|
|
50668
|
+
return {
|
|
50669
|
+
version: VERSION,
|
|
50670
|
+
claudeCliVersion: getClaudeCliVersion(),
|
|
50671
|
+
platform: session.platform.displayName,
|
|
50672
|
+
platformType: session.platform.platformType,
|
|
50673
|
+
nodeVersion: process.version,
|
|
50674
|
+
osVersion: `${process.platform} ${process.arch}`,
|
|
50675
|
+
sessionId: session.sessionId,
|
|
50676
|
+
claudeSessionId: session.claudeSessionId,
|
|
50677
|
+
workingDir: sanitizePath(session.workingDir),
|
|
50678
|
+
branch,
|
|
50679
|
+
worktreeBranch: session.worktreeInfo?.branch,
|
|
50680
|
+
usageStats,
|
|
50681
|
+
recentEvents: getRecentEvents(session),
|
|
50682
|
+
errorContext
|
|
50683
|
+
};
|
|
50684
|
+
}
|
|
50685
|
+
function generateIssueTitle(description) {
|
|
50686
|
+
let title = description.replace(/\s+/g, " ").trim();
|
|
50687
|
+
if (title.length > 80) {
|
|
50688
|
+
title = title.substring(0, 77) + "...";
|
|
50689
|
+
}
|
|
50690
|
+
return title;
|
|
50691
|
+
}
|
|
50692
|
+
function formatRecentEvents(events) {
|
|
50693
|
+
if (events.length === 0) {
|
|
50694
|
+
return "_No recent events_";
|
|
50695
|
+
}
|
|
50696
|
+
return events.map((e) => {
|
|
50697
|
+
const time = new Date(e.timestamp).toISOString().substring(11, 19);
|
|
50698
|
+
return `[${time}] ${e.type}: ${sanitizeText(e.summary)}`;
|
|
50699
|
+
}).join(`
|
|
50700
|
+
`);
|
|
50701
|
+
}
|
|
50702
|
+
function formatIssueBody(context, userDescription, imageUrls = []) {
|
|
50703
|
+
const sections = [];
|
|
50704
|
+
sections.push(`## Description
|
|
50705
|
+
|
|
50706
|
+
${sanitizeText(userDescription)}`);
|
|
50707
|
+
if (imageUrls.length > 0) {
|
|
50708
|
+
const imageSection = imageUrls.map((url, i) => ``).join(`
|
|
50709
|
+
|
|
50710
|
+
`);
|
|
50711
|
+
sections.push(`## Screenshots
|
|
50712
|
+
|
|
50713
|
+
${imageSection}`);
|
|
50714
|
+
}
|
|
50715
|
+
sections.push(`## Environment
|
|
50716
|
+
|
|
50717
|
+
| Property | Value |
|
|
50718
|
+
|----------|-------|
|
|
50719
|
+
| claude-threads | v${context.version} |
|
|
50720
|
+
| Claude CLI | ${context.claudeCliVersion || "unknown"} |
|
|
50721
|
+
| Platform | ${context.platformType} (${context.platform}) |
|
|
50722
|
+
| Node.js | ${context.nodeVersion} |
|
|
50723
|
+
| OS | ${context.osVersion} |`);
|
|
50724
|
+
sections.push(`## Session Context
|
|
50725
|
+
|
|
50726
|
+
| Property | Value |
|
|
50727
|
+
|----------|-------|
|
|
50728
|
+
| Session ID | \`${context.claudeSessionId.substring(0, 8)}\` |
|
|
50729
|
+
| Working Dir | \`${context.workingDir}\` |
|
|
50730
|
+
| Branch | ${context.branch || "N/A"} |
|
|
50731
|
+
| Worktree | ${context.worktreeBranch || "N/A"} |`);
|
|
50732
|
+
if (context.usageStats) {
|
|
50733
|
+
sections.push(`## Usage Stats
|
|
50734
|
+
|
|
50735
|
+
- **Model:** ${context.usageStats.model}
|
|
50736
|
+
- **Context:** ${context.usageStats.contextPercent}%
|
|
50737
|
+
- **Cost:** $${context.usageStats.cost}`);
|
|
50738
|
+
}
|
|
50739
|
+
sections.push(`## Recent Events
|
|
50740
|
+
|
|
50741
|
+
\`\`\`
|
|
50742
|
+
${formatRecentEvents(context.recentEvents)}
|
|
50743
|
+
\`\`\``);
|
|
50744
|
+
if (context.errorContext) {
|
|
50745
|
+
sections.push(`## Error Details
|
|
50746
|
+
|
|
50747
|
+
**Error message:**
|
|
50748
|
+
\`\`\`
|
|
50749
|
+
${sanitizeText(context.errorContext.message)}
|
|
50750
|
+
\`\`\`
|
|
50751
|
+
|
|
50752
|
+
**Occurred at:** ${context.errorContext.timestamp.toISOString()}`);
|
|
50753
|
+
}
|
|
50754
|
+
sections.push(`---
|
|
50755
|
+
_Reported via claude-threads bug report feature_`);
|
|
50756
|
+
return sections.join(`
|
|
50757
|
+
|
|
50758
|
+
`);
|
|
50759
|
+
}
|
|
50760
|
+
function escapeShell(str) {
|
|
50761
|
+
return str.replace(/"/g, "\\\"");
|
|
50762
|
+
}
|
|
50763
|
+
function checkGitHubCli() {
|
|
50764
|
+
try {
|
|
50765
|
+
execSync2("gh --version", {
|
|
50766
|
+
encoding: "utf-8",
|
|
50767
|
+
timeout: 5000,
|
|
50768
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
50769
|
+
});
|
|
50770
|
+
} catch {
|
|
50771
|
+
return {
|
|
50772
|
+
installed: false,
|
|
50773
|
+
authenticated: false,
|
|
50774
|
+
error: "GitHub CLI not installed. Install it with: `brew install gh` or see https://cli.github.com"
|
|
50775
|
+
};
|
|
50776
|
+
}
|
|
50777
|
+
try {
|
|
50778
|
+
execSync2("gh auth status", {
|
|
50779
|
+
encoding: "utf-8",
|
|
50780
|
+
timeout: 5000,
|
|
50781
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
50782
|
+
});
|
|
50783
|
+
} catch {
|
|
50784
|
+
return {
|
|
50785
|
+
installed: true,
|
|
50786
|
+
authenticated: false,
|
|
50787
|
+
error: "Not logged into GitHub CLI. Run `gh auth login` to authenticate."
|
|
50788
|
+
};
|
|
50789
|
+
}
|
|
50790
|
+
return { installed: true, authenticated: true };
|
|
50791
|
+
}
|
|
50792
|
+
async function createGitHubIssue(title, body, workingDir) {
|
|
50793
|
+
const ghStatus = checkGitHubCli();
|
|
50794
|
+
if (!ghStatus.installed || !ghStatus.authenticated) {
|
|
50795
|
+
throw new Error(ghStatus.error);
|
|
50796
|
+
}
|
|
50797
|
+
const bodyFile = join4(tmpdir2(), `bug-body-${Date.now()}.md`);
|
|
50798
|
+
try {
|
|
50799
|
+
writeFileSync4(bodyFile, body, "utf-8");
|
|
50800
|
+
const cmd = `gh issue create --repo "${GITHUB_REPO}" --title "${escapeShell(title)}" --body-file "${bodyFile}"`;
|
|
50801
|
+
const result = execSync2(cmd, {
|
|
50802
|
+
cwd: workingDir,
|
|
50803
|
+
encoding: "utf-8",
|
|
50804
|
+
timeout: 30000,
|
|
50805
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
50806
|
+
});
|
|
50807
|
+
return result.trim();
|
|
50808
|
+
} finally {
|
|
50809
|
+
try {
|
|
50810
|
+
unlinkSync2(bodyFile);
|
|
50811
|
+
} catch {}
|
|
50812
|
+
}
|
|
50813
|
+
}
|
|
50814
|
+
function formatBugPreview(title, description, context, imageUrls, imageErrors, formatter) {
|
|
50815
|
+
const lines = [];
|
|
50816
|
+
lines.push(`${formatter.formatBold("Bug Report Preview")}`);
|
|
50817
|
+
lines.push("");
|
|
50818
|
+
lines.push(`${formatter.formatBold("Title:")} ${title}`);
|
|
50819
|
+
lines.push("");
|
|
50820
|
+
lines.push(formatter.formatBlockquote(description));
|
|
50821
|
+
lines.push("");
|
|
50822
|
+
lines.push(formatter.formatBold("Environment:"));
|
|
50823
|
+
lines.push(formatter.formatListItem(`claude-threads v${context.version}`));
|
|
50824
|
+
lines.push(formatter.formatListItem(`Claude CLI ${context.claudeCliVersion || "unknown"}`));
|
|
50825
|
+
lines.push(formatter.formatListItem(`Platform: ${context.platformType}`));
|
|
50826
|
+
lines.push(formatter.formatListItem(`Branch: ${context.branch || "N/A"}`));
|
|
50827
|
+
if (context.usageStats) {
|
|
50828
|
+
lines.push("");
|
|
50829
|
+
lines.push(formatter.formatBold("Usage:"));
|
|
50830
|
+
lines.push(formatter.formatListItem(`Model: ${context.usageStats.model}`));
|
|
50831
|
+
lines.push(formatter.formatListItem(`Context: ${context.usageStats.contextPercent}%`));
|
|
50832
|
+
}
|
|
50833
|
+
if (context.recentEvents.length > 0) {
|
|
50834
|
+
lines.push("");
|
|
50835
|
+
lines.push(formatter.formatBold(`Recent Events (${context.recentEvents.length}):`));
|
|
50836
|
+
const lastFew = context.recentEvents.slice(-3);
|
|
50837
|
+
for (const event of lastFew) {
|
|
50838
|
+
lines.push(formatter.formatListItem(`${event.type}: ${event.summary.substring(0, 40)}...`));
|
|
50839
|
+
}
|
|
50840
|
+
}
|
|
50841
|
+
if (imageUrls.length > 0 || imageErrors.length > 0) {
|
|
50842
|
+
lines.push("");
|
|
50843
|
+
lines.push(formatter.formatBold("Screenshots:"));
|
|
50844
|
+
if (imageUrls.length > 0) {
|
|
50845
|
+
lines.push(formatter.formatListItem(`\u2705 ${imageUrls.length} image(s) uploaded successfully`));
|
|
50846
|
+
}
|
|
50847
|
+
if (imageErrors.length > 0) {
|
|
50848
|
+
lines.push(formatter.formatListItem(`\u26A0\uFE0F ${imageErrors.length} image(s) failed: ${imageErrors[0]}`));
|
|
50849
|
+
}
|
|
50850
|
+
}
|
|
50851
|
+
lines.push("");
|
|
50852
|
+
lines.push(`React ${formatter.formatCode("\uD83D\uDC4D")} to create GitHub issue or ${formatter.formatCode("\uD83D\uDC4E")} to cancel`);
|
|
50853
|
+
return lines.join(`
|
|
50854
|
+
`);
|
|
50855
|
+
}
|
|
50856
|
+
|
|
50491
50857
|
// src/utils/battery.ts
|
|
50492
50858
|
import { exec as exec2 } from "child_process";
|
|
50493
50859
|
import { promisify as promisify2 } from "util";
|
|
@@ -50762,58 +51128,6 @@ class KeepAliveManager {
|
|
|
50762
51128
|
}
|
|
50763
51129
|
var keepAlive = new KeepAliveManager;
|
|
50764
51130
|
|
|
50765
|
-
// src/claude/version-check.ts
|
|
50766
|
-
var import_semver3 = __toESM(require_semver2(), 1);
|
|
50767
|
-
import { execSync } from "child_process";
|
|
50768
|
-
var CLAUDE_CLI_VERSION_RANGE = ">=2.0.74 <2.2.0";
|
|
50769
|
-
function getClaudeCliVersion() {
|
|
50770
|
-
const claudePath = process.env.CLAUDE_PATH || "claude";
|
|
50771
|
-
try {
|
|
50772
|
-
const output = execSync(`${claudePath} --version`, {
|
|
50773
|
-
encoding: "utf8",
|
|
50774
|
-
timeout: 5000,
|
|
50775
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
50776
|
-
}).trim();
|
|
50777
|
-
const match = output.match(/^([\d.]+)/);
|
|
50778
|
-
return match ? match[1] : null;
|
|
50779
|
-
} catch {
|
|
50780
|
-
return null;
|
|
50781
|
-
}
|
|
50782
|
-
}
|
|
50783
|
-
function isVersionCompatible(version) {
|
|
50784
|
-
const semverVersion = import_semver3.coerce(version);
|
|
50785
|
-
if (!semverVersion)
|
|
50786
|
-
return false;
|
|
50787
|
-
return import_semver3.satisfies(semverVersion, CLAUDE_CLI_VERSION_RANGE);
|
|
50788
|
-
}
|
|
50789
|
-
function validateClaudeCli() {
|
|
50790
|
-
const version = getClaudeCliVersion();
|
|
50791
|
-
if (!version) {
|
|
50792
|
-
return {
|
|
50793
|
-
installed: false,
|
|
50794
|
-
version: null,
|
|
50795
|
-
compatible: false,
|
|
50796
|
-
message: "Claude CLI not found. Install it with: bun install -g @anthropic-ai/claude-code"
|
|
50797
|
-
};
|
|
50798
|
-
}
|
|
50799
|
-
const compatible = isVersionCompatible(version);
|
|
50800
|
-
if (!compatible) {
|
|
50801
|
-
return {
|
|
50802
|
-
installed: true,
|
|
50803
|
-
version,
|
|
50804
|
-
compatible: false,
|
|
50805
|
-
message: `Claude CLI version ${version} is not compatible. Required: ${CLAUDE_CLI_VERSION_RANGE}
|
|
50806
|
-
` + `Install a compatible version: bun install -g @anthropic-ai/claude-code@2.0.76`
|
|
50807
|
-
};
|
|
50808
|
-
}
|
|
50809
|
-
return {
|
|
50810
|
-
installed: true,
|
|
50811
|
-
version,
|
|
50812
|
-
compatible: true,
|
|
50813
|
-
message: `Claude CLI ${version} \u2713`
|
|
50814
|
-
};
|
|
50815
|
-
}
|
|
50816
|
-
|
|
50817
51131
|
// src/session/commands.ts
|
|
50818
51132
|
var log11 = createLogger("commands");
|
|
50819
51133
|
function sessionLog2(session) {
|
|
@@ -51215,6 +51529,71 @@ async function deferUpdate(session, username, updateManager) {
|
|
|
51215
51529
|
await postSuccess(session, `\u23F8\uFE0F ${formatter.formatBold("Update deferred")} for 1 hour
|
|
51216
51530
|
` + formatter.formatItalic("Use !update now to apply earlier"));
|
|
51217
51531
|
}
|
|
51532
|
+
async function reportBug(session, description, username, ctx, errorContext, attachedFiles) {
|
|
51533
|
+
const formatter = session.platform.getFormatter();
|
|
51534
|
+
if (!description && !errorContext) {
|
|
51535
|
+
await postInfo(session, `Usage: ${formatter.formatCode("!bug <description>")}
|
|
51536
|
+
` + `Example: ${formatter.formatCode("!bug Session crashed when uploading large image")}
|
|
51537
|
+
|
|
51538
|
+
` + `You can also attach screenshots to the !bug message.
|
|
51539
|
+
` + `Or react with \uD83D\uDC1B on any error message to report it.`);
|
|
51540
|
+
return;
|
|
51541
|
+
}
|
|
51542
|
+
const ghStatus = checkGitHubCli();
|
|
51543
|
+
if (!ghStatus.installed || !ghStatus.authenticated) {
|
|
51544
|
+
await postError(session, ghStatus.error || "GitHub CLI not configured");
|
|
51545
|
+
return;
|
|
51546
|
+
}
|
|
51547
|
+
const bugDescription = description || (errorContext ? `Error: ${errorContext.message.substring(0, 200)}` : "Unknown error");
|
|
51548
|
+
const context = await collectBugReportContext(session, errorContext);
|
|
51549
|
+
let imageUrls = [];
|
|
51550
|
+
let imageErrors = [];
|
|
51551
|
+
const downloadFile = session.platform.downloadFile;
|
|
51552
|
+
if (attachedFiles && attachedFiles.length > 0 && downloadFile) {
|
|
51553
|
+
await postInfo(session, `\uD83D\uDCE4 Uploading ${attachedFiles.length} image(s)...`);
|
|
51554
|
+
const uploadResults = await uploadImages(attachedFiles, (fileId) => downloadFile(fileId));
|
|
51555
|
+
imageUrls = uploadResults.filter((r) => r.success && typeof r.url === "string").map((r) => r.url);
|
|
51556
|
+
imageErrors = uploadResults.filter((r) => !r.success).map((r) => `${r.originalFile.name}: ${r.error}`);
|
|
51557
|
+
}
|
|
51558
|
+
const title = generateIssueTitle(bugDescription);
|
|
51559
|
+
const body = formatIssueBody(context, bugDescription, imageUrls);
|
|
51560
|
+
const preview = formatBugPreview(title, bugDescription, context, imageUrls, imageErrors, formatter);
|
|
51561
|
+
const previewMessage = `\uD83D\uDC1B ${preview}`;
|
|
51562
|
+
const post = await session.platform.createInteractivePost(previewMessage, [APPROVAL_EMOJIS[0], DENIAL_EMOJIS[0]], session.threadId);
|
|
51563
|
+
session.pendingBugReport = {
|
|
51564
|
+
postId: post.id,
|
|
51565
|
+
title,
|
|
51566
|
+
body,
|
|
51567
|
+
userDescription: bugDescription,
|
|
51568
|
+
imageUrls,
|
|
51569
|
+
imageErrors,
|
|
51570
|
+
errorContext
|
|
51571
|
+
};
|
|
51572
|
+
ctx.ops.registerPost(post.id, session.threadId);
|
|
51573
|
+
updateLastMessage(session, post);
|
|
51574
|
+
sessionLog2(session).info(`\uD83D\uDC1B Bug report preview created by @${username}: ${title}`);
|
|
51575
|
+
}
|
|
51576
|
+
async function handleBugReportApproval(session, isApproved, username) {
|
|
51577
|
+
const pending = session.pendingBugReport;
|
|
51578
|
+
if (!pending)
|
|
51579
|
+
return;
|
|
51580
|
+
const formatter = session.platform.getFormatter();
|
|
51581
|
+
if (isApproved) {
|
|
51582
|
+
try {
|
|
51583
|
+
const issueUrl = await createGitHubIssue(pending.title, pending.body, session.workingDir);
|
|
51584
|
+
await withErrorHandling(() => session.platform.updatePost(pending.postId, `\u2705 ${formatter.formatBold("Bug report submitted")}: ${issueUrl}`), { action: "Update bug report post", session });
|
|
51585
|
+
sessionLog2(session).info(`\uD83D\uDC1B Bug report created by @${username}: ${issueUrl}`);
|
|
51586
|
+
} catch (err) {
|
|
51587
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
51588
|
+
await withErrorHandling(() => session.platform.updatePost(pending.postId, `\u274C ${formatter.formatBold("Failed to create bug report")}: ${errorMessage}`), { action: "Update bug report post", session });
|
|
51589
|
+
sessionLog2(session).error(`Failed to create bug report: ${errorMessage}`);
|
|
51590
|
+
}
|
|
51591
|
+
} else {
|
|
51592
|
+
await withErrorHandling(() => session.platform.updatePost(pending.postId, `\uD83D\uDEAB ${formatter.formatBold("Bug report cancelled")} by ${formatter.formatUserMention(username)}`), { action: "Update bug report post", session });
|
|
51593
|
+
sessionLog2(session).info(`\uD83D\uDC1B Bug report cancelled by @${username}`);
|
|
51594
|
+
}
|
|
51595
|
+
session.pendingBugReport = undefined;
|
|
51596
|
+
}
|
|
51218
51597
|
|
|
51219
51598
|
// src/session/worktree.ts
|
|
51220
51599
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
@@ -51635,7 +52014,8 @@ async function cleanupWorktreeCommand(session, username, hasOtherSessionsUsingWo
|
|
|
51635
52014
|
// src/commands/parser.ts
|
|
51636
52015
|
var CLAUDE_ALLOWED_COMMANDS = new Set([
|
|
51637
52016
|
"cd",
|
|
51638
|
-
"worktree list"
|
|
52017
|
+
"worktree list",
|
|
52018
|
+
"bug"
|
|
51639
52019
|
]);
|
|
51640
52020
|
var COMMAND_PATTERNS = [
|
|
51641
52021
|
["stop", /^!(?:stop|cancel)\s*$/i],
|
|
@@ -51652,7 +52032,8 @@ var COMMAND_PATTERNS = [
|
|
|
51652
52032
|
["context", /^!context\s*$/i],
|
|
51653
52033
|
["cost", /^!cost\s*$/i],
|
|
51654
52034
|
["compact", /^!compact\s*$/i],
|
|
51655
|
-
["kill", /^!kill\s*$/i]
|
|
52035
|
+
["kill", /^!kill\s*$/i],
|
|
52036
|
+
["bug", /^!bug(?:\s+(.+))?$/i]
|
|
51656
52037
|
];
|
|
51657
52038
|
function parseCommand(text) {
|
|
51658
52039
|
for (const [command, pattern] of COMMAND_PATTERNS) {
|
|
@@ -51684,6 +52065,14 @@ function parseClaudeCommand(text) {
|
|
|
51684
52065
|
match: worktreeListMatch[0].trimEnd()
|
|
51685
52066
|
};
|
|
51686
52067
|
}
|
|
52068
|
+
const bugMatch = text.match(/^!bug\s+(.+)$/m);
|
|
52069
|
+
if (bugMatch && CLAUDE_ALLOWED_COMMANDS.has("bug")) {
|
|
52070
|
+
return {
|
|
52071
|
+
command: "bug",
|
|
52072
|
+
args: bugMatch[1].trim(),
|
|
52073
|
+
match: bugMatch[0].trimEnd()
|
|
52074
|
+
};
|
|
52075
|
+
}
|
|
51687
52076
|
return null;
|
|
51688
52077
|
}
|
|
51689
52078
|
function isClaudeAllowedCommand(command) {
|
|
@@ -51783,6 +52172,9 @@ ${plainMessage}
|
|
|
51783
52172
|
}
|
|
51784
52173
|
break;
|
|
51785
52174
|
}
|
|
52175
|
+
case "bug":
|
|
52176
|
+
await reportBug(session, args, session.startedBy, ctx);
|
|
52177
|
+
break;
|
|
51786
52178
|
}
|
|
51787
52179
|
}
|
|
51788
52180
|
function extractAndUpdatePullRequest(text, session, ctx) {
|
|
@@ -51902,6 +52294,7 @@ function formatEvent(session, e, ctx) {
|
|
|
51902
52294
|
if (tool.id) {
|
|
51903
52295
|
session.activeToolStarts.set(tool.id, Date.now());
|
|
51904
52296
|
}
|
|
52297
|
+
trackEvent(session, "tool_use", tool.name);
|
|
51905
52298
|
const worktreeInfo = session.worktreeInfo ? { path: session.worktreeInfo.worktreePath, branch: session.worktreeInfo.branch } : undefined;
|
|
51906
52299
|
return formatToolUse(tool.name, tool.input || {}, session.platform.getFormatter(), { detailed: true, worktreeInfo }) || null;
|
|
51907
52300
|
}
|
|
@@ -51918,8 +52311,10 @@ function formatEvent(session, e, ctx) {
|
|
|
51918
52311
|
session.activeToolStarts.delete(result.tool_use_id);
|
|
51919
52312
|
}
|
|
51920
52313
|
}
|
|
51921
|
-
if (result.is_error)
|
|
52314
|
+
if (result.is_error) {
|
|
52315
|
+
trackEvent(session, "tool_error", "Tool execution failed");
|
|
51922
52316
|
return ` \u21B3 \u274C Error${elapsed}`;
|
|
52317
|
+
}
|
|
51923
52318
|
if (elapsed)
|
|
51924
52319
|
return ` \u21B3 \u2713${elapsed}`;
|
|
51925
52320
|
return null;
|
|
@@ -51935,8 +52330,10 @@ function formatEvent(session, e, ctx) {
|
|
|
51935
52330
|
return null;
|
|
51936
52331
|
}
|
|
51937
52332
|
case "system": {
|
|
51938
|
-
if (e.subtype === "error")
|
|
52333
|
+
if (e.subtype === "error") {
|
|
52334
|
+
trackEvent(session, "system_error", String(e.error).substring(0, 80));
|
|
51939
52335
|
return `\u274C ${e.error}`;
|
|
52336
|
+
}
|
|
51940
52337
|
return null;
|
|
51941
52338
|
}
|
|
51942
52339
|
case "user": {
|
|
@@ -51978,6 +52375,28 @@ async function handleExitPlanMode(session, toolUseId, ctx) {
|
|
|
51978
52375
|
session.pendingApproval = { postId: post.id, type: "plan", toolUseId };
|
|
51979
52376
|
ctx.ops.stopTyping(session);
|
|
51980
52377
|
}
|
|
52378
|
+
async function cleanupOrphanedTaskPosts(session, currentTaskPostId) {
|
|
52379
|
+
try {
|
|
52380
|
+
const history = await session.platform.getThreadHistory(session.threadId, { limit: 50 });
|
|
52381
|
+
const taskPostPattern = /^(?:(?:---|___|\*\*\*|\u2014+)\s*\n)?\uD83D\uDCCB/;
|
|
52382
|
+
let cleanedCount = 0;
|
|
52383
|
+
for (const msg of history) {
|
|
52384
|
+
if (msg.id === currentTaskPostId)
|
|
52385
|
+
continue;
|
|
52386
|
+
if (!taskPostPattern.test(msg.message))
|
|
52387
|
+
continue;
|
|
52388
|
+
sessionLog4(session).info(`Cleaning up orphaned task post ${msg.id.substring(0, 8)}`);
|
|
52389
|
+
await session.platform.unpinPost(msg.id).catch(() => {});
|
|
52390
|
+
await session.platform.deletePost(msg.id).catch(() => {});
|
|
52391
|
+
cleanedCount++;
|
|
52392
|
+
}
|
|
52393
|
+
if (cleanedCount > 0) {
|
|
52394
|
+
sessionLog4(session).info(`Cleaned up ${cleanedCount} orphaned task post(s)`);
|
|
52395
|
+
}
|
|
52396
|
+
} catch (err) {
|
|
52397
|
+
sessionLog4(session).debug(`Task cleanup failed: ${err}`);
|
|
52398
|
+
}
|
|
52399
|
+
}
|
|
51981
52400
|
async function handleTodoWrite(session, input, ctx) {
|
|
51982
52401
|
const releaseLock = await acquireTaskListLock(session);
|
|
51983
52402
|
try {
|
|
@@ -52066,14 +52485,20 @@ async function handleTodoWriteWithLock(session, input, ctx) {
|
|
|
52066
52485
|
const displayMessage = session.tasksMinimized ? minimizedMessage : fullMessage;
|
|
52067
52486
|
const existingTasksPostId = session.tasksPostId;
|
|
52068
52487
|
if (existingTasksPostId) {
|
|
52069
|
-
await withErrorHandling(() => session.platform.updatePost(existingTasksPostId, displayMessage), { action: "Update tasks", session });
|
|
52070
|
-
|
|
52488
|
+
const updated = await withErrorHandling(() => session.platform.updatePost(existingTasksPostId, displayMessage), { action: "Update tasks", session });
|
|
52489
|
+
if (updated === undefined) {
|
|
52490
|
+
sessionLog4(session).warn(`Task post ${existingTasksPostId.substring(0, 8)} update failed, will create new one`);
|
|
52491
|
+
session.tasksPostId = null;
|
|
52492
|
+
}
|
|
52493
|
+
}
|
|
52494
|
+
if (!session.tasksPostId) {
|
|
52071
52495
|
const post = await withErrorHandling(() => session.platform.createInteractivePost(displayMessage, [TASK_TOGGLE_EMOJIS[0]], session.threadId), { action: "Create tasks post", session });
|
|
52072
52496
|
if (post) {
|
|
52073
52497
|
session.tasksPostId = post.id;
|
|
52074
52498
|
ctx.ops.registerPost(post.id, session.threadId);
|
|
52075
52499
|
updateLastMessage(session, post);
|
|
52076
52500
|
await session.platform.pinPost(post.id).catch(() => {});
|
|
52501
|
+
await cleanupOrphanedTaskPosts(session, post.id);
|
|
52077
52502
|
}
|
|
52078
52503
|
}
|
|
52079
52504
|
ctx.ops.updateStickyMessage().catch(() => {});
|
|
@@ -52472,6 +52897,36 @@ async function handleUpdateReaction(session, postId, emojiName, username, ctx, u
|
|
|
52472
52897
|
}
|
|
52473
52898
|
return true;
|
|
52474
52899
|
}
|
|
52900
|
+
async function handleBugReportReaction(session, postId, emojiName, username, ctx) {
|
|
52901
|
+
if (!isBugReportEmoji(emojiName)) {
|
|
52902
|
+
return false;
|
|
52903
|
+
}
|
|
52904
|
+
if (!session.lastError || session.lastError.postId !== postId) {
|
|
52905
|
+
return false;
|
|
52906
|
+
}
|
|
52907
|
+
if (session.startedBy !== username && !session.platform.isUserAllowed(username) && !session.sessionAllowedUsers.has(username)) {
|
|
52908
|
+
return false;
|
|
52909
|
+
}
|
|
52910
|
+
sessionLog5(session).info(`\uD83D\uDC1B @${username} triggered bug report from error reaction`);
|
|
52911
|
+
await reportBug(session, undefined, username, ctx, session.lastError);
|
|
52912
|
+
return true;
|
|
52913
|
+
}
|
|
52914
|
+
async function handleBugApprovalReaction(session, postId, emojiName, username, _ctx) {
|
|
52915
|
+
const pending = session.pendingBugReport;
|
|
52916
|
+
if (!pending || pending.postId !== postId) {
|
|
52917
|
+
return false;
|
|
52918
|
+
}
|
|
52919
|
+
if (session.startedBy !== username && !session.platform.isUserAllowed(username) && !session.sessionAllowedUsers.has(username)) {
|
|
52920
|
+
return false;
|
|
52921
|
+
}
|
|
52922
|
+
const isApprove = isApprovalEmoji(emojiName);
|
|
52923
|
+
const isDeny = isDenialEmoji(emojiName);
|
|
52924
|
+
if (!isApprove && !isDeny) {
|
|
52925
|
+
return false;
|
|
52926
|
+
}
|
|
52927
|
+
await handleBugReportApproval(session, isApprove, username);
|
|
52928
|
+
return true;
|
|
52929
|
+
}
|
|
52475
52930
|
|
|
52476
52931
|
// src/session/lifecycle.ts
|
|
52477
52932
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
@@ -52686,7 +53141,8 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
52686
53141
|
firstPrompt: options.prompt,
|
|
52687
53142
|
messageCount: 0,
|
|
52688
53143
|
isProcessing: true,
|
|
52689
|
-
statusBarTimer: null
|
|
53144
|
+
statusBarTimer: null,
|
|
53145
|
+
recentEvents: []
|
|
52690
53146
|
};
|
|
52691
53147
|
mutableSessions(ctx).set(sessionId, session);
|
|
52692
53148
|
ctx.ops.registerPost(post.id, actualThreadId);
|
|
@@ -52843,7 +53299,8 @@ ${CHAT_PLATFORM_PROMPT}`;
|
|
|
52843
53299
|
messageCount: state.messageCount ?? 0,
|
|
52844
53300
|
isProcessing: false,
|
|
52845
53301
|
lifecyclePostId: state.lifecyclePostId,
|
|
52846
|
-
statusBarTimer: null
|
|
53302
|
+
statusBarTimer: null,
|
|
53303
|
+
recentEvents: []
|
|
52847
53304
|
};
|
|
52848
53305
|
mutableSessions(ctx).set(sessionId, session);
|
|
52849
53306
|
if (session.worktreeInfo) {
|
|
@@ -54066,6 +54523,16 @@ class SessionManager extends EventEmitter4 {
|
|
|
54066
54523
|
await handleTaskToggleReaction(session, action, this.getContext());
|
|
54067
54524
|
return;
|
|
54068
54525
|
}
|
|
54526
|
+
if (action === "added" && session.lastError?.postId === postId) {
|
|
54527
|
+
const handled = await handleBugReportReaction(session, postId, emojiName, username, this.getContext());
|
|
54528
|
+
if (handled)
|
|
54529
|
+
return;
|
|
54530
|
+
}
|
|
54531
|
+
if (action === "added" && session.pendingBugReport?.postId === postId) {
|
|
54532
|
+
const handled = await handleBugApprovalReaction(session, postId, emojiName, username, this.getContext());
|
|
54533
|
+
if (handled)
|
|
54534
|
+
return;
|
|
54535
|
+
}
|
|
54069
54536
|
}
|
|
54070
54537
|
getContextPromptHandler() {
|
|
54071
54538
|
return {
|
|
@@ -54315,7 +54782,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
54315
54782
|
for (const entry of entries) {
|
|
54316
54783
|
if (!entry.isDirectory())
|
|
54317
54784
|
continue;
|
|
54318
|
-
const worktreePath =
|
|
54785
|
+
const worktreePath = join5(worktreesDir, entry.name);
|
|
54319
54786
|
if (activeWorktrees.has(worktreePath)) {
|
|
54320
54787
|
log18.debug(`Worktree in use by persisted session, skipping: ${entry.name}`);
|
|
54321
54788
|
continue;
|
|
@@ -54493,6 +54960,12 @@ class SessionManager extends EventEmitter4 {
|
|
|
54493
54960
|
return;
|
|
54494
54961
|
await enableInteractivePermissions(session, username, this.getContext());
|
|
54495
54962
|
}
|
|
54963
|
+
async reportBug(threadId, description, username, files) {
|
|
54964
|
+
const session = this.findSessionByThreadId(threadId);
|
|
54965
|
+
if (!session)
|
|
54966
|
+
return;
|
|
54967
|
+
await reportBug(session, description, username, this.getContext(), undefined, files);
|
|
54968
|
+
}
|
|
54496
54969
|
async showUpdateStatus(threadId, _username) {
|
|
54497
54970
|
const session = this.findSessionByThreadId(threadId);
|
|
54498
54971
|
if (!session)
|
|
@@ -65233,7 +65706,8 @@ async function handleMessage(client, session, post, user, options) {
|
|
|
65233
65706
|
[code("!update defer"), "Defer pending update for 1 hour"],
|
|
65234
65707
|
[code("!escape"), "Interrupt current task (session stays active)"],
|
|
65235
65708
|
[code("!stop"), "Stop this session"],
|
|
65236
|
-
[code("!kill"), "Emergency shutdown (kills ALL sessions, exits bot)"]
|
|
65709
|
+
[code("!kill"), "Emergency shutdown (kills ALL sessions, exits bot)"],
|
|
65710
|
+
[code("!bug <description>"), "Report a bug (creates GitHub issue)"]
|
|
65237
65711
|
]);
|
|
65238
65712
|
await client.createPost(`${formatter.formatBold("Commands:")}
|
|
65239
65713
|
|
|
@@ -65328,6 +65802,12 @@ Release notes not available. See ${formatter.formatLink("GitHub releases", "http
|
|
|
65328
65802
|
await session.sendFollowUp(threadRoot, claudeCommand);
|
|
65329
65803
|
}
|
|
65330
65804
|
return;
|
|
65805
|
+
case "bug":
|
|
65806
|
+
if (isAllowed) {
|
|
65807
|
+
const files3 = post.metadata?.files;
|
|
65808
|
+
await session.reportBug(threadRoot, parsed.args, username, files3);
|
|
65809
|
+
}
|
|
65810
|
+
return;
|
|
65331
65811
|
case "kill":
|
|
65332
65812
|
return;
|
|
65333
65813
|
}
|
|
@@ -65799,7 +66279,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
65799
66279
|
|
|
65800
66280
|
// src/auto-update/installer.ts
|
|
65801
66281
|
import { spawn as spawn5 } from "child_process";
|
|
65802
|
-
import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as
|
|
66282
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
65803
66283
|
import { dirname as dirname6, resolve as resolve6 } from "path";
|
|
65804
66284
|
import { homedir as homedir4 } from "os";
|
|
65805
66285
|
var log21 = createLogger("installer");
|
|
@@ -65822,7 +66302,7 @@ function saveUpdateState(state) {
|
|
|
65822
66302
|
if (!existsSync11(dir)) {
|
|
65823
66303
|
mkdirSync3(dir, { recursive: true });
|
|
65824
66304
|
}
|
|
65825
|
-
|
|
66305
|
+
writeFileSync5(STATE_PATH, JSON.stringify(state, null, 2), "utf-8");
|
|
65826
66306
|
log21.debug("Update state saved");
|
|
65827
66307
|
} catch (err) {
|
|
65828
66308
|
log21.warn(`Failed to save update state: ${err}`);
|
|
@@ -65831,7 +66311,7 @@ function saveUpdateState(state) {
|
|
|
65831
66311
|
function clearUpdateState() {
|
|
65832
66312
|
try {
|
|
65833
66313
|
if (existsSync11(STATE_PATH)) {
|
|
65834
|
-
|
|
66314
|
+
writeFileSync5(STATE_PATH, "{}", "utf-8");
|
|
65835
66315
|
}
|
|
65836
66316
|
} catch (err) {
|
|
65837
66317
|
log21.warn(`Failed to clear update state: ${err}`);
|
|
@@ -33831,9 +33831,11 @@ ${code}
|
|
|
33831
33831
|
`);
|
|
33832
33832
|
}
|
|
33833
33833
|
formatMarkdown(content) {
|
|
33834
|
-
|
|
33834
|
+
let processed = content.replace(/(?<=\n)```(?=\S)(?![a-zA-Z]*\n)/g, "```\n");
|
|
33835
|
+
processed = processed.replace(/\n{3,}/g, `
|
|
33835
33836
|
|
|
33836
33837
|
`);
|
|
33838
|
+
return processed;
|
|
33837
33839
|
}
|
|
33838
33840
|
}
|
|
33839
33841
|
|
|
@@ -34104,6 +34106,7 @@ function convertMarkdownToSlack(content) {
|
|
|
34104
34106
|
for (let i = 0;i < codeBlocks.length; i++) {
|
|
34105
34107
|
preserved = preserved.replace(`${CODE_BLOCK_PLACEHOLDER}${i}\x00`, codeBlocks[i]);
|
|
34106
34108
|
}
|
|
34109
|
+
preserved = preserved.replace(/(?<=\n)```(?=\S)(?![a-zA-Z]*\n)/g, "```\n");
|
|
34107
34110
|
return preserved;
|
|
34108
34111
|
}
|
|
34109
34112
|
function convertMarkdownTablesToSlack(content) {
|