claude-threads 1.27.0 → 1.29.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 +32 -2
- package/README.md +3 -0
- package/dist/index.js +2046 -1015
- package/dist/mcp/mcp-server.js +589 -329
- package/docs/CONFIGURATION.md +109 -0
- package/package.json +1 -1
package/dist/mcp/mcp-server.js
CHANGED
|
@@ -50772,6 +50772,144 @@ class SystemExecutor extends BaseExecutor {
|
|
|
50772
50772
|
}
|
|
50773
50773
|
// src/operations/executors/question-approval.ts
|
|
50774
50774
|
init_emoji();
|
|
50775
|
+
|
|
50776
|
+
// src/persistence/audit-log.ts
|
|
50777
|
+
import { chmodSync, closeSync, constants as fsConstants, fchmodSync, lstatSync, mkdirSync, openSync, writeSync } from "fs";
|
|
50778
|
+
import { join as join2 } from "path";
|
|
50779
|
+
import { homedir } from "os";
|
|
50780
|
+
|
|
50781
|
+
// src/utils/logger.ts
|
|
50782
|
+
var globalLogHandler = null;
|
|
50783
|
+
var COMPONENT_WIDTH = 10;
|
|
50784
|
+
function createLogger(component, useStderr = false, sessionId) {
|
|
50785
|
+
const isDebug = () => process.env.DEBUG === "1";
|
|
50786
|
+
const consoleLog = useStderr ? console.error : console.log;
|
|
50787
|
+
const paddedComponent = component.length > COMPONENT_WIDTH ? component.substring(0, COMPONENT_WIDTH) : component.padEnd(COMPONENT_WIDTH);
|
|
50788
|
+
const formatMessage = (msg, args) => {
|
|
50789
|
+
if (args.length === 0)
|
|
50790
|
+
return msg;
|
|
50791
|
+
return `${msg} ${args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}`;
|
|
50792
|
+
};
|
|
50793
|
+
const DEFAULT_JSON_MAX_LEN = 60;
|
|
50794
|
+
return {
|
|
50795
|
+
debug: (msg, ...args) => {
|
|
50796
|
+
if (isDebug()) {
|
|
50797
|
+
const fullMsg = formatMessage(msg, args);
|
|
50798
|
+
if (globalLogHandler) {
|
|
50799
|
+
globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
|
|
50800
|
+
} else {
|
|
50801
|
+
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
50802
|
+
}
|
|
50803
|
+
}
|
|
50804
|
+
},
|
|
50805
|
+
debugJson: (label, data, maxLen = DEFAULT_JSON_MAX_LEN) => {
|
|
50806
|
+
if (isDebug()) {
|
|
50807
|
+
const json2 = JSON.stringify(data);
|
|
50808
|
+
const truncated = json2.length > maxLen ? `${json2.substring(0, maxLen)}…` : json2;
|
|
50809
|
+
const fullMsg = `${label}: ${truncated}`;
|
|
50810
|
+
if (globalLogHandler) {
|
|
50811
|
+
globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
|
|
50812
|
+
} else {
|
|
50813
|
+
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
50814
|
+
}
|
|
50815
|
+
}
|
|
50816
|
+
},
|
|
50817
|
+
info: (msg, ...args) => {
|
|
50818
|
+
const fullMsg = formatMessage(msg, args);
|
|
50819
|
+
if (globalLogHandler) {
|
|
50820
|
+
globalLogHandler("info", paddedComponent, fullMsg, sessionId);
|
|
50821
|
+
} else {
|
|
50822
|
+
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
50823
|
+
}
|
|
50824
|
+
},
|
|
50825
|
+
warn: (msg, ...args) => {
|
|
50826
|
+
const fullMsg = formatMessage(msg, args);
|
|
50827
|
+
if (globalLogHandler) {
|
|
50828
|
+
globalLogHandler("warn", paddedComponent, fullMsg, sessionId);
|
|
50829
|
+
} else {
|
|
50830
|
+
console.warn(`[${paddedComponent}] ⚠️ ${fullMsg}`);
|
|
50831
|
+
}
|
|
50832
|
+
},
|
|
50833
|
+
error: (msg, err) => {
|
|
50834
|
+
const fullMsg = err && isDebug() ? `${msg}
|
|
50835
|
+
${err.stack || err.message}` : msg;
|
|
50836
|
+
if (globalLogHandler) {
|
|
50837
|
+
globalLogHandler("error", paddedComponent, fullMsg, sessionId);
|
|
50838
|
+
} else {
|
|
50839
|
+
console.error(`[${paddedComponent}] ❌ ${msg}`);
|
|
50840
|
+
if (err && isDebug()) {
|
|
50841
|
+
console.error(err);
|
|
50842
|
+
}
|
|
50843
|
+
}
|
|
50844
|
+
},
|
|
50845
|
+
forSession: (sid) => createLogger(component, useStderr, sid)
|
|
50846
|
+
};
|
|
50847
|
+
}
|
|
50848
|
+
var mcpLogger = createLogger("MCP", true);
|
|
50849
|
+
var wsLogger = createLogger("ws", false);
|
|
50850
|
+
|
|
50851
|
+
// src/persistence/audit-log.ts
|
|
50852
|
+
var log = createLogger("audit");
|
|
50853
|
+
var DETAIL_MAX = 500;
|
|
50854
|
+
var enabledPlatforms = new Set;
|
|
50855
|
+
var preparedDirs = new Set;
|
|
50856
|
+
var openFds = new Map;
|
|
50857
|
+
function auditDir() {
|
|
50858
|
+
return process.env.CLAUDE_THREADS_AUDIT_DIR || join2(homedir(), ".claude-threads", "audit");
|
|
50859
|
+
}
|
|
50860
|
+
function openAuditFd(platformId) {
|
|
50861
|
+
const cached3 = openFds.get(platformId);
|
|
50862
|
+
if (cached3 !== undefined)
|
|
50863
|
+
return cached3;
|
|
50864
|
+
const dir = auditDir();
|
|
50865
|
+
if (!preparedDirs.has(dir)) {
|
|
50866
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
50867
|
+
chmodSync(dir, 448);
|
|
50868
|
+
preparedDirs.add(dir);
|
|
50869
|
+
}
|
|
50870
|
+
const file2 = join2(dir, `${encodeURIComponent(platformId)}.jsonl`);
|
|
50871
|
+
try {
|
|
50872
|
+
const st = lstatSync(file2);
|
|
50873
|
+
if (st.isSymbolicLink() || !st.isFile()) {
|
|
50874
|
+
throw new Error(`audit path exists but is not a regular file: ${file2}`);
|
|
50875
|
+
}
|
|
50876
|
+
} catch (err) {
|
|
50877
|
+
if (err.code !== "ENOENT")
|
|
50878
|
+
throw err;
|
|
50879
|
+
}
|
|
50880
|
+
const noFollow = typeof fsConstants.O_NOFOLLOW === "number" ? fsConstants.O_NOFOLLOW : 0;
|
|
50881
|
+
const fd = openSync(file2, fsConstants.O_WRONLY | fsConstants.O_APPEND | fsConstants.O_CREAT | noFollow, 384);
|
|
50882
|
+
try {
|
|
50883
|
+
fchmodSync(fd, 384);
|
|
50884
|
+
} catch {}
|
|
50885
|
+
openFds.set(platformId, fd);
|
|
50886
|
+
return fd;
|
|
50887
|
+
}
|
|
50888
|
+
function auditLog(platformId, entry) {
|
|
50889
|
+
if (!enabledPlatforms.has(platformId))
|
|
50890
|
+
return;
|
|
50891
|
+
try {
|
|
50892
|
+
const full = {
|
|
50893
|
+
ts: new Date().toISOString(),
|
|
50894
|
+
platformId,
|
|
50895
|
+
...entry,
|
|
50896
|
+
...entry.detail !== undefined ? { detail: entry.detail.slice(0, DETAIL_MAX) } : {}
|
|
50897
|
+
};
|
|
50898
|
+
writeSync(openAuditFd(platformId), JSON.stringify(full) + `
|
|
50899
|
+
`);
|
|
50900
|
+
} catch (err) {
|
|
50901
|
+
const fd = openFds.get(platformId);
|
|
50902
|
+
if (fd !== undefined) {
|
|
50903
|
+
openFds.delete(platformId);
|
|
50904
|
+
try {
|
|
50905
|
+
closeSync(fd);
|
|
50906
|
+
} catch {}
|
|
50907
|
+
}
|
|
50908
|
+
log.warn(`audit write failed for ${platformId}: ${err}`);
|
|
50909
|
+
}
|
|
50910
|
+
}
|
|
50911
|
+
|
|
50912
|
+
// src/operations/executors/question-approval.ts
|
|
50775
50913
|
class QuestionApprovalExecutor extends BaseExecutor {
|
|
50776
50914
|
constructor(options) {
|
|
50777
50915
|
super(options, QuestionApprovalExecutor.createInitialState());
|
|
@@ -50986,14 +51124,29 @@ class QuestionApprovalExecutor extends BaseExecutor {
|
|
|
50986
51124
|
return false;
|
|
50987
51125
|
}
|
|
50988
51126
|
if (this.state.pendingApproval?.postId === postId) {
|
|
51127
|
+
const approvalType = this.state.pendingApproval.type;
|
|
51128
|
+
const auditDecision = (approved) => {
|
|
51129
|
+
if (approvalType !== "plan")
|
|
51130
|
+
return;
|
|
51131
|
+
auditLog(ctx.platform.platformId, {
|
|
51132
|
+
threadId: ctx.threadId,
|
|
51133
|
+
sessionId: ctx.sessionId,
|
|
51134
|
+
actor: user,
|
|
51135
|
+
kind: "plan_approval",
|
|
51136
|
+
approved,
|
|
51137
|
+
detail: "via reaction"
|
|
51138
|
+
});
|
|
51139
|
+
};
|
|
50989
51140
|
if (isApprovalEmoji(emoji4)) {
|
|
50990
51141
|
ctx.logger.debug(`Approval reaction from @${user}: approved`);
|
|
51142
|
+
auditDecision(true);
|
|
50991
51143
|
const handled = await this.handleApprovalResponse(postId, true, ctx);
|
|
50992
51144
|
ctx.logger.debug(`QuestionApprovalExecutor: approval outcome=approved, handled=${handled}`);
|
|
50993
51145
|
return handled;
|
|
50994
51146
|
}
|
|
50995
51147
|
if (isDenialEmoji(emoji4)) {
|
|
50996
51148
|
ctx.logger.debug(`Approval reaction from @${user}: denied`);
|
|
51149
|
+
auditDecision(false);
|
|
50997
51150
|
const handled = await this.handleApprovalResponse(postId, false, ctx);
|
|
50998
51151
|
ctx.logger.debug(`QuestionApprovalExecutor: approval outcome=denied, handled=${handled}`);
|
|
50999
51152
|
return handled;
|
|
@@ -51112,7 +51265,8 @@ class PromptExecutor extends BaseExecutor {
|
|
|
51112
51265
|
pendingContextPrompt: null,
|
|
51113
51266
|
pendingExistingWorktreePrompt: null,
|
|
51114
51267
|
pendingUpdatePrompt: null,
|
|
51115
|
-
pendingRoutinePrompt: null
|
|
51268
|
+
pendingRoutinePrompt: null,
|
|
51269
|
+
pendingWatchPrompt: null
|
|
51116
51270
|
};
|
|
51117
51271
|
}
|
|
51118
51272
|
getInitialState() {
|
|
@@ -51123,7 +51277,8 @@ class PromptExecutor extends BaseExecutor {
|
|
|
51123
51277
|
pendingContextPrompt: this.state.pendingContextPrompt ? { ...this.state.pendingContextPrompt } : null,
|
|
51124
51278
|
pendingExistingWorktreePrompt: this.state.pendingExistingWorktreePrompt ? { ...this.state.pendingExistingWorktreePrompt } : null,
|
|
51125
51279
|
pendingUpdatePrompt: this.state.pendingUpdatePrompt ? { ...this.state.pendingUpdatePrompt } : null,
|
|
51126
|
-
pendingRoutinePrompt: this.state.pendingRoutinePrompt ? { ...this.state.pendingRoutinePrompt } : null
|
|
51280
|
+
pendingRoutinePrompt: this.state.pendingRoutinePrompt ? { ...this.state.pendingRoutinePrompt } : null,
|
|
51281
|
+
pendingWatchPrompt: this.state.pendingWatchPrompt ? { ...this.state.pendingWatchPrompt } : null
|
|
51127
51282
|
};
|
|
51128
51283
|
}
|
|
51129
51284
|
hydrateState(persisted) {
|
|
@@ -51131,7 +51286,8 @@ class PromptExecutor extends BaseExecutor {
|
|
|
51131
51286
|
pendingContextPrompt: persisted.pendingContextPrompt ?? null,
|
|
51132
51287
|
pendingExistingWorktreePrompt: persisted.pendingExistingWorktreePrompt ?? null,
|
|
51133
51288
|
pendingUpdatePrompt: persisted.pendingUpdatePrompt ?? null,
|
|
51134
|
-
pendingRoutinePrompt: null
|
|
51289
|
+
pendingRoutinePrompt: null,
|
|
51290
|
+
pendingWatchPrompt: null
|
|
51135
51291
|
};
|
|
51136
51292
|
}
|
|
51137
51293
|
setPendingContextPrompt(prompt) {
|
|
@@ -51267,24 +51423,36 @@ class PromptExecutor extends BaseExecutor {
|
|
|
51267
51423
|
hasPendingRoutinePrompt() {
|
|
51268
51424
|
return this.state.pendingRoutinePrompt !== null;
|
|
51269
51425
|
}
|
|
51270
|
-
async
|
|
51271
|
-
if (!
|
|
51272
|
-
return false;
|
|
51273
|
-
if (this.state.pendingRoutinePrompt.postId !== postId)
|
|
51426
|
+
async completeCreationPrompt(pending, label, clear, emit, postId, approved, username, ctx) {
|
|
51427
|
+
if (!pending || pending.postId !== postId)
|
|
51274
51428
|
return false;
|
|
51275
|
-
const { parsed, requestedBy } =
|
|
51276
|
-
const statusMessage = approved ? `✅ ${ctx.formatter.formatBold(
|
|
51429
|
+
const { parsed, requestedBy } = pending;
|
|
51430
|
+
const statusMessage = approved ? `✅ ${ctx.formatter.formatBold(`${label} "${parsed.name}" confirmed`)} by ${ctx.formatter.formatUserMention(username)} — saving...` : `❌ ${ctx.formatter.formatBold(`${label} "${parsed.name}" discarded`)} by ${ctx.formatter.formatUserMention(username)}`;
|
|
51277
51431
|
try {
|
|
51278
51432
|
await ctx.platform.updatePost(postId, statusMessage);
|
|
51279
51433
|
} catch (err) {
|
|
51280
|
-
ctx.logger.debug(`Failed to update
|
|
51281
|
-
}
|
|
51282
|
-
this.state.pendingRoutinePrompt = null;
|
|
51283
|
-
if (this.events) {
|
|
51284
|
-
this.events.emit("routine-prompt:complete", { approved, parsed, requestedBy, postId });
|
|
51434
|
+
ctx.logger.debug(`Failed to update ${label.toLowerCase()} prompt post: ${err}`);
|
|
51285
51435
|
}
|
|
51436
|
+
clear();
|
|
51437
|
+
emit({ approved, parsed, requestedBy, postId });
|
|
51286
51438
|
return true;
|
|
51287
51439
|
}
|
|
51440
|
+
handleRoutinePromptResponse(postId, approved, username, ctx) {
|
|
51441
|
+
return this.completeCreationPrompt(this.state.pendingRoutinePrompt, "Routine", () => {
|
|
51442
|
+
this.state.pendingRoutinePrompt = null;
|
|
51443
|
+
}, (payload) => this.events?.emit("routine-prompt:complete", payload), postId, approved, username, ctx);
|
|
51444
|
+
}
|
|
51445
|
+
setPendingWatchPrompt(prompt) {
|
|
51446
|
+
this.state.pendingWatchPrompt = prompt;
|
|
51447
|
+
}
|
|
51448
|
+
hasPendingWatchPrompt() {
|
|
51449
|
+
return this.state.pendingWatchPrompt !== null;
|
|
51450
|
+
}
|
|
51451
|
+
handleWatchPromptResponse(postId, approved, username, ctx) {
|
|
51452
|
+
return this.completeCreationPrompt(this.state.pendingWatchPrompt, "Watch", () => {
|
|
51453
|
+
this.state.pendingWatchPrompt = null;
|
|
51454
|
+
}, (payload) => this.events?.emit("watch-prompt:complete", payload), postId, approved, username, ctx);
|
|
51455
|
+
}
|
|
51288
51456
|
async handleReaction(postId, emoji4, user, action, ctx) {
|
|
51289
51457
|
ctx.logger.debug(`PromptExecutor.handleReaction: postId=${postId.substring(0, 8)}, emoji=${emoji4}, user=${user}, action=${action}`);
|
|
51290
51458
|
if (action !== "added") {
|
|
@@ -51357,6 +51525,18 @@ class PromptExecutor extends BaseExecutor {
|
|
|
51357
51525
|
ctx.logger.debug(`PromptExecutor: emoji ${emoji4} not valid for routine prompt, ignoring`);
|
|
51358
51526
|
return false;
|
|
51359
51527
|
}
|
|
51528
|
+
if (this.state.pendingWatchPrompt?.postId === postId) {
|
|
51529
|
+
if (isApprovalEmoji(emoji4)) {
|
|
51530
|
+
ctx.logger.debug(`Watch prompt reaction from @${user}: approve`);
|
|
51531
|
+
return this.handleWatchPromptResponse(postId, true, user, ctx);
|
|
51532
|
+
}
|
|
51533
|
+
if (isDenialEmoji(emoji4)) {
|
|
51534
|
+
ctx.logger.debug(`Watch prompt reaction from @${user}: discard`);
|
|
51535
|
+
return this.handleWatchPromptResponse(postId, false, user, ctx);
|
|
51536
|
+
}
|
|
51537
|
+
ctx.logger.debug(`PromptExecutor: emoji ${emoji4} not valid for watch prompt, ignoring`);
|
|
51538
|
+
return false;
|
|
51539
|
+
}
|
|
51360
51540
|
ctx.logger.debug(`PromptExecutor: no pending prompt state matches postId=${postId.substring(0, 8)}`);
|
|
51361
51541
|
return false;
|
|
51362
51542
|
}
|
|
@@ -51450,79 +51630,7 @@ class BugReportExecutor extends BaseExecutor {
|
|
|
51450
51630
|
}
|
|
51451
51631
|
// src/operations/executors/worktree-prompt.ts
|
|
51452
51632
|
init_emoji();
|
|
51453
|
-
|
|
51454
|
-
// src/utils/logger.ts
|
|
51455
|
-
var globalLogHandler = null;
|
|
51456
|
-
var COMPONENT_WIDTH = 10;
|
|
51457
|
-
function createLogger(component, useStderr = false, sessionId) {
|
|
51458
|
-
const isDebug = () => process.env.DEBUG === "1";
|
|
51459
|
-
const consoleLog = useStderr ? console.error : console.log;
|
|
51460
|
-
const paddedComponent = component.length > COMPONENT_WIDTH ? component.substring(0, COMPONENT_WIDTH) : component.padEnd(COMPONENT_WIDTH);
|
|
51461
|
-
const formatMessage = (msg, args) => {
|
|
51462
|
-
if (args.length === 0)
|
|
51463
|
-
return msg;
|
|
51464
|
-
return `${msg} ${args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}`;
|
|
51465
|
-
};
|
|
51466
|
-
const DEFAULT_JSON_MAX_LEN = 60;
|
|
51467
|
-
return {
|
|
51468
|
-
debug: (msg, ...args) => {
|
|
51469
|
-
if (isDebug()) {
|
|
51470
|
-
const fullMsg = formatMessage(msg, args);
|
|
51471
|
-
if (globalLogHandler) {
|
|
51472
|
-
globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
|
|
51473
|
-
} else {
|
|
51474
|
-
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
51475
|
-
}
|
|
51476
|
-
}
|
|
51477
|
-
},
|
|
51478
|
-
debugJson: (label, data, maxLen = DEFAULT_JSON_MAX_LEN) => {
|
|
51479
|
-
if (isDebug()) {
|
|
51480
|
-
const json2 = JSON.stringify(data);
|
|
51481
|
-
const truncated = json2.length > maxLen ? `${json2.substring(0, maxLen)}…` : json2;
|
|
51482
|
-
const fullMsg = `${label}: ${truncated}`;
|
|
51483
|
-
if (globalLogHandler) {
|
|
51484
|
-
globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
|
|
51485
|
-
} else {
|
|
51486
|
-
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
51487
|
-
}
|
|
51488
|
-
}
|
|
51489
|
-
},
|
|
51490
|
-
info: (msg, ...args) => {
|
|
51491
|
-
const fullMsg = formatMessage(msg, args);
|
|
51492
|
-
if (globalLogHandler) {
|
|
51493
|
-
globalLogHandler("info", paddedComponent, fullMsg, sessionId);
|
|
51494
|
-
} else {
|
|
51495
|
-
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
51496
|
-
}
|
|
51497
|
-
},
|
|
51498
|
-
warn: (msg, ...args) => {
|
|
51499
|
-
const fullMsg = formatMessage(msg, args);
|
|
51500
|
-
if (globalLogHandler) {
|
|
51501
|
-
globalLogHandler("warn", paddedComponent, fullMsg, sessionId);
|
|
51502
|
-
} else {
|
|
51503
|
-
console.warn(`[${paddedComponent}] ⚠️ ${fullMsg}`);
|
|
51504
|
-
}
|
|
51505
|
-
},
|
|
51506
|
-
error: (msg, err) => {
|
|
51507
|
-
const fullMsg = err && isDebug() ? `${msg}
|
|
51508
|
-
${err.stack || err.message}` : msg;
|
|
51509
|
-
if (globalLogHandler) {
|
|
51510
|
-
globalLogHandler("error", paddedComponent, fullMsg, sessionId);
|
|
51511
|
-
} else {
|
|
51512
|
-
console.error(`[${paddedComponent}] ❌ ${msg}`);
|
|
51513
|
-
if (err && isDebug()) {
|
|
51514
|
-
console.error(err);
|
|
51515
|
-
}
|
|
51516
|
-
}
|
|
51517
|
-
},
|
|
51518
|
-
forSession: (sid) => createLogger(component, useStderr, sid)
|
|
51519
|
-
};
|
|
51520
|
-
}
|
|
51521
|
-
var mcpLogger = createLogger("MCP", true);
|
|
51522
|
-
var wsLogger = createLogger("ws", false);
|
|
51523
|
-
|
|
51524
|
-
// src/operations/executors/worktree-prompt.ts
|
|
51525
|
-
var log = createLogger("wt-prompt");
|
|
51633
|
+
var log2 = createLogger("wt-prompt");
|
|
51526
51634
|
// src/operations/message-manager-events.ts
|
|
51527
51635
|
import { EventEmitter } from "events";
|
|
51528
51636
|
|
|
@@ -51571,7 +51679,7 @@ function formatBytes(bytes) {
|
|
|
51571
51679
|
}
|
|
51572
51680
|
|
|
51573
51681
|
// src/operations/streaming/handler.ts
|
|
51574
|
-
var
|
|
51682
|
+
var log3 = createLogger("streaming");
|
|
51575
51683
|
async function postSkippedFilesFeedback(platform, threadId, skipped) {
|
|
51576
51684
|
if (skipped.length === 0)
|
|
51577
51685
|
return;
|
|
@@ -51639,7 +51747,7 @@ function formatRelativeTime(date8) {
|
|
|
51639
51747
|
return `${diffMin} min ago`;
|
|
51640
51748
|
}
|
|
51641
51749
|
// src/operations/message-manager.ts
|
|
51642
|
-
var
|
|
51750
|
+
var log4 = createLogger("msg-mgr");
|
|
51643
51751
|
|
|
51644
51752
|
class MessageManager {
|
|
51645
51753
|
platform;
|
|
@@ -51732,7 +51840,7 @@ class MessageManager {
|
|
|
51732
51840
|
});
|
|
51733
51841
|
}
|
|
51734
51842
|
async handleEvent(event) {
|
|
51735
|
-
const logger =
|
|
51843
|
+
const logger = log4.forSession(this.sessionId);
|
|
51736
51844
|
const transformCtx = {
|
|
51737
51845
|
sessionId: this.sessionId,
|
|
51738
51846
|
formatter: this.platform.getFormatter(),
|
|
@@ -51786,7 +51894,7 @@ class MessageManager {
|
|
|
51786
51894
|
}
|
|
51787
51895
|
}
|
|
51788
51896
|
async executeOperation(op) {
|
|
51789
|
-
const logger =
|
|
51897
|
+
const logger = log4.forSession(this.sessionId);
|
|
51790
51898
|
const ctx = this.getExecutorContext();
|
|
51791
51899
|
try {
|
|
51792
51900
|
if (isContentOp(op)) {
|
|
@@ -51854,7 +51962,7 @@ class MessageManager {
|
|
|
51854
51962
|
threadId: this.threadId,
|
|
51855
51963
|
platform: this.platform,
|
|
51856
51964
|
formatter: this.platform.getFormatter(),
|
|
51857
|
-
logger:
|
|
51965
|
+
logger: log4.forSession(this.sessionId),
|
|
51858
51966
|
postTracker: this.postTracker,
|
|
51859
51967
|
contentBreaker: this.contentBreaker,
|
|
51860
51968
|
threadLogger: this.session.threadLogger,
|
|
@@ -51976,6 +52084,9 @@ class MessageManager {
|
|
|
51976
52084
|
setPendingRoutinePrompt(prompt) {
|
|
51977
52085
|
this.promptExecutor.setPendingRoutinePrompt(prompt);
|
|
51978
52086
|
}
|
|
52087
|
+
setPendingWatchPrompt(prompt) {
|
|
52088
|
+
this.promptExecutor.setPendingWatchPrompt(prompt);
|
|
52089
|
+
}
|
|
51979
52090
|
setPendingBugReport(report) {
|
|
51980
52091
|
this.bugReportExecutor.setPendingBugReport(report);
|
|
51981
52092
|
}
|
|
@@ -52074,13 +52185,13 @@ class MessageManager {
|
|
|
52074
52185
|
return this.systemExecutor.postSuccess(message, this.getExecutorContext());
|
|
52075
52186
|
}
|
|
52076
52187
|
async prepareForUserMessage() {
|
|
52077
|
-
const logger =
|
|
52188
|
+
const logger = log4.forSession(this.sessionId);
|
|
52078
52189
|
logger.debug("Preparing for new user message");
|
|
52079
52190
|
await this.closeCurrentPost();
|
|
52080
52191
|
await this.bumpTaskList();
|
|
52081
52192
|
}
|
|
52082
52193
|
async handleUserMessage(message, files, username, displayName) {
|
|
52083
|
-
const logger =
|
|
52194
|
+
const logger = log4.forSession(this.sessionId);
|
|
52084
52195
|
if (!this.session.claude.isRunning()) {
|
|
52085
52196
|
logger.debug("Claude not running, ignoring user message");
|
|
52086
52197
|
return false;
|
|
@@ -52123,7 +52234,7 @@ class MessageManager {
|
|
|
52123
52234
|
];
|
|
52124
52235
|
}
|
|
52125
52236
|
async handleReaction(postId, emoji4, user, action) {
|
|
52126
|
-
const logger =
|
|
52237
|
+
const logger = log4.forSession(this.sessionId);
|
|
52127
52238
|
const ctx = this.getExecutorContext();
|
|
52128
52239
|
logger.debug(`Routing reaction: postId=${postId}, emoji=${emoji4}, user=${user}, action=${action}`);
|
|
52129
52240
|
for (const { name, executor } of this.reactionDispatchList()) {
|
|
@@ -52220,7 +52331,7 @@ class MessageManager {
|
|
|
52220
52331
|
}
|
|
52221
52332
|
}
|
|
52222
52333
|
// src/session/lifecycle-fsm.ts
|
|
52223
|
-
var
|
|
52334
|
+
var log5 = createLogger("fsm");
|
|
52224
52335
|
var ALLOWED_TRANSITIONS = {
|
|
52225
52336
|
starting: new Set(["active", "paused", "interrupted", "cancelling", "restarting"]),
|
|
52226
52337
|
active: new Set([
|
|
@@ -52247,7 +52358,7 @@ var ALLOWED_TRANSITIONS = {
|
|
|
52247
52358
|
};
|
|
52248
52359
|
// src/config/index.ts
|
|
52249
52360
|
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
52250
|
-
import { homedir } from "os";
|
|
52361
|
+
import { homedir as homedir2 } from "os";
|
|
52251
52362
|
|
|
52252
52363
|
// node_modules/js-yaml/dist/js-yaml.mjs
|
|
52253
52364
|
function getDefaultExportFromCjs(x) {
|
|
@@ -55359,7 +55470,7 @@ var jsYamlExports = requireJsYaml();
|
|
|
55359
55470
|
var yaml = /* @__PURE__ */ getDefaultExportFromCjs(jsYamlExports);
|
|
55360
55471
|
|
|
55361
55472
|
// src/config/index.ts
|
|
55362
|
-
var CONFIG_PATH = resolve2(
|
|
55473
|
+
var CONFIG_PATH = resolve2(homedir2(), ".config", "claude-threads", "config.yaml");
|
|
55363
55474
|
|
|
55364
55475
|
// src/utils/battery.ts
|
|
55365
55476
|
import { exec } from "child_process";
|
|
@@ -55464,7 +55575,7 @@ function formatReleaseNotes(notes, formatter) {
|
|
|
55464
55575
|
|
|
55465
55576
|
// src/utils/keep-alive.ts
|
|
55466
55577
|
import { spawn } from "child_process";
|
|
55467
|
-
var
|
|
55578
|
+
var log6 = createLogger("keepalive");
|
|
55468
55579
|
function keepAliveSpawnSpec(platform, parentPid) {
|
|
55469
55580
|
switch (platform) {
|
|
55470
55581
|
case "darwin":
|
|
@@ -55520,7 +55631,7 @@ class KeepAliveManager {
|
|
|
55520
55631
|
if (!enabled && this.keepAliveProcess) {
|
|
55521
55632
|
this.stopKeepAlive();
|
|
55522
55633
|
}
|
|
55523
|
-
|
|
55634
|
+
log6.debug(`Keep-alive ${enabled ? "enabled" : "disabled"}`);
|
|
55524
55635
|
}
|
|
55525
55636
|
isEnabled() {
|
|
55526
55637
|
return this.enabled;
|
|
@@ -55530,7 +55641,7 @@ class KeepAliveManager {
|
|
|
55530
55641
|
}
|
|
55531
55642
|
sessionStarted() {
|
|
55532
55643
|
this.activeSessionCount++;
|
|
55533
|
-
|
|
55644
|
+
log6.debug(`Session started (${this.activeSessionCount} active)`);
|
|
55534
55645
|
if (this.activeSessionCount === 1) {
|
|
55535
55646
|
this.startKeepAlive();
|
|
55536
55647
|
}
|
|
@@ -55539,7 +55650,7 @@ class KeepAliveManager {
|
|
|
55539
55650
|
if (this.activeSessionCount > 0) {
|
|
55540
55651
|
this.activeSessionCount--;
|
|
55541
55652
|
}
|
|
55542
|
-
|
|
55653
|
+
log6.debug(`Session ended (${this.activeSessionCount} active)`);
|
|
55543
55654
|
if (this.activeSessionCount === 0) {
|
|
55544
55655
|
this.stopKeepAlive();
|
|
55545
55656
|
}
|
|
@@ -55553,11 +55664,11 @@ class KeepAliveManager {
|
|
|
55553
55664
|
}
|
|
55554
55665
|
startKeepAlive() {
|
|
55555
55666
|
if (!this.enabled) {
|
|
55556
|
-
|
|
55667
|
+
log6.debug("Keep-alive disabled, skipping");
|
|
55557
55668
|
return;
|
|
55558
55669
|
}
|
|
55559
55670
|
if (this.keepAliveProcess) {
|
|
55560
|
-
|
|
55671
|
+
log6.debug("Keep-alive already running");
|
|
55561
55672
|
return;
|
|
55562
55673
|
}
|
|
55563
55674
|
switch (this.platform) {
|
|
@@ -55571,12 +55682,12 @@ class KeepAliveManager {
|
|
|
55571
55682
|
this.startWindowsKeepAlive();
|
|
55572
55683
|
break;
|
|
55573
55684
|
default:
|
|
55574
|
-
|
|
55685
|
+
log6.warn(`Keep-alive not supported on ${this.platform}`);
|
|
55575
55686
|
}
|
|
55576
55687
|
}
|
|
55577
55688
|
stopKeepAlive() {
|
|
55578
55689
|
if (this.keepAliveProcess) {
|
|
55579
|
-
|
|
55690
|
+
log6.debug("Stopping keep-alive");
|
|
55580
55691
|
this.keepAliveProcess.kill();
|
|
55581
55692
|
this.keepAliveProcess = null;
|
|
55582
55693
|
}
|
|
@@ -55591,18 +55702,18 @@ class KeepAliveManager {
|
|
|
55591
55702
|
detached: false
|
|
55592
55703
|
});
|
|
55593
55704
|
this.keepAliveProcess.on("error", (err) => {
|
|
55594
|
-
|
|
55705
|
+
log6.error(`Failed to start caffeinate: ${err.message}`);
|
|
55595
55706
|
this.keepAliveProcess = null;
|
|
55596
55707
|
});
|
|
55597
55708
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55598
55709
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55599
|
-
|
|
55710
|
+
log6.debug(`caffeinate exited with code ${code}`);
|
|
55600
55711
|
}
|
|
55601
55712
|
this.keepAliveProcess = null;
|
|
55602
55713
|
});
|
|
55603
|
-
|
|
55714
|
+
log6.info("Sleep prevention active (caffeinate)");
|
|
55604
55715
|
} catch (err) {
|
|
55605
|
-
|
|
55716
|
+
log6.error(`Failed to start caffeinate: ${err}`);
|
|
55606
55717
|
}
|
|
55607
55718
|
}
|
|
55608
55719
|
startLinuxKeepAlive() {
|
|
@@ -55615,19 +55726,19 @@ class KeepAliveManager {
|
|
|
55615
55726
|
detached: false
|
|
55616
55727
|
});
|
|
55617
55728
|
this.keepAliveProcess.on("error", (err) => {
|
|
55618
|
-
|
|
55729
|
+
log6.debug(`systemd-inhibit not available: ${err.message}`);
|
|
55619
55730
|
this.keepAliveProcess = null;
|
|
55620
55731
|
this.startLinuxKeepAliveFallback();
|
|
55621
55732
|
});
|
|
55622
55733
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55623
55734
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55624
|
-
|
|
55735
|
+
log6.debug(`systemd-inhibit exited with code ${code}`);
|
|
55625
55736
|
}
|
|
55626
55737
|
this.keepAliveProcess = null;
|
|
55627
55738
|
});
|
|
55628
|
-
|
|
55739
|
+
log6.info("Sleep prevention active (systemd-inhibit)");
|
|
55629
55740
|
} catch (err) {
|
|
55630
|
-
|
|
55741
|
+
log6.debug(`Failed to start systemd-inhibit: ${err}`);
|
|
55631
55742
|
this.startLinuxKeepAliveFallback();
|
|
55632
55743
|
}
|
|
55633
55744
|
}
|
|
@@ -55638,15 +55749,15 @@ class KeepAliveManager {
|
|
|
55638
55749
|
detached: false
|
|
55639
55750
|
});
|
|
55640
55751
|
this.keepAliveProcess.on("error", (err) => {
|
|
55641
|
-
|
|
55752
|
+
log6.warn(`Linux keep-alive fallback not available: ${err.message}`);
|
|
55642
55753
|
this.keepAliveProcess = null;
|
|
55643
55754
|
});
|
|
55644
55755
|
this.keepAliveProcess.on("exit", () => {
|
|
55645
55756
|
this.keepAliveProcess = null;
|
|
55646
55757
|
});
|
|
55647
|
-
|
|
55758
|
+
log6.info("Sleep prevention active (xdg-screensaver)");
|
|
55648
55759
|
} catch (err) {
|
|
55649
|
-
|
|
55760
|
+
log6.warn(`Linux keep-alive not available: ${err}`);
|
|
55650
55761
|
}
|
|
55651
55762
|
}
|
|
55652
55763
|
startWindowsKeepAlive() {
|
|
@@ -55658,18 +55769,18 @@ class KeepAliveManager {
|
|
|
55658
55769
|
windowsHide: true
|
|
55659
55770
|
});
|
|
55660
55771
|
this.keepAliveProcess.on("error", (err) => {
|
|
55661
|
-
|
|
55772
|
+
log6.warn(`Windows keep-alive not available: ${err.message}`);
|
|
55662
55773
|
this.keepAliveProcess = null;
|
|
55663
55774
|
});
|
|
55664
55775
|
this.keepAliveProcess.on("exit", (code) => {
|
|
55665
55776
|
if (code !== null && code !== 0 && this.activeSessionCount > 0) {
|
|
55666
|
-
|
|
55777
|
+
log6.debug(`PowerShell keep-alive exited with code ${code}`);
|
|
55667
55778
|
}
|
|
55668
55779
|
this.keepAliveProcess = null;
|
|
55669
55780
|
});
|
|
55670
|
-
|
|
55781
|
+
log6.info("Sleep prevention active (SetThreadExecutionState)");
|
|
55671
55782
|
} catch (err) {
|
|
55672
|
-
|
|
55783
|
+
log6.warn(`Windows keep-alive not available: ${err}`);
|
|
55673
55784
|
}
|
|
55674
55785
|
}
|
|
55675
55786
|
}
|
|
@@ -55683,7 +55794,7 @@ function formatSponsorFooter(formatter) {
|
|
|
55683
55794
|
}
|
|
55684
55795
|
|
|
55685
55796
|
// src/operations/sticky-message/handler.ts
|
|
55686
|
-
var
|
|
55797
|
+
var log7 = createLogger("sticky");
|
|
55687
55798
|
var botStartedAt = new Date;
|
|
55688
55799
|
var stickyPostIds = new Map;
|
|
55689
55800
|
var needsBump = new Map;
|
|
@@ -55922,10 +56033,10 @@ class Redactor {
|
|
|
55922
56033
|
}
|
|
55923
56034
|
|
|
55924
56035
|
// src/persistence/thread-logger.ts
|
|
55925
|
-
import { homedir as
|
|
55926
|
-
import { join as
|
|
55927
|
-
var
|
|
55928
|
-
var LOGS_BASE_DIR =
|
|
56036
|
+
import { homedir as homedir3 } from "os";
|
|
56037
|
+
import { join as join3, dirname as dirname4 } from "path";
|
|
56038
|
+
var log8 = createLogger("thread-log");
|
|
56039
|
+
var LOGS_BASE_DIR = join3(homedir3(), ".claude-threads", "logs");
|
|
55929
56040
|
|
|
55930
56041
|
// src/operations/bug-report/handler.ts
|
|
55931
56042
|
var piiRedactor = new Redactor({ aggressive: true });
|
|
@@ -55948,7 +56059,7 @@ class UsernameAnonymizer {
|
|
|
55948
56059
|
// src/mcp/decision-bridge.ts
|
|
55949
56060
|
import { createServer, createConnection } from "node:net";
|
|
55950
56061
|
import { tmpdir } from "node:os";
|
|
55951
|
-
import { join as
|
|
56062
|
+
import { join as join4 } from "node:path";
|
|
55952
56063
|
import { randomUUID } from "node:crypto";
|
|
55953
56064
|
import { mkdtempSync } from "node:fs";
|
|
55954
56065
|
import { rm } from "node:fs/promises";
|
|
@@ -55959,8 +56070,8 @@ function bridgeSocketPath() {
|
|
|
55959
56070
|
if (process.platform === "win32") {
|
|
55960
56071
|
return `\\\\.\\pipe\\ctb-${randomUUID()}`;
|
|
55961
56072
|
}
|
|
55962
|
-
const dir = mkdtempSync(
|
|
55963
|
-
return
|
|
56073
|
+
const dir = mkdtempSync(join4(tmpdir(), "ctb-"));
|
|
56074
|
+
return join4(dir, "b.sock");
|
|
55964
56075
|
}
|
|
55965
56076
|
|
|
55966
56077
|
class DecisionBridgeServer {
|
|
@@ -56030,7 +56141,7 @@ class DecisionBridgeServer {
|
|
|
56030
56141
|
});
|
|
56031
56142
|
} catch (err) {
|
|
56032
56143
|
if (process.platform !== "win32") {
|
|
56033
|
-
await rm(
|
|
56144
|
+
await rm(join4(path, ".."), { recursive: true, force: true }).catch(() => {});
|
|
56034
56145
|
}
|
|
56035
56146
|
throw err;
|
|
56036
56147
|
}
|
|
@@ -56043,7 +56154,7 @@ class DecisionBridgeServer {
|
|
|
56043
56154
|
socket.destroy();
|
|
56044
56155
|
await new Promise((resolve4) => this.server.close(() => resolve4()));
|
|
56045
56156
|
if (process.platform !== "win32") {
|
|
56046
|
-
await rm(
|
|
56157
|
+
await rm(join4(this.path, ".."), { recursive: true, force: true }).catch(() => {});
|
|
56047
56158
|
}
|
|
56048
56159
|
}
|
|
56049
56160
|
}
|
|
@@ -56106,7 +56217,7 @@ import { resolve as resolve4, dirname as dirname5 } from "path";
|
|
|
56106
56217
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
56107
56218
|
import { existsSync as existsSync4, readFileSync as readFileSync3, watchFile, unwatchFile, unlinkSync, statSync, readdirSync, writeFileSync } from "fs";
|
|
56108
56219
|
import { tmpdir as tmpdir2 } from "os";
|
|
56109
|
-
import { join as
|
|
56220
|
+
import { join as join5 } from "path";
|
|
56110
56221
|
|
|
56111
56222
|
// src/mcp/outbound-env.ts
|
|
56112
56223
|
var OUTBOUND_ENV = {
|
|
@@ -56197,25 +56308,25 @@ function parseRateLimitEvent(event, now = Date.now()) {
|
|
|
56197
56308
|
}
|
|
56198
56309
|
|
|
56199
56310
|
// src/claude/cli.ts
|
|
56200
|
-
var
|
|
56311
|
+
var log9 = createLogger("claude");
|
|
56201
56312
|
function cleanupBrowserBridgeSockets() {
|
|
56202
56313
|
try {
|
|
56203
56314
|
const tempDir = tmpdir2();
|
|
56204
56315
|
const files = readdirSync(tempDir);
|
|
56205
56316
|
for (const file2 of files) {
|
|
56206
56317
|
if (file2.startsWith("claude-mcp-browser-bridge-")) {
|
|
56207
|
-
const filePath =
|
|
56318
|
+
const filePath = join5(tempDir, file2);
|
|
56208
56319
|
try {
|
|
56209
56320
|
const stats = statSync(filePath);
|
|
56210
56321
|
if (stats.isSocket()) {
|
|
56211
56322
|
unlinkSync(filePath);
|
|
56212
|
-
|
|
56323
|
+
log9.debug(`Removed stale browser bridge socket: ${file2}`);
|
|
56213
56324
|
}
|
|
56214
56325
|
} catch {}
|
|
56215
56326
|
}
|
|
56216
56327
|
}
|
|
56217
56328
|
} catch (err) {
|
|
56218
|
-
|
|
56329
|
+
log9.debug(`Browser bridge cleanup failed: ${err}`);
|
|
56219
56330
|
}
|
|
56220
56331
|
}
|
|
56221
56332
|
function buildClaudeChildEnv(parentEnv, account, opts) {
|
|
@@ -56274,7 +56385,7 @@ function materializeMcpConfig(config3, sessionId, opts = {}) {
|
|
|
56274
56385
|
return { mode: "inline", value: JSON.stringify(config3) };
|
|
56275
56386
|
}
|
|
56276
56387
|
const dir = opts.tmpDirOverride ?? tmpdir2();
|
|
56277
|
-
const path =
|
|
56388
|
+
const path = join5(dir, `claude-threads-mcp-${sessionId ?? process.pid}-${Date.now()}.json`);
|
|
56278
56389
|
writeFileSync(path, JSON.stringify(config3), { mode: 384 });
|
|
56279
56390
|
return { mode: "file", path };
|
|
56280
56391
|
}
|
|
@@ -56457,7 +56568,7 @@ class ClaudeCli extends EventEmitter2 {
|
|
|
56457
56568
|
}
|
|
56458
56569
|
let statusLineCommand;
|
|
56459
56570
|
if (this.options.sessionId) {
|
|
56460
|
-
this.statusFilePath =
|
|
56571
|
+
this.statusFilePath = join5(tmpdir2(), `claude-threads-status-${this.options.sessionId}.json`);
|
|
56461
56572
|
const statusLineWriterPath = this.getStatusLineWriterPath();
|
|
56462
56573
|
const runtime = runtimeForScriptPath(statusLineWriterPath);
|
|
56463
56574
|
statusLineCommand = `${runtime} ${statusLineWriterPath} ${this.options.sessionId}`;
|
|
@@ -56876,6 +56987,27 @@ var COMMAND_REGISTRY = [
|
|
|
56876
56987
|
{ name: "run", description: "Run a routine now, outside its schedule", args: "<n>" }
|
|
56877
56988
|
]
|
|
56878
56989
|
},
|
|
56990
|
+
{
|
|
56991
|
+
command: "watch",
|
|
56992
|
+
description: "Create an event trigger from a natural-language request (confirmed with \uD83D\uDC4D before saving)",
|
|
56993
|
+
args: "<when ..., task>",
|
|
56994
|
+
category: "settings",
|
|
56995
|
+
audience: "user",
|
|
56996
|
+
claudeNotes: "User decisions, not yours"
|
|
56997
|
+
},
|
|
56998
|
+
{
|
|
56999
|
+
command: "watches",
|
|
57000
|
+
description: "List event triggers; pause/resume/delete manage them",
|
|
57001
|
+
args: "[pause|resume|delete <n>]",
|
|
57002
|
+
category: "settings",
|
|
57003
|
+
audience: "user",
|
|
57004
|
+
claudeNotes: "User decisions, not yours",
|
|
57005
|
+
subcommands: [
|
|
57006
|
+
{ name: "pause", description: "Pause a watch", args: "<n>" },
|
|
57007
|
+
{ name: "resume", description: "Resume a paused watch", args: "<n>" },
|
|
57008
|
+
{ name: "delete", description: "Delete a watch", args: "<n>" }
|
|
57009
|
+
]
|
|
57010
|
+
},
|
|
56879
57011
|
{
|
|
56880
57012
|
command: "update",
|
|
56881
57013
|
description: "Show auto-update status",
|
|
@@ -57174,6 +57306,30 @@ var handleRoutines = async (ctx, args) => {
|
|
|
57174
57306
|
await ctx.sessionManager.manageRoutines(ctx.threadId, args, ctx.username);
|
|
57175
57307
|
return { handled: true };
|
|
57176
57308
|
};
|
|
57309
|
+
var handleWatch = async (ctx, args) => {
|
|
57310
|
+
if (ctx.commandContext === "first-message") {
|
|
57311
|
+
return { handled: false };
|
|
57312
|
+
}
|
|
57313
|
+
if (!ctx.isAllowed) {
|
|
57314
|
+
return { handled: true };
|
|
57315
|
+
}
|
|
57316
|
+
if (!args?.trim()) {
|
|
57317
|
+
await ctx.client.createPost(`⚠️ Usage: ${ctx.formatter.formatCode("!watch when <something happens>, <task>")}`, ctx.threadId);
|
|
57318
|
+
return { handled: true };
|
|
57319
|
+
}
|
|
57320
|
+
await ctx.sessionManager.createWatch(ctx.threadId, args, ctx.username);
|
|
57321
|
+
return { handled: true };
|
|
57322
|
+
};
|
|
57323
|
+
var handleWatches = async (ctx, args) => {
|
|
57324
|
+
if (ctx.commandContext === "first-message") {
|
|
57325
|
+
return { handled: false };
|
|
57326
|
+
}
|
|
57327
|
+
if (!ctx.isAllowed) {
|
|
57328
|
+
return { handled: true };
|
|
57329
|
+
}
|
|
57330
|
+
await ctx.sessionManager.manageWatches(ctx.threadId, args, ctx.username);
|
|
57331
|
+
return { handled: true };
|
|
57332
|
+
};
|
|
57177
57333
|
var handleCd = async (ctx, args) => {
|
|
57178
57334
|
if (!args) {
|
|
57179
57335
|
return { handled: false };
|
|
@@ -57367,6 +57523,8 @@ handlers.set("remember", handleRemember);
|
|
|
57367
57523
|
handlers.set("memory", handleMemory);
|
|
57368
57524
|
handlers.set("routine", handleRoutine);
|
|
57369
57525
|
handlers.set("routines", handleRoutines);
|
|
57526
|
+
handlers.set("watch", handleWatch);
|
|
57527
|
+
handlers.set("watches", handleWatches);
|
|
57370
57528
|
handlers.set("cd", handleCd);
|
|
57371
57529
|
handlers.set("permissions", handlePermissions);
|
|
57372
57530
|
handlers.set("mentions", handleMentions);
|
|
@@ -57379,7 +57537,7 @@ handlers.set("compact", createPassthroughHandler("compact"));
|
|
|
57379
57537
|
handlers.set("model", createPassthroughHandler("model"));
|
|
57380
57538
|
handlers.set("effort", createPassthroughHandler("effort"));
|
|
57381
57539
|
// src/commands/system-prompt-generator.ts
|
|
57382
|
-
var
|
|
57540
|
+
var log10 = createLogger("system-prompt");
|
|
57383
57541
|
function formatUserCommand(cmd) {
|
|
57384
57542
|
const cmdStr = cmd.args ? `\`!${cmd.command} ${cmd.args}\`` : `\`!${cmd.command}\``;
|
|
57385
57543
|
const description = cmd.description;
|
|
@@ -57464,7 +57622,7 @@ ${avoidCommands.map((c) => `- \`!${c.command}\` - ${c.reason}`).join(`
|
|
|
57464
57622
|
`.trim();
|
|
57465
57623
|
}
|
|
57466
57624
|
// src/utils/error-handler/index.ts
|
|
57467
|
-
var
|
|
57625
|
+
var log11 = createLogger("error");
|
|
57468
57626
|
|
|
57469
57627
|
// src/utils/session-log.ts
|
|
57470
57628
|
function createSessionLog(baseLog) {
|
|
@@ -57481,45 +57639,64 @@ init_emoji();
|
|
|
57481
57639
|
|
|
57482
57640
|
// src/git/worktree.ts
|
|
57483
57641
|
import * as path from "path";
|
|
57484
|
-
import { homedir as
|
|
57485
|
-
var
|
|
57486
|
-
var WORKTREES_DIR = path.join(
|
|
57487
|
-
var METADATA_STORE_PATH = path.join(
|
|
57642
|
+
import { homedir as homedir4 } from "os";
|
|
57643
|
+
var log12 = createLogger("git-wt");
|
|
57644
|
+
var WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
|
|
57645
|
+
var METADATA_STORE_PATH = path.join(homedir4(), ".claude-threads", "worktree-metadata.json");
|
|
57488
57646
|
|
|
57489
57647
|
// src/operations/post-helpers/index.ts
|
|
57490
|
-
var
|
|
57491
|
-
var sessionLog = createSessionLog(
|
|
57648
|
+
var log13 = createLogger("helpers");
|
|
57649
|
+
var sessionLog = createSessionLog(log13);
|
|
57492
57650
|
|
|
57493
57651
|
// src/claude/quick-query.ts
|
|
57494
|
-
var
|
|
57652
|
+
var log14 = createLogger("query");
|
|
57495
57653
|
|
|
57496
57654
|
// src/operations/suggestions/title.ts
|
|
57497
|
-
var
|
|
57655
|
+
var log15 = createLogger("title");
|
|
57498
57656
|
|
|
57499
57657
|
// src/operations/suggestions/tag.ts
|
|
57500
|
-
var
|
|
57658
|
+
var log16 = createLogger("tags");
|
|
57501
57659
|
|
|
57502
57660
|
// src/operations/context-prompt/handler.ts
|
|
57503
57661
|
init_emoji();
|
|
57504
|
-
var
|
|
57505
|
-
var sessionLog2 = createSessionLog(
|
|
57662
|
+
var log17 = createLogger("context");
|
|
57663
|
+
var sessionLog2 = createSessionLog(log17);
|
|
57506
57664
|
var contextPromptTimeouts = new Map;
|
|
57507
57665
|
var contextPromptFiles = new Map;
|
|
57508
57666
|
// src/memory/store.ts
|
|
57509
57667
|
import { createHash } from "crypto";
|
|
57510
57668
|
import {
|
|
57511
|
-
chmodSync,
|
|
57512
57669
|
existsSync as existsSync5,
|
|
57513
|
-
mkdirSync,
|
|
57670
|
+
mkdirSync as mkdirSync2,
|
|
57514
57671
|
readFileSync as readFileSync4,
|
|
57515
|
-
|
|
57516
|
-
realpathSync,
|
|
57517
|
-
writeFileSync as writeFileSync2
|
|
57672
|
+
realpathSync
|
|
57518
57673
|
} from "fs";
|
|
57519
|
-
import { homedir as
|
|
57520
|
-
import { basename as basename3, dirname as dirname7, join as
|
|
57521
|
-
|
|
57522
|
-
|
|
57674
|
+
import { homedir as homedir5 } from "os";
|
|
57675
|
+
import { basename as basename3, dirname as dirname7, join as join7, sep as sep2 } from "path";
|
|
57676
|
+
|
|
57677
|
+
// src/persistence/atomic-file.ts
|
|
57678
|
+
import { chmodSync as chmodSync2, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
57679
|
+
|
|
57680
|
+
class SerialQueue {
|
|
57681
|
+
tail = Promise.resolve();
|
|
57682
|
+
run(fn) {
|
|
57683
|
+
const next = this.tail.then(fn, fn);
|
|
57684
|
+
this.tail = next.catch(() => {
|
|
57685
|
+
return;
|
|
57686
|
+
});
|
|
57687
|
+
return next;
|
|
57688
|
+
}
|
|
57689
|
+
}
|
|
57690
|
+
function writeFileAtomic(file2, content) {
|
|
57691
|
+
const tempFile = `${file2}.tmp`;
|
|
57692
|
+
writeFileSync2(tempFile, content, { encoding: "utf-8", mode: 384 });
|
|
57693
|
+
renameSync(tempFile, file2);
|
|
57694
|
+
chmodSync2(file2, 384);
|
|
57695
|
+
}
|
|
57696
|
+
|
|
57697
|
+
// src/memory/store.ts
|
|
57698
|
+
var log18 = createLogger("memory");
|
|
57699
|
+
var DEFAULT_ROOT = join7(homedir5(), ".config", "claude-threads", "memory");
|
|
57523
57700
|
var CHANNEL_BLOCK_MAX_LINES = 200;
|
|
57524
57701
|
var CHANNEL_BLOCK_MAX_BYTES = 25 * 1024;
|
|
57525
57702
|
var CHANNEL_FILE_MAX_ENTRIES = 400;
|
|
@@ -57562,10 +57739,10 @@ class MemoryStore {
|
|
|
57562
57739
|
return this.root;
|
|
57563
57740
|
}
|
|
57564
57741
|
channelMemoryPath(platformId) {
|
|
57565
|
-
return
|
|
57742
|
+
return join7(this.root, platformSegment(platformId), "channel", "MEMORY.md");
|
|
57566
57743
|
}
|
|
57567
57744
|
repoMemoryDir(platformId, repoKey) {
|
|
57568
|
-
const dir =
|
|
57745
|
+
const dir = join7(this.root, platformSegment(platformId), "repos", repoKey);
|
|
57569
57746
|
this.ensureDir(dir);
|
|
57570
57747
|
return dir;
|
|
57571
57748
|
}
|
|
@@ -57612,7 +57789,7 @@ class MemoryStore {
|
|
|
57612
57789
|
if (result.added.length > 0) {
|
|
57613
57790
|
this.enforceFileCap(lines);
|
|
57614
57791
|
this.writeLines(platformId, lines);
|
|
57615
|
-
|
|
57792
|
+
log18.debug(`Channel memory for ${platformId}: +${result.added.length} entries` + (result.duplicates.length ? ` (${result.duplicates.length} duplicates skipped)` : ""));
|
|
57616
57793
|
}
|
|
57617
57794
|
return result;
|
|
57618
57795
|
});
|
|
@@ -57651,14 +57828,14 @@ class MemoryStore {
|
|
|
57651
57828
|
}
|
|
57652
57829
|
lines.splice(target.lineIndex, 1);
|
|
57653
57830
|
this.writeLines(platformId, lines);
|
|
57654
|
-
|
|
57831
|
+
log18.debug(`Channel memory for ${platformId}: removed one entry`);
|
|
57655
57832
|
return { ok: true, removed: target.entry };
|
|
57656
57833
|
});
|
|
57657
57834
|
}
|
|
57658
57835
|
clearChannel(platformId) {
|
|
57659
57836
|
return this.runExclusive(platformId, () => {
|
|
57660
57837
|
this.writeLines(platformId, []);
|
|
57661
|
-
|
|
57838
|
+
log18.debug(`Channel memory for ${platformId}: cleared`);
|
|
57662
57839
|
});
|
|
57663
57840
|
}
|
|
57664
57841
|
buildChannelMemoryBlock(platformId) {
|
|
@@ -57666,7 +57843,7 @@ class MemoryStore {
|
|
|
57666
57843
|
try {
|
|
57667
57844
|
lines = this.loadLines(platformId);
|
|
57668
57845
|
} catch (err) {
|
|
57669
|
-
|
|
57846
|
+
log18.warn(`Failed to read channel memory for ${platformId}: ${err.message}`);
|
|
57670
57847
|
return null;
|
|
57671
57848
|
}
|
|
57672
57849
|
if (lines.length === 0)
|
|
@@ -57690,12 +57867,12 @@ class MemoryStore {
|
|
|
57690
57867
|
_(older entries omitted — \`!memory\` shows all)_` : rendered;
|
|
57691
57868
|
}
|
|
57692
57869
|
runExclusive(platformId, fn) {
|
|
57693
|
-
|
|
57694
|
-
|
|
57695
|
-
|
|
57696
|
-
|
|
57697
|
-
}
|
|
57698
|
-
return
|
|
57870
|
+
let queue = this.locks.get(platformId);
|
|
57871
|
+
if (!queue) {
|
|
57872
|
+
queue = new SerialQueue;
|
|
57873
|
+
this.locks.set(platformId, queue);
|
|
57874
|
+
}
|
|
57875
|
+
return queue.run(fn);
|
|
57699
57876
|
}
|
|
57700
57877
|
loadLines(platformId) {
|
|
57701
57878
|
const file2 = this.channelMemoryPath(platformId);
|
|
@@ -57738,24 +57915,132 @@ _(older entries omitted — \`!memory\` shows all)_` : rendered;
|
|
|
57738
57915
|
const content = [FILE_HEADER, ...lines.map((l) => l.raw)].join(`
|
|
57739
57916
|
`) + `
|
|
57740
57917
|
`;
|
|
57741
|
-
|
|
57742
|
-
writeFileSync2(tempFile, content, { encoding: "utf-8", mode: 384 });
|
|
57743
|
-
renameSync(tempFile, file2);
|
|
57744
|
-
chmodSync(file2, 384);
|
|
57918
|
+
writeFileAtomic(file2, content);
|
|
57745
57919
|
}
|
|
57746
57920
|
ensureDir(dir) {
|
|
57747
57921
|
if (!existsSync5(dir)) {
|
|
57748
|
-
|
|
57922
|
+
mkdirSync2(dir, { recursive: true, mode: 448 });
|
|
57749
57923
|
}
|
|
57750
57924
|
}
|
|
57751
57925
|
}
|
|
57752
57926
|
|
|
57753
57927
|
// src/memory/distiller.ts
|
|
57754
|
-
var
|
|
57928
|
+
var log19 = createLogger("memory");
|
|
57929
|
+
|
|
57930
|
+
// src/session/registry.ts
|
|
57931
|
+
function compositeSessionId(platformId, threadId) {
|
|
57932
|
+
return `${platformId}:${threadId}`;
|
|
57933
|
+
}
|
|
57934
|
+
|
|
57935
|
+
class SessionRegistry {
|
|
57936
|
+
sessions = new Map;
|
|
57937
|
+
postIndex = new Map;
|
|
57938
|
+
sessionStore;
|
|
57939
|
+
constructor(sessionStore) {
|
|
57940
|
+
this.sessionStore = sessionStore;
|
|
57941
|
+
}
|
|
57942
|
+
getSessionId(platformId, threadId) {
|
|
57943
|
+
return compositeSessionId(platformId, threadId);
|
|
57944
|
+
}
|
|
57945
|
+
parseSessionId(sessionId) {
|
|
57946
|
+
const colonIndex = sessionId.indexOf(":");
|
|
57947
|
+
if (colonIndex === -1)
|
|
57948
|
+
return null;
|
|
57949
|
+
return {
|
|
57950
|
+
platformId: sessionId.substring(0, colonIndex),
|
|
57951
|
+
threadId: sessionId.substring(colonIndex + 1)
|
|
57952
|
+
};
|
|
57953
|
+
}
|
|
57954
|
+
find(platformId, threadId) {
|
|
57955
|
+
return this.sessions.get(this.getSessionId(platformId, threadId));
|
|
57956
|
+
}
|
|
57957
|
+
findByThreadId(threadId) {
|
|
57958
|
+
for (const session of this.sessions.values()) {
|
|
57959
|
+
if (session.threadId === threadId) {
|
|
57960
|
+
return session;
|
|
57961
|
+
}
|
|
57962
|
+
}
|
|
57963
|
+
return;
|
|
57964
|
+
}
|
|
57965
|
+
findByPost(postId) {
|
|
57966
|
+
const threadId = this.postIndex.get(postId);
|
|
57967
|
+
if (!threadId)
|
|
57968
|
+
return;
|
|
57969
|
+
return this.findByThreadId(threadId);
|
|
57970
|
+
}
|
|
57971
|
+
get(sessionId) {
|
|
57972
|
+
return this.sessions.get(sessionId);
|
|
57973
|
+
}
|
|
57974
|
+
has(platformId, threadId) {
|
|
57975
|
+
return this.sessions.has(this.getSessionId(platformId, threadId));
|
|
57976
|
+
}
|
|
57977
|
+
isActiveThread(threadId) {
|
|
57978
|
+
return this.findByThreadId(threadId) !== undefined;
|
|
57979
|
+
}
|
|
57980
|
+
register(session) {
|
|
57981
|
+
this.sessions.set(session.sessionId, session);
|
|
57982
|
+
}
|
|
57983
|
+
unregister(sessionId) {
|
|
57984
|
+
this.sessions.delete(sessionId);
|
|
57985
|
+
}
|
|
57986
|
+
registerPost(postId, threadId) {
|
|
57987
|
+
this.postIndex.set(postId, threadId);
|
|
57988
|
+
}
|
|
57989
|
+
unregisterPost(postId) {
|
|
57990
|
+
this.postIndex.delete(postId);
|
|
57991
|
+
}
|
|
57992
|
+
clearPostsForThread(threadId) {
|
|
57993
|
+
for (const [postId, tid] of this.postIndex.entries()) {
|
|
57994
|
+
if (tid === threadId) {
|
|
57995
|
+
this.postIndex.delete(postId);
|
|
57996
|
+
}
|
|
57997
|
+
}
|
|
57998
|
+
}
|
|
57999
|
+
getAll() {
|
|
58000
|
+
return Array.from(this.sessions.values());
|
|
58001
|
+
}
|
|
58002
|
+
getActiveThreadIds() {
|
|
58003
|
+
return Array.from(this.sessions.values()).map((s) => s.threadId);
|
|
58004
|
+
}
|
|
58005
|
+
get size() {
|
|
58006
|
+
return this.sessions.size;
|
|
58007
|
+
}
|
|
58008
|
+
getForPlatform(platformId) {
|
|
58009
|
+
return Array.from(this.sessions.values()).filter((s) => s.sessionId.startsWith(`${platformId}:`));
|
|
58010
|
+
}
|
|
58011
|
+
hasPaused(platformId, threadId) {
|
|
58012
|
+
return this.sessionStore.findByThread(platformId, threadId) !== undefined;
|
|
58013
|
+
}
|
|
58014
|
+
getPersisted(platformId, threadId) {
|
|
58015
|
+
return this.sessionStore.findByThread(platformId, threadId);
|
|
58016
|
+
}
|
|
58017
|
+
getPersistedByThreadId(threadId) {
|
|
58018
|
+
return this.sessionStore.findByThreadIdAnyState(threadId);
|
|
58019
|
+
}
|
|
58020
|
+
getSessionStore() {
|
|
58021
|
+
return this.sessionStore;
|
|
58022
|
+
}
|
|
58023
|
+
hasById(sessionId) {
|
|
58024
|
+
return this.sessions.has(sessionId);
|
|
58025
|
+
}
|
|
58026
|
+
clear() {
|
|
58027
|
+
this.sessions.clear();
|
|
58028
|
+
this.postIndex.clear();
|
|
58029
|
+
}
|
|
58030
|
+
getThreadIdForPost(postId) {
|
|
58031
|
+
return this.postIndex.get(postId);
|
|
58032
|
+
}
|
|
58033
|
+
getSessions() {
|
|
58034
|
+
return this.sessions;
|
|
58035
|
+
}
|
|
58036
|
+
getPostIndex() {
|
|
58037
|
+
return this.postIndex;
|
|
58038
|
+
}
|
|
58039
|
+
}
|
|
57755
58040
|
|
|
57756
58041
|
// src/session/lifecycle.ts
|
|
57757
|
-
var
|
|
57758
|
-
var sessionLog3 = createSessionLog(
|
|
58042
|
+
var log20 = createLogger("lifecycle");
|
|
58043
|
+
var sessionLog3 = createSessionLog(log20);
|
|
57759
58044
|
var _inFlightSessionStarts = new Map;
|
|
57760
58045
|
var CHAT_PLATFORM_PROMPT = generateChatPlatformPrompt();
|
|
57761
58046
|
// src/update-notifier.ts
|
|
@@ -57765,206 +58050,181 @@ var import_semver2 = __toESM(require_semver2(), 1);
|
|
|
57765
58050
|
init_emoji();
|
|
57766
58051
|
|
|
57767
58052
|
// src/persistence/github-emails-store.ts
|
|
57768
|
-
import { homedir as homedir5 } from "os";
|
|
57769
|
-
import { join as join7 } from "path";
|
|
57770
|
-
var log20 = createLogger("gh-emails");
|
|
57771
|
-
var DEFAULT_CONFIG_DIR = join7(homedir5(), ".config", "claude-threads");
|
|
57772
|
-
var DEFAULT_FILE = join7(DEFAULT_CONFIG_DIR, "github-emails.yaml");
|
|
57773
|
-
|
|
57774
|
-
// src/persistence/routines-store.ts
|
|
57775
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync5 } from "fs";
|
|
57776
58053
|
import { homedir as homedir6 } from "os";
|
|
57777
58054
|
import { join as join8 } from "path";
|
|
57778
|
-
|
|
57779
|
-
|
|
57780
|
-
|
|
57781
|
-
import { chmodSync as chmodSync2, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
57782
|
-
|
|
57783
|
-
class SerialQueue {
|
|
57784
|
-
tail = Promise.resolve();
|
|
57785
|
-
run(fn) {
|
|
57786
|
-
const next = this.tail.then(fn, fn);
|
|
57787
|
-
this.tail = next.catch(() => {
|
|
57788
|
-
return;
|
|
57789
|
-
});
|
|
57790
|
-
return next;
|
|
57791
|
-
}
|
|
57792
|
-
}
|
|
57793
|
-
function writeFileAtomic(file2, content) {
|
|
57794
|
-
const tempFile = `${file2}.tmp`;
|
|
57795
|
-
writeFileSync3(tempFile, content, { encoding: "utf-8", mode: 384 });
|
|
57796
|
-
renameSync2(tempFile, file2);
|
|
57797
|
-
chmodSync2(file2, 384);
|
|
57798
|
-
}
|
|
58055
|
+
var log21 = createLogger("gh-emails");
|
|
58056
|
+
var DEFAULT_CONFIG_DIR = join8(homedir6(), ".config", "claude-threads");
|
|
58057
|
+
var DEFAULT_FILE = join8(DEFAULT_CONFIG_DIR, "github-emails.yaml");
|
|
57799
58058
|
|
|
57800
58059
|
// src/persistence/routines-store.ts
|
|
57801
|
-
|
|
57802
|
-
|
|
57803
|
-
|
|
58060
|
+
import { join as join10 } from "path";
|
|
58061
|
+
|
|
58062
|
+
// src/persistence/platform-list-store.ts
|
|
58063
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
58064
|
+
import { homedir as homedir7 } from "os";
|
|
58065
|
+
import { join as join9 } from "path";
|
|
58066
|
+
var STORES_CONFIG_DIR = join9(homedir7(), ".config", "claude-threads");
|
|
57804
58067
|
var STORE_VERSION = 1;
|
|
57805
|
-
|
|
57806
|
-
|
|
57807
|
-
var TIME_RE = /^([01]\d|2[0-3]):[0-5]\d$/;
|
|
57808
|
-
function isValidTimezone(tz) {
|
|
57809
|
-
if (typeof tz !== "string" || !tz)
|
|
57810
|
-
return false;
|
|
57811
|
-
try {
|
|
57812
|
-
new Intl.DateTimeFormat("en-US", { timeZone: tz });
|
|
57813
|
-
return true;
|
|
57814
|
-
} catch {
|
|
57815
|
-
return false;
|
|
57816
|
-
}
|
|
57817
|
-
}
|
|
57818
|
-
function validateSchedule(schedule) {
|
|
57819
|
-
if (!SCHEDULE_PRESETS.includes(schedule.preset)) {
|
|
57820
|
-
return `unknown preset "${String(schedule.preset)}" (expected ${SCHEDULE_PRESETS.join("/")})`;
|
|
57821
|
-
}
|
|
57822
|
-
if (!isValidTimezone(schedule.timezone)) {
|
|
57823
|
-
return `invalid timezone "${String(schedule.timezone)}"`;
|
|
57824
|
-
}
|
|
57825
|
-
if (schedule.preset === "hourly") {
|
|
57826
|
-
return null;
|
|
57827
|
-
}
|
|
57828
|
-
if (!schedule.time || !TIME_RE.test(schedule.time)) {
|
|
57829
|
-
return `invalid time "${String(schedule.time)}" (expected HH:MM, 24h)`;
|
|
57830
|
-
}
|
|
57831
|
-
if (schedule.preset === "weekly") {
|
|
57832
|
-
const weekday = schedule.weekday;
|
|
57833
|
-
if (typeof weekday !== "number" || !Number.isInteger(weekday) || weekday < 1 || weekday > 7) {
|
|
57834
|
-
return `invalid weekday "${String(weekday)}" (expected 1=Mon … 7=Sun)`;
|
|
57835
|
-
}
|
|
57836
|
-
}
|
|
57837
|
-
return null;
|
|
57838
|
-
}
|
|
57839
|
-
class RoutinesStore {
|
|
58068
|
+
|
|
58069
|
+
class PlatformListStore {
|
|
57840
58070
|
file;
|
|
57841
58071
|
configDir;
|
|
57842
58072
|
queue = new SerialQueue;
|
|
57843
|
-
|
|
57844
|
-
|
|
57845
|
-
|
|
57846
|
-
|
|
57847
|
-
|
|
58073
|
+
collectionKey;
|
|
58074
|
+
cache = null;
|
|
58075
|
+
constructor(collectionKey, defaultFile, filePath) {
|
|
58076
|
+
this.collectionKey = collectionKey;
|
|
58077
|
+
if (filePath) {
|
|
58078
|
+
this.file = filePath;
|
|
58079
|
+
this.configDir = join9(filePath, "..");
|
|
57848
58080
|
} else {
|
|
57849
|
-
this.file =
|
|
57850
|
-
this.configDir =
|
|
57851
|
-
}
|
|
57852
|
-
if (!existsSync6(this.configDir)) {
|
|
57853
|
-
mkdirSync2(this.configDir, { recursive: true, mode: 448 });
|
|
58081
|
+
this.file = defaultFile;
|
|
58082
|
+
this.configDir = STORES_CONFIG_DIR;
|
|
57854
58083
|
}
|
|
58084
|
+
mkdirSync3(this.configDir, { recursive: true, mode: 448 });
|
|
57855
58085
|
}
|
|
57856
58086
|
list(platformId) {
|
|
57857
|
-
return this.loadRaw().
|
|
58087
|
+
return structuredClone(this.loadRaw().items[platformId] ?? []);
|
|
57858
58088
|
}
|
|
57859
58089
|
get(platformId, id) {
|
|
57860
|
-
|
|
58090
|
+
const item = (this.loadRaw().items[platformId] ?? []).find((i) => i.id === id);
|
|
58091
|
+
return item === undefined ? undefined : structuredClone(item);
|
|
57861
58092
|
}
|
|
57862
|
-
|
|
58093
|
+
addItem(platformId, max, capNoun, build) {
|
|
57863
58094
|
return this.runExclusive(() => {
|
|
57864
|
-
const
|
|
57865
|
-
if (
|
|
57866
|
-
return { ok: false, error:
|
|
57867
|
-
const
|
|
57868
|
-
const
|
|
57869
|
-
if (
|
|
57870
|
-
return { ok: false, error:
|
|
57871
|
-
|
|
57872
|
-
|
|
57873
|
-
if (existing.length >= maxRoutines) {
|
|
57874
|
-
return { ok: false, error: `routine limit reached (${maxRoutines}); delete one first` };
|
|
57875
|
-
}
|
|
57876
|
-
const full = {
|
|
57877
|
-
...routine,
|
|
57878
|
-
name,
|
|
57879
|
-
prompt,
|
|
57880
|
-
id: randomUUID2().slice(0, 8),
|
|
57881
|
-
createdAt: new Date().toISOString(),
|
|
57882
|
-
enabled: true,
|
|
57883
|
-
consecutiveFailures: 0
|
|
57884
|
-
};
|
|
57885
|
-
data.routines[platformId] = [...existing, full];
|
|
58095
|
+
const built = build();
|
|
58096
|
+
if (typeof built === "string")
|
|
58097
|
+
return { ok: false, error: built };
|
|
58098
|
+
const data = this.loadRaw(true);
|
|
58099
|
+
const existing = data.items[platformId] ?? [];
|
|
58100
|
+
if (existing.length >= max) {
|
|
58101
|
+
return { ok: false, error: `${capNoun} limit reached (${max}); delete one first` };
|
|
58102
|
+
}
|
|
58103
|
+
data.items[platformId] = [...existing, built];
|
|
57886
58104
|
this.writeAtomic(data);
|
|
57887
|
-
|
|
57888
|
-
return { ok: true, routine: full };
|
|
58105
|
+
return { ok: true, item: structuredClone(built) };
|
|
57889
58106
|
});
|
|
57890
58107
|
}
|
|
57891
58108
|
update(platformId, id, patch) {
|
|
57892
58109
|
return this.runExclusive(() => {
|
|
57893
|
-
const data = this.loadRaw();
|
|
57894
|
-
const
|
|
57895
|
-
const idx =
|
|
58110
|
+
const data = this.loadRaw(true);
|
|
58111
|
+
const items = data.items[platformId] ?? [];
|
|
58112
|
+
const idx = items.findIndex((item) => item.id === id);
|
|
57896
58113
|
if (idx < 0)
|
|
57897
58114
|
return;
|
|
57898
|
-
|
|
58115
|
+
items[idx] = { ...items[idx], ...patch };
|
|
57899
58116
|
this.writeAtomic(data);
|
|
57900
|
-
return
|
|
58117
|
+
return structuredClone(items[idx]);
|
|
57901
58118
|
});
|
|
57902
58119
|
}
|
|
57903
58120
|
remove(platformId, id) {
|
|
57904
58121
|
return this.runExclusive(() => {
|
|
57905
|
-
const data = this.loadRaw();
|
|
57906
|
-
const
|
|
57907
|
-
const idx =
|
|
58122
|
+
const data = this.loadRaw(true);
|
|
58123
|
+
const items = data.items[platformId] ?? [];
|
|
58124
|
+
const idx = items.findIndex((item) => item.id === id);
|
|
57908
58125
|
if (idx < 0)
|
|
57909
58126
|
return;
|
|
57910
|
-
const [removed] =
|
|
57911
|
-
if (
|
|
57912
|
-
delete data.
|
|
58127
|
+
const [removed] = items.splice(idx, 1);
|
|
58128
|
+
if (items.length === 0)
|
|
58129
|
+
delete data.items[platformId];
|
|
57913
58130
|
this.writeAtomic(data);
|
|
57914
|
-
|
|
58131
|
+
this.onRemoved(platformId, removed);
|
|
57915
58132
|
return removed;
|
|
57916
58133
|
});
|
|
57917
58134
|
}
|
|
58135
|
+
onRemoved(_platformId, _item) {}
|
|
57918
58136
|
runExclusive(fn) {
|
|
57919
58137
|
return this.queue.run(fn);
|
|
57920
58138
|
}
|
|
57921
|
-
loadRaw() {
|
|
58139
|
+
loadRaw(forWrite = false) {
|
|
57922
58140
|
if (!existsSync6(this.file)) {
|
|
57923
|
-
|
|
58141
|
+
this.cache = null;
|
|
58142
|
+
return { version: STORE_VERSION, items: {} };
|
|
57924
58143
|
}
|
|
57925
58144
|
try {
|
|
57926
|
-
const
|
|
57927
|
-
if (
|
|
57928
|
-
return
|
|
57929
|
-
}
|
|
57930
|
-
const routines = parsed.routines && typeof parsed.routines === "object" ? parsed.routines : {};
|
|
57931
|
-
for (const list of Object.values(routines)) {
|
|
57932
|
-
for (const r of list) {
|
|
57933
|
-
r.enabled = r.enabled ?? true;
|
|
57934
|
-
r.consecutiveFailures = r.consecutiveFailures ?? 0;
|
|
57935
|
-
}
|
|
58145
|
+
const stat = statSync2(this.file);
|
|
58146
|
+
if (this.cache && this.cache.mtimeMs === stat.mtimeMs && this.cache.size === stat.size) {
|
|
58147
|
+
return this.cache.data;
|
|
57936
58148
|
}
|
|
57937
|
-
|
|
58149
|
+
const parsed = yaml.load(readFileSync5(this.file, "utf-8"));
|
|
58150
|
+
const rawItems = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed[this.collectionKey] : undefined;
|
|
58151
|
+
if (rawItems !== null && (rawItems === undefined || typeof rawItems !== "object" || Array.isArray(rawItems))) {
|
|
58152
|
+
this.cache = null;
|
|
58153
|
+
const problem = `unexpected shape (missing or non-map '${this.collectionKey}' key)`;
|
|
58154
|
+
if (forWrite) {
|
|
58155
|
+
throw new Error(`refusing to write over unreadable ${this.file}: ${problem}`);
|
|
58156
|
+
}
|
|
58157
|
+
this.warn(`Failed to read ${this.file}: ${problem} — starting empty`);
|
|
58158
|
+
return { version: STORE_VERSION, items: {} };
|
|
58159
|
+
}
|
|
58160
|
+
const items = rawItems ?? {};
|
|
58161
|
+
for (const list of Object.values(items)) {
|
|
58162
|
+
for (const item of list)
|
|
58163
|
+
this.applyItemDefaults(item);
|
|
58164
|
+
}
|
|
58165
|
+
const data = { version: parsed?.version ?? STORE_VERSION, items };
|
|
58166
|
+
this.cache = { mtimeMs: stat.mtimeMs, size: stat.size, data };
|
|
58167
|
+
return data;
|
|
57938
58168
|
} catch (err) {
|
|
57939
|
-
|
|
57940
|
-
|
|
58169
|
+
this.cache = null;
|
|
58170
|
+
if (forWrite) {
|
|
58171
|
+
throw new Error(`refusing to write over unreadable ${this.file}: ${err.message}`, { cause: err });
|
|
58172
|
+
}
|
|
58173
|
+
this.warn(`Failed to read ${this.file}: ${err.message} — starting empty`);
|
|
58174
|
+
return { version: STORE_VERSION, items: {} };
|
|
57941
58175
|
}
|
|
57942
58176
|
}
|
|
57943
58177
|
writeAtomic(data) {
|
|
57944
|
-
|
|
58178
|
+
try {
|
|
58179
|
+
this.persistFile(yaml.dump({ version: data.version, [this.collectionKey]: data.items }, { sortKeys: true, lineWidth: -1 }));
|
|
58180
|
+
} catch (err) {
|
|
58181
|
+
this.cache = null;
|
|
58182
|
+
throw err;
|
|
58183
|
+
}
|
|
58184
|
+
try {
|
|
58185
|
+
const stat = statSync2(this.file);
|
|
58186
|
+
this.cache = { mtimeMs: stat.mtimeMs, size: stat.size, data };
|
|
58187
|
+
} catch {
|
|
58188
|
+
this.cache = null;
|
|
58189
|
+
}
|
|
58190
|
+
}
|
|
58191
|
+
persistFile(content) {
|
|
58192
|
+
writeFileAtomic(this.file, content);
|
|
57945
58193
|
}
|
|
57946
58194
|
}
|
|
57947
58195
|
|
|
57948
|
-
// src/routines
|
|
58196
|
+
// src/persistence/routines-store.ts
|
|
57949
58197
|
var log22 = createLogger("routines");
|
|
58198
|
+
var DEFAULT_FILE2 = join10(STORES_CONFIG_DIR, "routines.yaml");
|
|
58199
|
+
|
|
58200
|
+
// src/routines/parser.ts
|
|
58201
|
+
var log23 = createLogger("routines");
|
|
58202
|
+
|
|
58203
|
+
// src/persistence/watches-store.ts
|
|
58204
|
+
import { join as join11 } from "path";
|
|
58205
|
+
var log24 = createLogger("watches");
|
|
58206
|
+
var DEFAULT_FILE3 = join11(STORES_CONFIG_DIR, "watches.yaml");
|
|
58207
|
+
|
|
58208
|
+
// src/watches/parser.ts
|
|
58209
|
+
var log25 = createLogger("watches");
|
|
57950
58210
|
|
|
57951
58211
|
// src/operations/commands/handler.ts
|
|
57952
|
-
var
|
|
57953
|
-
var sessionLog4 = createSessionLog(
|
|
58212
|
+
var log26 = createLogger("commands");
|
|
58213
|
+
var sessionLog4 = createSessionLog(log26);
|
|
57954
58214
|
// src/operations/suggestions/branch.ts
|
|
57955
58215
|
import { exec as exec2 } from "child_process";
|
|
57956
58216
|
import { promisify as promisify2 } from "util";
|
|
57957
58217
|
var execAsync2 = promisify2(exec2);
|
|
57958
|
-
var
|
|
58218
|
+
var log27 = createLogger("branch");
|
|
57959
58219
|
|
|
57960
58220
|
// src/operations/worktree/handler.ts
|
|
57961
|
-
var
|
|
57962
|
-
var sessionLog5 = createSessionLog(
|
|
58221
|
+
var log28 = createLogger("worktree");
|
|
58222
|
+
var sessionLog5 = createSessionLog(log28);
|
|
57963
58223
|
// src/operations/events/handler.ts
|
|
57964
|
-
var
|
|
57965
|
-
var sessionLog6 = createSessionLog(
|
|
58224
|
+
var log29 = createLogger("events");
|
|
58225
|
+
var sessionLog6 = createSessionLog(log29);
|
|
57966
58226
|
// src/operations/monitor/handler.ts
|
|
57967
|
-
var
|
|
58227
|
+
var log30 = createLogger("monitor");
|
|
57968
58228
|
var DEFAULT_INTERVAL_MS = 60 * 1000;
|
|
57969
58229
|
// src/utils/websocket.ts
|
|
57970
58230
|
var WS;
|
|
@@ -58046,7 +58306,7 @@ ${code}
|
|
|
58046
58306
|
|
|
58047
58307
|
// src/platform/mattermost/upload.ts
|
|
58048
58308
|
import { readFile } from "fs/promises";
|
|
58049
|
-
var
|
|
58309
|
+
var log31 = createLogger("mm-upload");
|
|
58050
58310
|
async function uploadFileMattermost(args) {
|
|
58051
58311
|
const { url: url2, token, channelId, threadId, filePath, filename, caption } = args;
|
|
58052
58312
|
const buffer = await readFile(filePath);
|
|
@@ -58054,7 +58314,7 @@ async function uploadFileMattermost(args) {
|
|
|
58054
58314
|
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
58055
58315
|
const formData = new FormData;
|
|
58056
58316
|
formData.append("files", new Blob([arrayBuffer]), filename);
|
|
58057
|
-
|
|
58317
|
+
log31.debug(`POST /files (${buffer.length} bytes, ${filename})`);
|
|
58058
58318
|
const uploadResponse = await fetch(uploadUrl, {
|
|
58059
58319
|
method: "POST",
|
|
58060
58320
|
headers: {
|
|
@@ -58078,7 +58338,7 @@ async function uploadFileMattermost(args) {
|
|
|
58078
58338
|
root_id: resolvePostThreadId(threadId),
|
|
58079
58339
|
file_ids: [fileInfo.id]
|
|
58080
58340
|
};
|
|
58081
|
-
|
|
58341
|
+
log31.debug(`POST /posts (file_ids=[${fileInfo.id}])`);
|
|
58082
58342
|
const postResponse = await fetch(postUrl, {
|
|
58083
58343
|
method: "POST",
|
|
58084
58344
|
headers: {
|
|
@@ -58572,7 +58832,7 @@ ${code}
|
|
|
58572
58832
|
|
|
58573
58833
|
// src/platform/slack/upload.ts
|
|
58574
58834
|
import { readFile as readFile2 } from "fs/promises";
|
|
58575
|
-
var
|
|
58835
|
+
var log32 = createLogger("slack-upload");
|
|
58576
58836
|
var DEFAULT_API_URL = "https://slack.com/api";
|
|
58577
58837
|
async function uploadFileSlack(args) {
|
|
58578
58838
|
const { botToken, channelId, threadTs, filePath, filename, caption } = args;
|
|
@@ -58580,7 +58840,7 @@ async function uploadFileSlack(args) {
|
|
|
58580
58840
|
const buffer = await readFile2(filePath);
|
|
58581
58841
|
const params = new URLSearchParams({ filename, length: String(buffer.length) });
|
|
58582
58842
|
const step1Url = `${apiUrl}/files.getUploadURLExternal?${params.toString()}`;
|
|
58583
|
-
|
|
58843
|
+
log32.debug(`GET files.getUploadURLExternal (${buffer.length} bytes, ${filename})`);
|
|
58584
58844
|
const step1Response = await fetch(step1Url, {
|
|
58585
58845
|
method: "GET",
|
|
58586
58846
|
headers: {
|
|
@@ -58598,7 +58858,7 @@ async function uploadFileSlack(args) {
|
|
|
58598
58858
|
const uploadUrl = step1Data.upload_url;
|
|
58599
58859
|
const fileId = step1Data.file_id;
|
|
58600
58860
|
const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
58601
|
-
|
|
58861
|
+
log32.debug(`POST <upload_url>`);
|
|
58602
58862
|
const step2Response = await fetch(uploadUrl, {
|
|
58603
58863
|
method: "POST",
|
|
58604
58864
|
headers: {
|
|
@@ -58618,7 +58878,7 @@ async function uploadFileSlack(args) {
|
|
|
58618
58878
|
if (caption !== undefined) {
|
|
58619
58879
|
step3Body.initial_comment = caption;
|
|
58620
58880
|
}
|
|
58621
|
-
|
|
58881
|
+
log32.debug(`POST files.completeUploadExternal (file_id=${fileId}, thread_ts=${threadTs})`);
|
|
58622
58882
|
const step3Response = await fetch(`${apiUrl}/files.completeUploadExternal`, {
|
|
58623
58883
|
method: "POST",
|
|
58624
58884
|
headers: {
|
|
@@ -58636,7 +58896,7 @@ async function uploadFileSlack(args) {
|
|
|
58636
58896
|
throw new Error(`Slack completeUploadExternal error: ${step3Data.error || "unknown"}`);
|
|
58637
58897
|
}
|
|
58638
58898
|
if (!step3Data.ts) {
|
|
58639
|
-
|
|
58899
|
+
log32.warn(`Slack completeUploadExternal returned no ts; using fileId ${fileId} as postId. ` + `Do not use this id for updatePost/addReaction.`);
|
|
58640
58900
|
}
|
|
58641
58901
|
return { fileId, postId: step3Data.ts ?? fileId };
|
|
58642
58902
|
}
|