claude-threads 1.4.3 → 1.4.5
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 +10 -0
- package/dist/index.js +536 -392
- package/dist/mcp/permission-server.js +23 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.4.5] - 2026-01-18
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Worktree switch with prompt** - `!worktree switch branch prompt text` now switches to existing worktree and starts session with the prompt (#242)
|
|
12
|
+
|
|
13
|
+
## [1.4.4] - 2026-01-18
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- **Worktree commands in root messages** - `!worktree list` now works without a session, `!worktree branch-name` now starts session without requiring additional prompt (#241)
|
|
17
|
+
|
|
8
18
|
## [1.4.3] - 2026-01-18
|
|
9
19
|
|
|
10
20
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -8822,6 +8822,80 @@ var require_semver2 = __commonJS((exports, module) => {
|
|
|
8822
8822
|
};
|
|
8823
8823
|
});
|
|
8824
8824
|
|
|
8825
|
+
// src/utils/logger.ts
|
|
8826
|
+
function setLogHandler(handler) {
|
|
8827
|
+
globalLogHandler = handler;
|
|
8828
|
+
}
|
|
8829
|
+
function createLogger(component, useStderr = false, sessionId) {
|
|
8830
|
+
const isDebug = () => process.env.DEBUG === "1";
|
|
8831
|
+
const consoleLog = useStderr ? console.error : console.log;
|
|
8832
|
+
const paddedComponent = component.length > COMPONENT_WIDTH ? component.substring(0, COMPONENT_WIDTH) : component.padEnd(COMPONENT_WIDTH);
|
|
8833
|
+
const formatMessage = (msg, args) => {
|
|
8834
|
+
if (args.length === 0)
|
|
8835
|
+
return msg;
|
|
8836
|
+
return `${msg} ${args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}`;
|
|
8837
|
+
};
|
|
8838
|
+
const DEFAULT_JSON_MAX_LEN = 60;
|
|
8839
|
+
return {
|
|
8840
|
+
debug: (msg, ...args) => {
|
|
8841
|
+
if (isDebug()) {
|
|
8842
|
+
const fullMsg = formatMessage(msg, args);
|
|
8843
|
+
if (globalLogHandler) {
|
|
8844
|
+
globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
|
|
8845
|
+
} else {
|
|
8846
|
+
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
8847
|
+
}
|
|
8848
|
+
}
|
|
8849
|
+
},
|
|
8850
|
+
debugJson: (label, data, maxLen = DEFAULT_JSON_MAX_LEN) => {
|
|
8851
|
+
if (isDebug()) {
|
|
8852
|
+
const json2 = JSON.stringify(data);
|
|
8853
|
+
const truncated = json2.length > maxLen ? `${json2.substring(0, maxLen)}…` : json2;
|
|
8854
|
+
const fullMsg = `${label}: ${truncated}`;
|
|
8855
|
+
if (globalLogHandler) {
|
|
8856
|
+
globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
|
|
8857
|
+
} else {
|
|
8858
|
+
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
8859
|
+
}
|
|
8860
|
+
}
|
|
8861
|
+
},
|
|
8862
|
+
info: (msg, ...args) => {
|
|
8863
|
+
const fullMsg = formatMessage(msg, args);
|
|
8864
|
+
if (globalLogHandler) {
|
|
8865
|
+
globalLogHandler("info", paddedComponent, fullMsg, sessionId);
|
|
8866
|
+
} else {
|
|
8867
|
+
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
8868
|
+
}
|
|
8869
|
+
},
|
|
8870
|
+
warn: (msg, ...args) => {
|
|
8871
|
+
const fullMsg = formatMessage(msg, args);
|
|
8872
|
+
if (globalLogHandler) {
|
|
8873
|
+
globalLogHandler("warn", paddedComponent, fullMsg, sessionId);
|
|
8874
|
+
} else {
|
|
8875
|
+
console.warn(`[${paddedComponent}] ⚠️ ${fullMsg}`);
|
|
8876
|
+
}
|
|
8877
|
+
},
|
|
8878
|
+
error: (msg, err) => {
|
|
8879
|
+
const fullMsg = err && isDebug() ? `${msg}
|
|
8880
|
+
${err.stack || err.message}` : msg;
|
|
8881
|
+
if (globalLogHandler) {
|
|
8882
|
+
globalLogHandler("error", paddedComponent, fullMsg, sessionId);
|
|
8883
|
+
} else {
|
|
8884
|
+
console.error(`[${paddedComponent}] ❌ ${msg}`);
|
|
8885
|
+
if (err && isDebug()) {
|
|
8886
|
+
console.error(err);
|
|
8887
|
+
}
|
|
8888
|
+
}
|
|
8889
|
+
},
|
|
8890
|
+
forSession: (sid) => createLogger(component, useStderr, sid)
|
|
8891
|
+
};
|
|
8892
|
+
}
|
|
8893
|
+
var globalLogHandler = null, COMPONENT_WIDTH = 10, mcpLogger, wsLogger;
|
|
8894
|
+
var init_logger = __esm(() => {
|
|
8895
|
+
mcpLogger = createLogger("MCP", true);
|
|
8896
|
+
wsLogger = createLogger("ws", false);
|
|
8897
|
+
});
|
|
8898
|
+
|
|
8825
8899
|
// src/utils/emoji.ts
|
|
8826
8900
|
var exports_emoji = {};
|
|
8827
8901
|
__export(exports_emoji, {
|
|
@@ -8892,6 +8966,334 @@ var init_emoji = __esm(() => {
|
|
|
8892
8966
|
};
|
|
8893
8967
|
});
|
|
8894
8968
|
|
|
8969
|
+
// src/git/worktree.ts
|
|
8970
|
+
var exports_worktree = {};
|
|
8971
|
+
__export(exports_worktree, {
|
|
8972
|
+
writeWorktreeMetadata: () => writeWorktreeMetadata,
|
|
8973
|
+
updateWorktreeActivity: () => updateWorktreeActivity,
|
|
8974
|
+
removeWorktreeMetadata: () => removeWorktreeMetadata,
|
|
8975
|
+
removeWorktree: () => removeWorktree,
|
|
8976
|
+
readWorktreeMetadata: () => readWorktreeMetadata,
|
|
8977
|
+
listWorktrees: () => listWorktrees,
|
|
8978
|
+
isValidWorktreePath: () => isValidWorktreePath,
|
|
8979
|
+
isValidBranchName: () => isValidBranchName,
|
|
8980
|
+
isGitRepository: () => isGitRepository,
|
|
8981
|
+
isBranchMerged: () => isBranchMerged,
|
|
8982
|
+
hasUncommittedChanges: () => hasUncommittedChanges,
|
|
8983
|
+
getWorktreesDir: () => getWorktreesDir,
|
|
8984
|
+
getWorktreeDir: () => getWorktreeDir,
|
|
8985
|
+
getRepositoryRoot: () => getRepositoryRoot,
|
|
8986
|
+
getDefaultBranch: () => getDefaultBranch,
|
|
8987
|
+
getCurrentBranch: () => getCurrentBranch,
|
|
8988
|
+
findWorktreeByBranch: () => findWorktreeByBranch,
|
|
8989
|
+
detectWorktreeInfo: () => detectWorktreeInfo,
|
|
8990
|
+
createWorktree: () => createWorktree
|
|
8991
|
+
});
|
|
8992
|
+
import { spawn as spawn2 } from "child_process";
|
|
8993
|
+
import { randomUUID } from "crypto";
|
|
8994
|
+
import * as path from "path";
|
|
8995
|
+
import * as fs from "fs/promises";
|
|
8996
|
+
import { homedir as homedir4 } from "os";
|
|
8997
|
+
async function execGit(args, cwd) {
|
|
8998
|
+
const cmd = `git ${args.join(" ")}`;
|
|
8999
|
+
log7.debug(`Executing: ${cmd}`);
|
|
9000
|
+
return new Promise((resolve3, reject) => {
|
|
9001
|
+
const proc = spawn2("git", args, { cwd });
|
|
9002
|
+
let stdout = "";
|
|
9003
|
+
let stderr = "";
|
|
9004
|
+
proc.stdout.on("data", (data) => {
|
|
9005
|
+
stdout += data.toString();
|
|
9006
|
+
});
|
|
9007
|
+
proc.stderr.on("data", (data) => {
|
|
9008
|
+
stderr += data.toString();
|
|
9009
|
+
});
|
|
9010
|
+
proc.on("close", (code) => {
|
|
9011
|
+
if (code === 0) {
|
|
9012
|
+
log7.debug(`${cmd} → success`);
|
|
9013
|
+
resolve3(stdout.trim());
|
|
9014
|
+
} else {
|
|
9015
|
+
log7.debug(`${cmd} → failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
|
|
9016
|
+
reject(new Error(`git ${args.join(" ")} failed: ${stderr || stdout}`));
|
|
9017
|
+
}
|
|
9018
|
+
});
|
|
9019
|
+
proc.on("error", (err) => {
|
|
9020
|
+
log7.warn(`${cmd} → error: ${err}`);
|
|
9021
|
+
reject(err);
|
|
9022
|
+
});
|
|
9023
|
+
});
|
|
9024
|
+
}
|
|
9025
|
+
async function isGitRepository(dir) {
|
|
9026
|
+
try {
|
|
9027
|
+
await execGit(["rev-parse", "--git-dir"], dir);
|
|
9028
|
+
return true;
|
|
9029
|
+
} catch (err) {
|
|
9030
|
+
log7.debug(`Not a git repository: ${dir} (${err})`);
|
|
9031
|
+
return false;
|
|
9032
|
+
}
|
|
9033
|
+
}
|
|
9034
|
+
async function getRepositoryRoot(dir) {
|
|
9035
|
+
return execGit(["rev-parse", "--show-toplevel"], dir);
|
|
9036
|
+
}
|
|
9037
|
+
async function getCurrentBranch(dir) {
|
|
9038
|
+
try {
|
|
9039
|
+
const branch = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], dir);
|
|
9040
|
+
return branch === "HEAD" ? null : branch;
|
|
9041
|
+
} catch {
|
|
9042
|
+
return null;
|
|
9043
|
+
}
|
|
9044
|
+
}
|
|
9045
|
+
async function getDefaultBranch(repoRoot) {
|
|
9046
|
+
try {
|
|
9047
|
+
const remoteHead = await execGit(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoRoot);
|
|
9048
|
+
return remoteHead.replace("origin/", "");
|
|
9049
|
+
} catch {
|
|
9050
|
+
try {
|
|
9051
|
+
await execGit(["rev-parse", "--verify", "main"], repoRoot);
|
|
9052
|
+
return "main";
|
|
9053
|
+
} catch {
|
|
9054
|
+
try {
|
|
9055
|
+
await execGit(["rev-parse", "--verify", "master"], repoRoot);
|
|
9056
|
+
return "master";
|
|
9057
|
+
} catch {
|
|
9058
|
+
return "main";
|
|
9059
|
+
}
|
|
9060
|
+
}
|
|
9061
|
+
}
|
|
9062
|
+
}
|
|
9063
|
+
async function isBranchMerged(repoRoot, branchName) {
|
|
9064
|
+
try {
|
|
9065
|
+
const defaultBranch = await getDefaultBranch(repoRoot);
|
|
9066
|
+
if (branchName === defaultBranch) {
|
|
9067
|
+
return false;
|
|
9068
|
+
}
|
|
9069
|
+
await execGit(["fetch", "origin", defaultBranch], repoRoot).catch(() => {});
|
|
9070
|
+
const branchCommit = await execGit(["rev-parse", branchName], repoRoot);
|
|
9071
|
+
const defaultCommit = await execGit(["rev-parse", `origin/${defaultBranch}`], repoRoot);
|
|
9072
|
+
if (branchCommit === defaultCommit) {
|
|
9073
|
+
return false;
|
|
9074
|
+
}
|
|
9075
|
+
await execGit(["merge-base", "--is-ancestor", branchName, `origin/${defaultBranch}`], repoRoot);
|
|
9076
|
+
return true;
|
|
9077
|
+
} catch {
|
|
9078
|
+
return false;
|
|
9079
|
+
}
|
|
9080
|
+
}
|
|
9081
|
+
async function hasUncommittedChanges(dir) {
|
|
9082
|
+
try {
|
|
9083
|
+
const staged = await execGit(["diff", "--cached", "--quiet"], dir).catch(() => "changes");
|
|
9084
|
+
if (staged === "changes")
|
|
9085
|
+
return true;
|
|
9086
|
+
const unstaged = await execGit(["diff", "--quiet"], dir).catch(() => "changes");
|
|
9087
|
+
if (unstaged === "changes")
|
|
9088
|
+
return true;
|
|
9089
|
+
const untracked = await execGit(["ls-files", "--others", "--exclude-standard"], dir);
|
|
9090
|
+
return untracked.length > 0;
|
|
9091
|
+
} catch {
|
|
9092
|
+
return false;
|
|
9093
|
+
}
|
|
9094
|
+
}
|
|
9095
|
+
async function listWorktrees(repoRoot) {
|
|
9096
|
+
const output = await execGit(["worktree", "list", "--porcelain"], repoRoot);
|
|
9097
|
+
const worktrees = [];
|
|
9098
|
+
if (!output)
|
|
9099
|
+
return worktrees;
|
|
9100
|
+
const blocks = output.split(`
|
|
9101
|
+
|
|
9102
|
+
`).filter(Boolean);
|
|
9103
|
+
for (const block of blocks) {
|
|
9104
|
+
const lines = block.split(`
|
|
9105
|
+
`);
|
|
9106
|
+
const worktree = {};
|
|
9107
|
+
for (const line of lines) {
|
|
9108
|
+
if (line.startsWith("worktree ")) {
|
|
9109
|
+
worktree.path = line.slice(9);
|
|
9110
|
+
} else if (line.startsWith("HEAD ")) {
|
|
9111
|
+
worktree.commit = line.slice(5);
|
|
9112
|
+
} else if (line.startsWith("branch ")) {
|
|
9113
|
+
worktree.branch = line.slice(7).replace("refs/heads/", "");
|
|
9114
|
+
} else if (line === "bare") {
|
|
9115
|
+
worktree.isBare = true;
|
|
9116
|
+
} else if (line === "detached") {
|
|
9117
|
+
worktree.branch = "(detached)";
|
|
9118
|
+
}
|
|
9119
|
+
}
|
|
9120
|
+
if (worktree.path) {
|
|
9121
|
+
worktrees.push({
|
|
9122
|
+
path: worktree.path,
|
|
9123
|
+
branch: worktree.branch || "(unknown)",
|
|
9124
|
+
commit: worktree.commit || "",
|
|
9125
|
+
isMain: worktrees.length === 0,
|
|
9126
|
+
isBare: worktree.isBare || false
|
|
9127
|
+
});
|
|
9128
|
+
}
|
|
9129
|
+
}
|
|
9130
|
+
return worktrees;
|
|
9131
|
+
}
|
|
9132
|
+
async function branchExists(repoRoot, branch) {
|
|
9133
|
+
try {
|
|
9134
|
+
await execGit(["rev-parse", "--verify", `refs/heads/${branch}`], repoRoot);
|
|
9135
|
+
return true;
|
|
9136
|
+
} catch {
|
|
9137
|
+
try {
|
|
9138
|
+
await execGit(["rev-parse", "--verify", `refs/remotes/origin/${branch}`], repoRoot);
|
|
9139
|
+
return true;
|
|
9140
|
+
} catch {
|
|
9141
|
+
return false;
|
|
9142
|
+
}
|
|
9143
|
+
}
|
|
9144
|
+
}
|
|
9145
|
+
function getWorktreeDir(repoRoot, branch) {
|
|
9146
|
+
const repoName = repoRoot.replace(/\//g, "-").replace(/^-/, "");
|
|
9147
|
+
const sanitizedBranch = branch.replace(/\//g, "-").replace(/[^a-zA-Z0-9-_]/g, "");
|
|
9148
|
+
const shortUuid = randomUUID().slice(0, 8);
|
|
9149
|
+
return path.join(WORKTREES_DIR, `${repoName}--${sanitizedBranch}-${shortUuid}`);
|
|
9150
|
+
}
|
|
9151
|
+
function isValidWorktreePath(worktreePath) {
|
|
9152
|
+
return worktreePath.startsWith(WORKTREES_DIR + path.sep);
|
|
9153
|
+
}
|
|
9154
|
+
function getWorktreesDir() {
|
|
9155
|
+
return WORKTREES_DIR;
|
|
9156
|
+
}
|
|
9157
|
+
async function detectWorktreeInfo(workingDir) {
|
|
9158
|
+
if (!isValidWorktreePath(workingDir)) {
|
|
9159
|
+
return null;
|
|
9160
|
+
}
|
|
9161
|
+
try {
|
|
9162
|
+
const branchOutput = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
|
|
9163
|
+
const branch = branchOutput?.trim();
|
|
9164
|
+
if (!branch) {
|
|
9165
|
+
log7.debug(`Could not detect branch for worktree at ${workingDir}`);
|
|
9166
|
+
return null;
|
|
9167
|
+
}
|
|
9168
|
+
const gitDirOutput = await execGit(["rev-parse", "--git-common-dir"], workingDir);
|
|
9169
|
+
let repoRoot = gitDirOutput?.trim();
|
|
9170
|
+
if (repoRoot) {
|
|
9171
|
+
if (repoRoot.endsWith("/.git")) {
|
|
9172
|
+
repoRoot = repoRoot.slice(0, -5);
|
|
9173
|
+
} else if (repoRoot.endsWith(".git")) {
|
|
9174
|
+
repoRoot = repoRoot.slice(0, -4);
|
|
9175
|
+
}
|
|
9176
|
+
}
|
|
9177
|
+
log7.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
|
|
9178
|
+
return {
|
|
9179
|
+
worktreePath: workingDir,
|
|
9180
|
+
branch,
|
|
9181
|
+
repoRoot: repoRoot || workingDir
|
|
9182
|
+
};
|
|
9183
|
+
} catch (err) {
|
|
9184
|
+
log7.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
|
|
9185
|
+
return null;
|
|
9186
|
+
}
|
|
9187
|
+
}
|
|
9188
|
+
async function createWorktree(repoRoot, branch, targetDir) {
|
|
9189
|
+
log7.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
|
|
9190
|
+
const parentDir = path.dirname(targetDir);
|
|
9191
|
+
log7.debug(`Creating parent directory: ${parentDir}`);
|
|
9192
|
+
await fs.mkdir(parentDir, { recursive: true });
|
|
9193
|
+
const exists = await branchExists(repoRoot, branch);
|
|
9194
|
+
if (exists) {
|
|
9195
|
+
log7.debug(`Branch '${branch}' exists, adding worktree`);
|
|
9196
|
+
await execGit(["worktree", "add", targetDir, branch], repoRoot);
|
|
9197
|
+
} else {
|
|
9198
|
+
log7.debug(`Branch '${branch}' does not exist, creating with worktree`);
|
|
9199
|
+
await execGit(["worktree", "add", "-b", branch, targetDir], repoRoot);
|
|
9200
|
+
}
|
|
9201
|
+
log7.info(`Worktree created successfully: ${targetDir}`);
|
|
9202
|
+
return targetDir;
|
|
9203
|
+
}
|
|
9204
|
+
async function removeWorktree(repoRoot, worktreePath) {
|
|
9205
|
+
log7.info(`Removing worktree: ${worktreePath}`);
|
|
9206
|
+
try {
|
|
9207
|
+
await execGit(["worktree", "remove", worktreePath], repoRoot);
|
|
9208
|
+
log7.debug("Worktree removed cleanly");
|
|
9209
|
+
} catch (err) {
|
|
9210
|
+
log7.debug(`Clean remove failed (${err}), trying force remove`);
|
|
9211
|
+
await execGit(["worktree", "remove", "--force", worktreePath], repoRoot);
|
|
9212
|
+
}
|
|
9213
|
+
log7.debug("Pruning stale worktree references");
|
|
9214
|
+
await execGit(["worktree", "prune"], repoRoot);
|
|
9215
|
+
log7.info("Worktree removed and pruned successfully");
|
|
9216
|
+
}
|
|
9217
|
+
async function findWorktreeByBranch(repoRoot, branch) {
|
|
9218
|
+
const worktrees = await listWorktrees(repoRoot);
|
|
9219
|
+
return worktrees.find((wt) => wt.branch === branch) || null;
|
|
9220
|
+
}
|
|
9221
|
+
function isValidBranchName(name) {
|
|
9222
|
+
if (!name || name.length === 0)
|
|
9223
|
+
return false;
|
|
9224
|
+
if (name.startsWith("/") || name.endsWith("/"))
|
|
9225
|
+
return false;
|
|
9226
|
+
if (name.includes(".."))
|
|
9227
|
+
return false;
|
|
9228
|
+
if (/[\s~^:?*[\]\\]/.test(name))
|
|
9229
|
+
return false;
|
|
9230
|
+
if (name.startsWith("-"))
|
|
9231
|
+
return false;
|
|
9232
|
+
if (name.endsWith(".lock"))
|
|
9233
|
+
return false;
|
|
9234
|
+
if (name.includes("@{"))
|
|
9235
|
+
return false;
|
|
9236
|
+
if (name === "@")
|
|
9237
|
+
return false;
|
|
9238
|
+
if (/\.\./.test(name))
|
|
9239
|
+
return false;
|
|
9240
|
+
return true;
|
|
9241
|
+
}
|
|
9242
|
+
async function readMetadataStore() {
|
|
9243
|
+
try {
|
|
9244
|
+
const content = await fs.readFile(METADATA_STORE_PATH, "utf-8");
|
|
9245
|
+
return JSON.parse(content);
|
|
9246
|
+
} catch {
|
|
9247
|
+
return {};
|
|
9248
|
+
}
|
|
9249
|
+
}
|
|
9250
|
+
async function writeMetadataStore(store) {
|
|
9251
|
+
try {
|
|
9252
|
+
await fs.mkdir(path.dirname(METADATA_STORE_PATH), { recursive: true });
|
|
9253
|
+
await fs.writeFile(METADATA_STORE_PATH, JSON.stringify(store, null, 2), { encoding: "utf-8", mode: 384 });
|
|
9254
|
+
await fs.chmod(METADATA_STORE_PATH, 384);
|
|
9255
|
+
} catch (err) {
|
|
9256
|
+
log7.warn(`Failed to write worktree metadata store: ${err}`);
|
|
9257
|
+
}
|
|
9258
|
+
}
|
|
9259
|
+
async function writeWorktreeMetadata(worktreePath, metadata) {
|
|
9260
|
+
const store = await readMetadataStore();
|
|
9261
|
+
store[worktreePath] = metadata;
|
|
9262
|
+
await writeMetadataStore(store);
|
|
9263
|
+
log7.debug(`Wrote worktree metadata for: ${worktreePath}`);
|
|
9264
|
+
}
|
|
9265
|
+
async function readWorktreeMetadata(worktreePath) {
|
|
9266
|
+
const store = await readMetadataStore();
|
|
9267
|
+
return store[worktreePath] || null;
|
|
9268
|
+
}
|
|
9269
|
+
async function updateWorktreeActivity(worktreePath, sessionId) {
|
|
9270
|
+
const store = await readMetadataStore();
|
|
9271
|
+
const existing = store[worktreePath];
|
|
9272
|
+
if (!existing)
|
|
9273
|
+
return;
|
|
9274
|
+
existing.lastActivityAt = new Date().toISOString();
|
|
9275
|
+
if (sessionId !== undefined) {
|
|
9276
|
+
existing.sessionId = sessionId;
|
|
9277
|
+
}
|
|
9278
|
+
store[worktreePath] = existing;
|
|
9279
|
+
await writeMetadataStore(store);
|
|
9280
|
+
}
|
|
9281
|
+
async function removeWorktreeMetadata(worktreePath) {
|
|
9282
|
+
const store = await readMetadataStore();
|
|
9283
|
+
if (store[worktreePath]) {
|
|
9284
|
+
delete store[worktreePath];
|
|
9285
|
+
await writeMetadataStore(store);
|
|
9286
|
+
log7.debug(`Removed worktree metadata for: ${worktreePath}`);
|
|
9287
|
+
}
|
|
9288
|
+
}
|
|
9289
|
+
var log7, WORKTREES_DIR, METADATA_STORE_PATH;
|
|
9290
|
+
var init_worktree = __esm(() => {
|
|
9291
|
+
init_logger();
|
|
9292
|
+
log7 = createLogger("git-wt");
|
|
9293
|
+
WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
|
|
9294
|
+
METADATA_STORE_PATH = path.join(homedir4(), ".claude-threads", "worktree-metadata.json");
|
|
9295
|
+
});
|
|
9296
|
+
|
|
8895
9297
|
// node_modules/pend/index.js
|
|
8896
9298
|
var require_pend = __commonJS((exports, module) => {
|
|
8897
9299
|
module.exports = Pend;
|
|
@@ -50840,82 +51242,8 @@ async function setupSlackPlatform(id, existing) {
|
|
|
50840
51242
|
}
|
|
50841
51243
|
|
|
50842
51244
|
// src/platform/base-client.ts
|
|
51245
|
+
init_logger();
|
|
50843
51246
|
import { EventEmitter } from "events";
|
|
50844
|
-
|
|
50845
|
-
// src/utils/logger.ts
|
|
50846
|
-
var globalLogHandler = null;
|
|
50847
|
-
function setLogHandler(handler) {
|
|
50848
|
-
globalLogHandler = handler;
|
|
50849
|
-
}
|
|
50850
|
-
var COMPONENT_WIDTH = 10;
|
|
50851
|
-
function createLogger(component, useStderr = false, sessionId) {
|
|
50852
|
-
const isDebug = () => process.env.DEBUG === "1";
|
|
50853
|
-
const consoleLog = useStderr ? console.error : console.log;
|
|
50854
|
-
const paddedComponent = component.length > COMPONENT_WIDTH ? component.substring(0, COMPONENT_WIDTH) : component.padEnd(COMPONENT_WIDTH);
|
|
50855
|
-
const formatMessage = (msg, args) => {
|
|
50856
|
-
if (args.length === 0)
|
|
50857
|
-
return msg;
|
|
50858
|
-
return `${msg} ${args.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}`;
|
|
50859
|
-
};
|
|
50860
|
-
const DEFAULT_JSON_MAX_LEN = 60;
|
|
50861
|
-
return {
|
|
50862
|
-
debug: (msg, ...args) => {
|
|
50863
|
-
if (isDebug()) {
|
|
50864
|
-
const fullMsg = formatMessage(msg, args);
|
|
50865
|
-
if (globalLogHandler) {
|
|
50866
|
-
globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
|
|
50867
|
-
} else {
|
|
50868
|
-
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
50869
|
-
}
|
|
50870
|
-
}
|
|
50871
|
-
},
|
|
50872
|
-
debugJson: (label, data, maxLen = DEFAULT_JSON_MAX_LEN) => {
|
|
50873
|
-
if (isDebug()) {
|
|
50874
|
-
const json2 = JSON.stringify(data);
|
|
50875
|
-
const truncated = json2.length > maxLen ? `${json2.substring(0, maxLen)}…` : json2;
|
|
50876
|
-
const fullMsg = `${label}: ${truncated}`;
|
|
50877
|
-
if (globalLogHandler) {
|
|
50878
|
-
globalLogHandler("debug", paddedComponent, fullMsg, sessionId);
|
|
50879
|
-
} else {
|
|
50880
|
-
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
50881
|
-
}
|
|
50882
|
-
}
|
|
50883
|
-
},
|
|
50884
|
-
info: (msg, ...args) => {
|
|
50885
|
-
const fullMsg = formatMessage(msg, args);
|
|
50886
|
-
if (globalLogHandler) {
|
|
50887
|
-
globalLogHandler("info", paddedComponent, fullMsg, sessionId);
|
|
50888
|
-
} else {
|
|
50889
|
-
consoleLog(`[${paddedComponent}] ${fullMsg}`);
|
|
50890
|
-
}
|
|
50891
|
-
},
|
|
50892
|
-
warn: (msg, ...args) => {
|
|
50893
|
-
const fullMsg = formatMessage(msg, args);
|
|
50894
|
-
if (globalLogHandler) {
|
|
50895
|
-
globalLogHandler("warn", paddedComponent, fullMsg, sessionId);
|
|
50896
|
-
} else {
|
|
50897
|
-
console.warn(`[${paddedComponent}] ⚠️ ${fullMsg}`);
|
|
50898
|
-
}
|
|
50899
|
-
},
|
|
50900
|
-
error: (msg, err) => {
|
|
50901
|
-
const fullMsg = err && isDebug() ? `${msg}
|
|
50902
|
-
${err.stack || err.message}` : msg;
|
|
50903
|
-
if (globalLogHandler) {
|
|
50904
|
-
globalLogHandler("error", paddedComponent, fullMsg, sessionId);
|
|
50905
|
-
} else {
|
|
50906
|
-
console.error(`[${paddedComponent}] ❌ ${msg}`);
|
|
50907
|
-
if (err && isDebug()) {
|
|
50908
|
-
console.error(err);
|
|
50909
|
-
}
|
|
50910
|
-
}
|
|
50911
|
-
},
|
|
50912
|
-
forSession: (sid) => createLogger(component, useStderr, sid)
|
|
50913
|
-
};
|
|
50914
|
-
}
|
|
50915
|
-
var mcpLogger = createLogger("MCP", true);
|
|
50916
|
-
var wsLogger = createLogger("ws", false);
|
|
50917
|
-
|
|
50918
|
-
// src/platform/base-client.ts
|
|
50919
51247
|
var log = createLogger("base-client");
|
|
50920
51248
|
|
|
50921
51249
|
class BasePlatformClient extends EventEmitter {
|
|
@@ -51038,6 +51366,9 @@ class BasePlatformClient extends EventEmitter {
|
|
|
51038
51366
|
this.lastMessageAt = Date.now();
|
|
51039
51367
|
}
|
|
51040
51368
|
}
|
|
51369
|
+
// src/platform/mattermost/client.ts
|
|
51370
|
+
init_logger();
|
|
51371
|
+
|
|
51041
51372
|
// src/version.ts
|
|
51042
51373
|
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
51043
51374
|
import { dirname as dirname3, resolve as resolve2 } from "path";
|
|
@@ -51797,6 +52128,9 @@ class MattermostClient extends BasePlatformClient {
|
|
|
51797
52128
|
}));
|
|
51798
52129
|
}
|
|
51799
52130
|
}
|
|
52131
|
+
// src/platform/slack/client.ts
|
|
52132
|
+
init_logger();
|
|
52133
|
+
|
|
51800
52134
|
// src/platform/slack/formatter.ts
|
|
51801
52135
|
class SlackFormatter {
|
|
51802
52136
|
formatBold(text) {
|
|
@@ -52510,6 +52844,7 @@ class SlackClient extends BasePlatformClient {
|
|
|
52510
52844
|
}
|
|
52511
52845
|
}
|
|
52512
52846
|
// src/mattermost/api.ts
|
|
52847
|
+
init_logger();
|
|
52513
52848
|
var log4 = createLogger("mm-api");
|
|
52514
52849
|
async function mattermostApi(config, method, path, body) {
|
|
52515
52850
|
const url = `${config.url}/api/v4${path}`;
|
|
@@ -52579,6 +52914,7 @@ async function createInteractivePost(config, channelId, message, reactions, root
|
|
|
52579
52914
|
}
|
|
52580
52915
|
|
|
52581
52916
|
// src/platform/mattermost/permission-api.ts
|
|
52917
|
+
init_logger();
|
|
52582
52918
|
class MattermostPermissionApi {
|
|
52583
52919
|
apiConfig;
|
|
52584
52920
|
config;
|
|
@@ -52707,6 +53043,7 @@ class MattermostPermissionApi {
|
|
|
52707
53043
|
}
|
|
52708
53044
|
|
|
52709
53045
|
// src/platform/slack/permission-api.ts
|
|
53046
|
+
init_logger();
|
|
52710
53047
|
var SLACK_API_BASE = "https://slack.com/api";
|
|
52711
53048
|
async function slackApi(method, token, body) {
|
|
52712
53049
|
const url = `${SLACK_API_BASE}/${method}`;
|
|
@@ -52919,6 +53256,7 @@ class SlackPermissionApi {
|
|
|
52919
53256
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
52920
53257
|
|
|
52921
53258
|
// src/persistence/session-store.ts
|
|
53259
|
+
init_logger();
|
|
52922
53260
|
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync, chmodSync as chmodSync2 } from "fs";
|
|
52923
53261
|
import { homedir as homedir2 } from "os";
|
|
52924
53262
|
import { join as join2 } from "path";
|
|
@@ -53145,11 +53483,13 @@ class SessionStore {
|
|
|
53145
53483
|
init_emoji();
|
|
53146
53484
|
|
|
53147
53485
|
// src/cleanup/scheduler.ts
|
|
53486
|
+
init_logger();
|
|
53148
53487
|
import { existsSync as existsSync7 } from "fs";
|
|
53149
53488
|
import { readdir, rm } from "fs/promises";
|
|
53150
53489
|
import { join as join5 } from "path";
|
|
53151
53490
|
|
|
53152
53491
|
// src/persistence/thread-logger.ts
|
|
53492
|
+
init_logger();
|
|
53153
53493
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, appendFileSync, readdirSync, statSync, unlinkSync, rmdirSync, readFileSync as readFileSync5, chmodSync as chmodSync3 } from "fs";
|
|
53154
53494
|
import { homedir as homedir3 } from "os";
|
|
53155
53495
|
import { join as join3, dirname as dirname4 } from "path";
|
|
@@ -53424,309 +53764,8 @@ function readRecentLogEntries(platformId, sessionId, maxLines = 50) {
|
|
|
53424
53764
|
}
|
|
53425
53765
|
}
|
|
53426
53766
|
|
|
53427
|
-
// src/git/worktree.ts
|
|
53428
|
-
import { spawn as spawn2 } from "child_process";
|
|
53429
|
-
import { randomUUID } from "crypto";
|
|
53430
|
-
import * as path from "path";
|
|
53431
|
-
import * as fs from "fs/promises";
|
|
53432
|
-
import { homedir as homedir4 } from "os";
|
|
53433
|
-
var log7 = createLogger("git-wt");
|
|
53434
|
-
var WORKTREES_DIR = path.join(homedir4(), ".claude-threads", "worktrees");
|
|
53435
|
-
async function execGit(args, cwd) {
|
|
53436
|
-
const cmd = `git ${args.join(" ")}`;
|
|
53437
|
-
log7.debug(`Executing: ${cmd}`);
|
|
53438
|
-
return new Promise((resolve3, reject) => {
|
|
53439
|
-
const proc = spawn2("git", args, { cwd });
|
|
53440
|
-
let stdout = "";
|
|
53441
|
-
let stderr = "";
|
|
53442
|
-
proc.stdout.on("data", (data) => {
|
|
53443
|
-
stdout += data.toString();
|
|
53444
|
-
});
|
|
53445
|
-
proc.stderr.on("data", (data) => {
|
|
53446
|
-
stderr += data.toString();
|
|
53447
|
-
});
|
|
53448
|
-
proc.on("close", (code) => {
|
|
53449
|
-
if (code === 0) {
|
|
53450
|
-
log7.debug(`${cmd} → success`);
|
|
53451
|
-
resolve3(stdout.trim());
|
|
53452
|
-
} else {
|
|
53453
|
-
log7.debug(`${cmd} → failed (code=${code}): ${stderr.substring(0, 100) || stdout.substring(0, 100)}`);
|
|
53454
|
-
reject(new Error(`git ${args.join(" ")} failed: ${stderr || stdout}`));
|
|
53455
|
-
}
|
|
53456
|
-
});
|
|
53457
|
-
proc.on("error", (err) => {
|
|
53458
|
-
log7.warn(`${cmd} → error: ${err}`);
|
|
53459
|
-
reject(err);
|
|
53460
|
-
});
|
|
53461
|
-
});
|
|
53462
|
-
}
|
|
53463
|
-
async function isGitRepository(dir) {
|
|
53464
|
-
try {
|
|
53465
|
-
await execGit(["rev-parse", "--git-dir"], dir);
|
|
53466
|
-
return true;
|
|
53467
|
-
} catch (err) {
|
|
53468
|
-
log7.debug(`Not a git repository: ${dir} (${err})`);
|
|
53469
|
-
return false;
|
|
53470
|
-
}
|
|
53471
|
-
}
|
|
53472
|
-
async function getRepositoryRoot(dir) {
|
|
53473
|
-
return execGit(["rev-parse", "--show-toplevel"], dir);
|
|
53474
|
-
}
|
|
53475
|
-
async function getCurrentBranch(dir) {
|
|
53476
|
-
try {
|
|
53477
|
-
const branch = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], dir);
|
|
53478
|
-
return branch === "HEAD" ? null : branch;
|
|
53479
|
-
} catch {
|
|
53480
|
-
return null;
|
|
53481
|
-
}
|
|
53482
|
-
}
|
|
53483
|
-
async function getDefaultBranch(repoRoot) {
|
|
53484
|
-
try {
|
|
53485
|
-
const remoteHead = await execGit(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"], repoRoot);
|
|
53486
|
-
return remoteHead.replace("origin/", "");
|
|
53487
|
-
} catch {
|
|
53488
|
-
try {
|
|
53489
|
-
await execGit(["rev-parse", "--verify", "main"], repoRoot);
|
|
53490
|
-
return "main";
|
|
53491
|
-
} catch {
|
|
53492
|
-
try {
|
|
53493
|
-
await execGit(["rev-parse", "--verify", "master"], repoRoot);
|
|
53494
|
-
return "master";
|
|
53495
|
-
} catch {
|
|
53496
|
-
return "main";
|
|
53497
|
-
}
|
|
53498
|
-
}
|
|
53499
|
-
}
|
|
53500
|
-
}
|
|
53501
|
-
async function isBranchMerged(repoRoot, branchName) {
|
|
53502
|
-
try {
|
|
53503
|
-
const defaultBranch = await getDefaultBranch(repoRoot);
|
|
53504
|
-
if (branchName === defaultBranch) {
|
|
53505
|
-
return false;
|
|
53506
|
-
}
|
|
53507
|
-
await execGit(["fetch", "origin", defaultBranch], repoRoot).catch(() => {});
|
|
53508
|
-
const branchCommit = await execGit(["rev-parse", branchName], repoRoot);
|
|
53509
|
-
const defaultCommit = await execGit(["rev-parse", `origin/${defaultBranch}`], repoRoot);
|
|
53510
|
-
if (branchCommit === defaultCommit) {
|
|
53511
|
-
return false;
|
|
53512
|
-
}
|
|
53513
|
-
await execGit(["merge-base", "--is-ancestor", branchName, `origin/${defaultBranch}`], repoRoot);
|
|
53514
|
-
return true;
|
|
53515
|
-
} catch {
|
|
53516
|
-
return false;
|
|
53517
|
-
}
|
|
53518
|
-
}
|
|
53519
|
-
async function hasUncommittedChanges(dir) {
|
|
53520
|
-
try {
|
|
53521
|
-
const staged = await execGit(["diff", "--cached", "--quiet"], dir).catch(() => "changes");
|
|
53522
|
-
if (staged === "changes")
|
|
53523
|
-
return true;
|
|
53524
|
-
const unstaged = await execGit(["diff", "--quiet"], dir).catch(() => "changes");
|
|
53525
|
-
if (unstaged === "changes")
|
|
53526
|
-
return true;
|
|
53527
|
-
const untracked = await execGit(["ls-files", "--others", "--exclude-standard"], dir);
|
|
53528
|
-
return untracked.length > 0;
|
|
53529
|
-
} catch {
|
|
53530
|
-
return false;
|
|
53531
|
-
}
|
|
53532
|
-
}
|
|
53533
|
-
async function listWorktrees(repoRoot) {
|
|
53534
|
-
const output = await execGit(["worktree", "list", "--porcelain"], repoRoot);
|
|
53535
|
-
const worktrees = [];
|
|
53536
|
-
if (!output)
|
|
53537
|
-
return worktrees;
|
|
53538
|
-
const blocks = output.split(`
|
|
53539
|
-
|
|
53540
|
-
`).filter(Boolean);
|
|
53541
|
-
for (const block of blocks) {
|
|
53542
|
-
const lines = block.split(`
|
|
53543
|
-
`);
|
|
53544
|
-
const worktree = {};
|
|
53545
|
-
for (const line of lines) {
|
|
53546
|
-
if (line.startsWith("worktree ")) {
|
|
53547
|
-
worktree.path = line.slice(9);
|
|
53548
|
-
} else if (line.startsWith("HEAD ")) {
|
|
53549
|
-
worktree.commit = line.slice(5);
|
|
53550
|
-
} else if (line.startsWith("branch ")) {
|
|
53551
|
-
worktree.branch = line.slice(7).replace("refs/heads/", "");
|
|
53552
|
-
} else if (line === "bare") {
|
|
53553
|
-
worktree.isBare = true;
|
|
53554
|
-
} else if (line === "detached") {
|
|
53555
|
-
worktree.branch = "(detached)";
|
|
53556
|
-
}
|
|
53557
|
-
}
|
|
53558
|
-
if (worktree.path) {
|
|
53559
|
-
worktrees.push({
|
|
53560
|
-
path: worktree.path,
|
|
53561
|
-
branch: worktree.branch || "(unknown)",
|
|
53562
|
-
commit: worktree.commit || "",
|
|
53563
|
-
isMain: worktrees.length === 0,
|
|
53564
|
-
isBare: worktree.isBare || false
|
|
53565
|
-
});
|
|
53566
|
-
}
|
|
53567
|
-
}
|
|
53568
|
-
return worktrees;
|
|
53569
|
-
}
|
|
53570
|
-
async function branchExists(repoRoot, branch) {
|
|
53571
|
-
try {
|
|
53572
|
-
await execGit(["rev-parse", "--verify", `refs/heads/${branch}`], repoRoot);
|
|
53573
|
-
return true;
|
|
53574
|
-
} catch {
|
|
53575
|
-
try {
|
|
53576
|
-
await execGit(["rev-parse", "--verify", `refs/remotes/origin/${branch}`], repoRoot);
|
|
53577
|
-
return true;
|
|
53578
|
-
} catch {
|
|
53579
|
-
return false;
|
|
53580
|
-
}
|
|
53581
|
-
}
|
|
53582
|
-
}
|
|
53583
|
-
function getWorktreeDir(repoRoot, branch) {
|
|
53584
|
-
const repoName = repoRoot.replace(/\//g, "-").replace(/^-/, "");
|
|
53585
|
-
const sanitizedBranch = branch.replace(/\//g, "-").replace(/[^a-zA-Z0-9-_]/g, "");
|
|
53586
|
-
const shortUuid = randomUUID().slice(0, 8);
|
|
53587
|
-
return path.join(WORKTREES_DIR, `${repoName}--${sanitizedBranch}-${shortUuid}`);
|
|
53588
|
-
}
|
|
53589
|
-
function isValidWorktreePath(worktreePath) {
|
|
53590
|
-
return worktreePath.startsWith(WORKTREES_DIR + path.sep);
|
|
53591
|
-
}
|
|
53592
|
-
function getWorktreesDir() {
|
|
53593
|
-
return WORKTREES_DIR;
|
|
53594
|
-
}
|
|
53595
|
-
async function detectWorktreeInfo(workingDir) {
|
|
53596
|
-
if (!isValidWorktreePath(workingDir)) {
|
|
53597
|
-
return null;
|
|
53598
|
-
}
|
|
53599
|
-
try {
|
|
53600
|
-
const branchOutput = await execGit(["rev-parse", "--abbrev-ref", "HEAD"], workingDir);
|
|
53601
|
-
const branch = branchOutput?.trim();
|
|
53602
|
-
if (!branch) {
|
|
53603
|
-
log7.debug(`Could not detect branch for worktree at ${workingDir}`);
|
|
53604
|
-
return null;
|
|
53605
|
-
}
|
|
53606
|
-
const gitDirOutput = await execGit(["rev-parse", "--git-common-dir"], workingDir);
|
|
53607
|
-
let repoRoot = gitDirOutput?.trim();
|
|
53608
|
-
if (repoRoot) {
|
|
53609
|
-
if (repoRoot.endsWith("/.git")) {
|
|
53610
|
-
repoRoot = repoRoot.slice(0, -5);
|
|
53611
|
-
} else if (repoRoot.endsWith(".git")) {
|
|
53612
|
-
repoRoot = repoRoot.slice(0, -4);
|
|
53613
|
-
}
|
|
53614
|
-
}
|
|
53615
|
-
log7.debug(`Detected worktree: path=${workingDir}, branch=${branch}, repoRoot=${repoRoot}`);
|
|
53616
|
-
return {
|
|
53617
|
-
worktreePath: workingDir,
|
|
53618
|
-
branch,
|
|
53619
|
-
repoRoot: repoRoot || workingDir
|
|
53620
|
-
};
|
|
53621
|
-
} catch (err) {
|
|
53622
|
-
log7.debug(`Failed to detect worktree info for ${workingDir}: ${err}`);
|
|
53623
|
-
return null;
|
|
53624
|
-
}
|
|
53625
|
-
}
|
|
53626
|
-
async function createWorktree(repoRoot, branch, targetDir) {
|
|
53627
|
-
log7.info(`Creating worktree for branch '${branch}' at ${targetDir}`);
|
|
53628
|
-
const parentDir = path.dirname(targetDir);
|
|
53629
|
-
log7.debug(`Creating parent directory: ${parentDir}`);
|
|
53630
|
-
await fs.mkdir(parentDir, { recursive: true });
|
|
53631
|
-
const exists = await branchExists(repoRoot, branch);
|
|
53632
|
-
if (exists) {
|
|
53633
|
-
log7.debug(`Branch '${branch}' exists, adding worktree`);
|
|
53634
|
-
await execGit(["worktree", "add", targetDir, branch], repoRoot);
|
|
53635
|
-
} else {
|
|
53636
|
-
log7.debug(`Branch '${branch}' does not exist, creating with worktree`);
|
|
53637
|
-
await execGit(["worktree", "add", "-b", branch, targetDir], repoRoot);
|
|
53638
|
-
}
|
|
53639
|
-
log7.info(`Worktree created successfully: ${targetDir}`);
|
|
53640
|
-
return targetDir;
|
|
53641
|
-
}
|
|
53642
|
-
async function removeWorktree(repoRoot, worktreePath) {
|
|
53643
|
-
log7.info(`Removing worktree: ${worktreePath}`);
|
|
53644
|
-
try {
|
|
53645
|
-
await execGit(["worktree", "remove", worktreePath], repoRoot);
|
|
53646
|
-
log7.debug("Worktree removed cleanly");
|
|
53647
|
-
} catch (err) {
|
|
53648
|
-
log7.debug(`Clean remove failed (${err}), trying force remove`);
|
|
53649
|
-
await execGit(["worktree", "remove", "--force", worktreePath], repoRoot);
|
|
53650
|
-
}
|
|
53651
|
-
log7.debug("Pruning stale worktree references");
|
|
53652
|
-
await execGit(["worktree", "prune"], repoRoot);
|
|
53653
|
-
log7.info("Worktree removed and pruned successfully");
|
|
53654
|
-
}
|
|
53655
|
-
async function findWorktreeByBranch(repoRoot, branch) {
|
|
53656
|
-
const worktrees = await listWorktrees(repoRoot);
|
|
53657
|
-
return worktrees.find((wt) => wt.branch === branch) || null;
|
|
53658
|
-
}
|
|
53659
|
-
function isValidBranchName(name) {
|
|
53660
|
-
if (!name || name.length === 0)
|
|
53661
|
-
return false;
|
|
53662
|
-
if (name.startsWith("/") || name.endsWith("/"))
|
|
53663
|
-
return false;
|
|
53664
|
-
if (name.includes(".."))
|
|
53665
|
-
return false;
|
|
53666
|
-
if (/[\s~^:?*[\]\\]/.test(name))
|
|
53667
|
-
return false;
|
|
53668
|
-
if (name.startsWith("-"))
|
|
53669
|
-
return false;
|
|
53670
|
-
if (name.endsWith(".lock"))
|
|
53671
|
-
return false;
|
|
53672
|
-
if (name.includes("@{"))
|
|
53673
|
-
return false;
|
|
53674
|
-
if (name === "@")
|
|
53675
|
-
return false;
|
|
53676
|
-
if (/\.\./.test(name))
|
|
53677
|
-
return false;
|
|
53678
|
-
return true;
|
|
53679
|
-
}
|
|
53680
|
-
var METADATA_STORE_PATH = path.join(homedir4(), ".claude-threads", "worktree-metadata.json");
|
|
53681
|
-
async function readMetadataStore() {
|
|
53682
|
-
try {
|
|
53683
|
-
const content = await fs.readFile(METADATA_STORE_PATH, "utf-8");
|
|
53684
|
-
return JSON.parse(content);
|
|
53685
|
-
} catch {
|
|
53686
|
-
return {};
|
|
53687
|
-
}
|
|
53688
|
-
}
|
|
53689
|
-
async function writeMetadataStore(store) {
|
|
53690
|
-
try {
|
|
53691
|
-
await fs.mkdir(path.dirname(METADATA_STORE_PATH), { recursive: true });
|
|
53692
|
-
await fs.writeFile(METADATA_STORE_PATH, JSON.stringify(store, null, 2), { encoding: "utf-8", mode: 384 });
|
|
53693
|
-
await fs.chmod(METADATA_STORE_PATH, 384);
|
|
53694
|
-
} catch (err) {
|
|
53695
|
-
log7.warn(`Failed to write worktree metadata store: ${err}`);
|
|
53696
|
-
}
|
|
53697
|
-
}
|
|
53698
|
-
async function writeWorktreeMetadata(worktreePath, metadata) {
|
|
53699
|
-
const store = await readMetadataStore();
|
|
53700
|
-
store[worktreePath] = metadata;
|
|
53701
|
-
await writeMetadataStore(store);
|
|
53702
|
-
log7.debug(`Wrote worktree metadata for: ${worktreePath}`);
|
|
53703
|
-
}
|
|
53704
|
-
async function readWorktreeMetadata(worktreePath) {
|
|
53705
|
-
const store = await readMetadataStore();
|
|
53706
|
-
return store[worktreePath] || null;
|
|
53707
|
-
}
|
|
53708
|
-
async function updateWorktreeActivity(worktreePath, sessionId) {
|
|
53709
|
-
const store = await readMetadataStore();
|
|
53710
|
-
const existing = store[worktreePath];
|
|
53711
|
-
if (!existing)
|
|
53712
|
-
return;
|
|
53713
|
-
existing.lastActivityAt = new Date().toISOString();
|
|
53714
|
-
if (sessionId !== undefined) {
|
|
53715
|
-
existing.sessionId = sessionId;
|
|
53716
|
-
}
|
|
53717
|
-
store[worktreePath] = existing;
|
|
53718
|
-
await writeMetadataStore(store);
|
|
53719
|
-
}
|
|
53720
|
-
async function removeWorktreeMetadata(worktreePath) {
|
|
53721
|
-
const store = await readMetadataStore();
|
|
53722
|
-
if (store[worktreePath]) {
|
|
53723
|
-
delete store[worktreePath];
|
|
53724
|
-
await writeMetadataStore(store);
|
|
53725
|
-
log7.debug(`Removed worktree metadata for: ${worktreePath}`);
|
|
53726
|
-
}
|
|
53727
|
-
}
|
|
53728
|
-
|
|
53729
53767
|
// src/cleanup/scheduler.ts
|
|
53768
|
+
init_worktree();
|
|
53730
53769
|
var log8 = createLogger("cleanup");
|
|
53731
53770
|
var DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
|
|
53732
53771
|
var MAX_WORKTREE_AGE_MS = 24 * 60 * 60 * 1000;
|
|
@@ -53900,6 +53939,9 @@ class CleanupScheduler {
|
|
|
53900
53939
|
return result;
|
|
53901
53940
|
}
|
|
53902
53941
|
}
|
|
53942
|
+
// src/operations/monitor/handler.ts
|
|
53943
|
+
init_logger();
|
|
53944
|
+
|
|
53903
53945
|
// src/session/timer-manager.ts
|
|
53904
53946
|
function createSessionTimers() {
|
|
53905
53947
|
return {
|
|
@@ -53961,6 +54003,7 @@ function getSessionStatus(session) {
|
|
|
53961
54003
|
}
|
|
53962
54004
|
|
|
53963
54005
|
// src/claude/cli.ts
|
|
54006
|
+
init_logger();
|
|
53964
54007
|
import { spawn as spawn3 } from "child_process";
|
|
53965
54008
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
53966
54009
|
import { resolve as resolve3, dirname as dirname6 } from "path";
|
|
@@ -54845,15 +54888,35 @@ var handleWorktree = async (ctx, args) => {
|
|
|
54845
54888
|
}
|
|
54846
54889
|
switch (subcommandOrBranch) {
|
|
54847
54890
|
case "list":
|
|
54848
|
-
|
|
54891
|
+
if (ctx.commandContext === "first-message") {
|
|
54892
|
+
await ctx.sessionManager.listWorktreesWithoutSession(ctx.client.platformId, ctx.threadId);
|
|
54893
|
+
} else {
|
|
54894
|
+
await ctx.sessionManager.listWorktreesCommand(ctx.threadId, ctx.username);
|
|
54895
|
+
}
|
|
54849
54896
|
return { handled: true };
|
|
54850
|
-
case "switch":
|
|
54897
|
+
case "switch": {
|
|
54851
54898
|
if (!subArgs) {
|
|
54852
54899
|
await ctx.client.createPost(`❌ Usage: ${ctx.formatter.formatCode("!worktree switch <branch>")}`, ctx.threadId);
|
|
54853
54900
|
return { handled: true };
|
|
54854
54901
|
}
|
|
54855
|
-
|
|
54902
|
+
const switchParts = subArgs.split(/\s+/);
|
|
54903
|
+
const branchName = switchParts[0];
|
|
54904
|
+
const remainingPrompt = switchParts.slice(1).join(" ").trim();
|
|
54905
|
+
if (ctx.commandContext === "first-message") {
|
|
54906
|
+
if (remainingPrompt) {
|
|
54907
|
+
return {
|
|
54908
|
+
worktreeBranch: branchName,
|
|
54909
|
+
continueProcessing: false,
|
|
54910
|
+
remainingText: remainingPrompt,
|
|
54911
|
+
sessionOptions: { switchToExisting: true }
|
|
54912
|
+
};
|
|
54913
|
+
}
|
|
54914
|
+
await ctx.sessionManager.switchToWorktreeWithoutSession(ctx.client.platformId, ctx.threadId, branchName);
|
|
54915
|
+
return { handled: true };
|
|
54916
|
+
}
|
|
54917
|
+
await ctx.sessionManager.switchToWorktree(ctx.threadId, branchName, ctx.username);
|
|
54856
54918
|
return { handled: true };
|
|
54919
|
+
}
|
|
54857
54920
|
case "remove":
|
|
54858
54921
|
if (!subArgs) {
|
|
54859
54922
|
await ctx.client.createPost(`❌ Usage: ${ctx.formatter.formatCode("!worktree remove <branch>")}`, ctx.threadId);
|
|
@@ -55065,6 +55128,7 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
55065
55128
|
import { existsSync as existsSync11 } from "fs";
|
|
55066
55129
|
|
|
55067
55130
|
// src/utils/keep-alive.ts
|
|
55131
|
+
init_logger();
|
|
55068
55132
|
import { spawn as spawn4 } from "child_process";
|
|
55069
55133
|
var log10 = createLogger("keepalive");
|
|
55070
55134
|
|
|
@@ -55253,6 +55317,7 @@ class KeepAliveManager {
|
|
|
55253
55317
|
var keepAlive = new KeepAliveManager;
|
|
55254
55318
|
|
|
55255
55319
|
// src/utils/error-handler/index.ts
|
|
55320
|
+
init_logger();
|
|
55256
55321
|
var log11 = createLogger("error");
|
|
55257
55322
|
|
|
55258
55323
|
class SessionError extends Error {
|
|
@@ -55321,6 +55386,9 @@ function logSilentError(context, error) {
|
|
|
55321
55386
|
log11.debug(`[${context}] Silently caught: ${message}`);
|
|
55322
55387
|
}
|
|
55323
55388
|
|
|
55389
|
+
// src/session/lifecycle.ts
|
|
55390
|
+
init_logger();
|
|
55391
|
+
|
|
55324
55392
|
// src/utils/session-log.ts
|
|
55325
55393
|
function createSessionLog(baseLog) {
|
|
55326
55394
|
return (session) => {
|
|
@@ -55332,7 +55400,9 @@ function createSessionLog(baseLog) {
|
|
|
55332
55400
|
}
|
|
55333
55401
|
|
|
55334
55402
|
// src/operations/post-helpers/index.ts
|
|
55403
|
+
init_logger();
|
|
55335
55404
|
init_emoji();
|
|
55405
|
+
init_worktree();
|
|
55336
55406
|
var log12 = createLogger("helpers");
|
|
55337
55407
|
var sessionLog = createSessionLog(log12);
|
|
55338
55408
|
var POST_TYPES = {
|
|
@@ -55416,6 +55486,7 @@ function updateLastMessage(session, post2) {
|
|
|
55416
55486
|
}
|
|
55417
55487
|
|
|
55418
55488
|
// src/claude/quick-query.ts
|
|
55489
|
+
init_logger();
|
|
55419
55490
|
import { spawn as spawn5 } from "child_process";
|
|
55420
55491
|
var log13 = createLogger("query");
|
|
55421
55492
|
async function quickQuery(options2) {
|
|
@@ -55500,6 +55571,7 @@ async function quickQuery(options2) {
|
|
|
55500
55571
|
}
|
|
55501
55572
|
|
|
55502
55573
|
// src/operations/suggestions/title.ts
|
|
55574
|
+
init_logger();
|
|
55503
55575
|
var log14 = createLogger("title");
|
|
55504
55576
|
var SUGGESTION_TIMEOUT = 15000;
|
|
55505
55577
|
var MIN_TITLE_LENGTH = 3;
|
|
@@ -55612,6 +55684,7 @@ async function suggestSessionMetadata(context) {
|
|
|
55612
55684
|
}
|
|
55613
55685
|
|
|
55614
55686
|
// src/operations/suggestions/tag.ts
|
|
55687
|
+
init_logger();
|
|
55615
55688
|
var log15 = createLogger("tags");
|
|
55616
55689
|
var SUGGESTION_TIMEOUT2 = 15000;
|
|
55617
55690
|
var MAX_TAGS = 3;
|
|
@@ -59200,7 +59273,11 @@ class BugReportExecutor extends BaseExecutor {
|
|
|
59200
59273
|
}
|
|
59201
59274
|
// src/operations/executors/worktree-prompt.ts
|
|
59202
59275
|
init_emoji();
|
|
59276
|
+
init_logger();
|
|
59203
59277
|
var log16 = createLogger("wt-prompt");
|
|
59278
|
+
// src/operations/message-manager.ts
|
|
59279
|
+
init_logger();
|
|
59280
|
+
|
|
59204
59281
|
// src/operations/message-manager-events.ts
|
|
59205
59282
|
import { EventEmitter as EventEmitter3 } from "events";
|
|
59206
59283
|
|
|
@@ -59229,6 +59306,7 @@ function createMessageManagerEvents() {
|
|
|
59229
59306
|
}
|
|
59230
59307
|
|
|
59231
59308
|
// src/operations/streaming/handler.ts
|
|
59309
|
+
init_logger();
|
|
59232
59310
|
var import_yauzl = __toESM(require_yauzl(), 1);
|
|
59233
59311
|
import { createGunzip } from "zlib";
|
|
59234
59312
|
import { pipeline } from "stream/promises";
|
|
@@ -60515,6 +60593,9 @@ function formatUptime(startedAt) {
|
|
|
60515
60593
|
return `${minutes}m`;
|
|
60516
60594
|
}
|
|
60517
60595
|
|
|
60596
|
+
// src/operations/sticky-message/handler.ts
|
|
60597
|
+
init_logger();
|
|
60598
|
+
|
|
60518
60599
|
// src/utils/pr-detector.ts
|
|
60519
60600
|
var PR_PATTERNS = [
|
|
60520
60601
|
{
|
|
@@ -65162,6 +65243,8 @@ function getUpdateInfo() {
|
|
|
65162
65243
|
|
|
65163
65244
|
// src/operations/commands/handler.ts
|
|
65164
65245
|
init_emoji();
|
|
65246
|
+
init_logger();
|
|
65247
|
+
init_worktree();
|
|
65165
65248
|
var log20 = createLogger("commands");
|
|
65166
65249
|
var sessionLog2 = createSessionLog(log20);
|
|
65167
65250
|
async function restartClaudeSession(session, cliOptions, ctx, actionName) {
|
|
@@ -65676,6 +65759,8 @@ async function handleBugReportApproval(session, isApproved, username) {
|
|
|
65676
65759
|
session.messageManager?.clearPendingBugReport();
|
|
65677
65760
|
}
|
|
65678
65761
|
// src/operations/suggestions/branch.ts
|
|
65762
|
+
init_worktree();
|
|
65763
|
+
init_logger();
|
|
65679
65764
|
import { exec as exec3 } from "child_process";
|
|
65680
65765
|
import { promisify as promisify3 } from "util";
|
|
65681
65766
|
var execAsync2 = promisify3(exec3);
|
|
@@ -65755,7 +65840,9 @@ async function suggestBranchNames(workingDir, userMessage) {
|
|
|
65755
65840
|
}
|
|
65756
65841
|
|
|
65757
65842
|
// src/operations/worktree/handler.ts
|
|
65843
|
+
init_worktree();
|
|
65758
65844
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
65845
|
+
init_logger();
|
|
65759
65846
|
var log22 = createLogger("worktree");
|
|
65760
65847
|
var sessionLog3 = createSessionLog(log22);
|
|
65761
65848
|
function parseWorktreeError(error) {
|
|
@@ -66183,33 +66270,40 @@ async function switchToWorktree(session, branchOrPath, username, changeDirectory
|
|
|
66183
66270
|
session.messageManager?.setWorktreeInfo(target.path, target.branch);
|
|
66184
66271
|
session.isWorktreeOwner = false;
|
|
66185
66272
|
}
|
|
66186
|
-
async function
|
|
66187
|
-
const isRepo = await isGitRepository(
|
|
66273
|
+
async function buildWorktreeListMessageFromDir(workingDir, formatter, currentWorkingDir) {
|
|
66274
|
+
const isRepo = await isGitRepository(workingDir);
|
|
66188
66275
|
if (!isRepo) {
|
|
66189
|
-
sessionLog3(session).warn(`\uD83C\uDF3F Not a git repository: ${session.workingDir}`);
|
|
66190
66276
|
return null;
|
|
66191
66277
|
}
|
|
66192
|
-
const repoRoot =
|
|
66278
|
+
const repoRoot = await getRepositoryRoot(workingDir);
|
|
66193
66279
|
const worktrees = await listWorktrees(repoRoot);
|
|
66194
66280
|
if (worktrees.length === 0) {
|
|
66195
|
-
sessionLog3(session).debug(`\uD83C\uDF3F No worktrees found`);
|
|
66196
66281
|
return "No worktrees found for this repository";
|
|
66197
66282
|
}
|
|
66198
66283
|
const shortRepoRoot = repoRoot.replace(process.env.HOME || "", "~");
|
|
66199
|
-
|
|
66200
|
-
let message = `\uD83D\uDCCB ${fmt.formatBold("Worktrees for")} ${fmt.formatCode(shortRepoRoot)}:
|
|
66284
|
+
let message = `\uD83D\uDCCB ${formatter.formatBold("Worktrees for")} ${formatter.formatCode(shortRepoRoot)}:
|
|
66201
66285
|
|
|
66202
66286
|
`;
|
|
66203
66287
|
for (const wt of worktrees) {
|
|
66204
66288
|
const shortPath = wt.isMain ? wt.path.replace(process.env.HOME || "", "~") : shortenPath(wt.path, undefined, { path: wt.path, branch: wt.branch });
|
|
66205
|
-
const isCurrent =
|
|
66289
|
+
const isCurrent = currentWorkingDir === wt.path;
|
|
66206
66290
|
const marker = isCurrent ? " ← current" : "";
|
|
66207
66291
|
const label = wt.isMain ? "(main repository)" : "";
|
|
66208
|
-
message += `• ${
|
|
66292
|
+
message += `• ${formatter.formatCode(wt.branch)} → ${formatter.formatCode(shortPath)} ${label}${marker}
|
|
66209
66293
|
`;
|
|
66210
66294
|
}
|
|
66211
66295
|
return message;
|
|
66212
66296
|
}
|
|
66297
|
+
async function buildWorktreeListMessage(session) {
|
|
66298
|
+
const repoRoot = session.worktreeInfo?.repoRoot;
|
|
66299
|
+
const isRepo = await isGitRepository(session.workingDir);
|
|
66300
|
+
if (!isRepo) {
|
|
66301
|
+
sessionLog3(session).warn(`\uD83C\uDF3F Not a git repository: ${session.workingDir}`);
|
|
66302
|
+
return null;
|
|
66303
|
+
}
|
|
66304
|
+
const workingDirForList = repoRoot || session.workingDir;
|
|
66305
|
+
return buildWorktreeListMessageFromDir(workingDirForList, session.platform.getFormatter(), session.workingDir);
|
|
66306
|
+
}
|
|
66213
66307
|
async function listWorktreesCommand(session) {
|
|
66214
66308
|
const message = await buildWorktreeListMessage(session);
|
|
66215
66309
|
if (message === null) {
|
|
@@ -66299,6 +66393,7 @@ async function cleanupWorktreeCommand(session, username, hasOtherSessionsUsingWo
|
|
|
66299
66393
|
}
|
|
66300
66394
|
}
|
|
66301
66395
|
// src/operations/events/handler.ts
|
|
66396
|
+
init_logger();
|
|
66302
66397
|
var log23 = createLogger("events");
|
|
66303
66398
|
var sessionLog4 = createSessionLog(log23);
|
|
66304
66399
|
function detectAndExecuteClaudeCommands(text, session, ctx) {
|
|
@@ -66547,6 +66642,7 @@ function createSessionContext(config, state, ops) {
|
|
|
66547
66642
|
}
|
|
66548
66643
|
// src/operations/context-prompt/handler.ts
|
|
66549
66644
|
init_emoji();
|
|
66645
|
+
init_logger();
|
|
66550
66646
|
var log24 = createLogger("context");
|
|
66551
66647
|
var sessionLog5 = createSessionLog(log24);
|
|
66552
66648
|
var CONTEXT_PROMPT_TIMEOUT_MS = 30000;
|
|
@@ -66794,6 +66890,7 @@ function formatRelativeTime(date) {
|
|
|
66794
66890
|
return `${diffMin} min ago`;
|
|
66795
66891
|
}
|
|
66796
66892
|
// src/session/lifecycle.ts
|
|
66893
|
+
init_worktree();
|
|
66797
66894
|
var log25 = createLogger("lifecycle");
|
|
66798
66895
|
var sessionLog6 = createSessionLog(log25);
|
|
66799
66896
|
function mutableSessions(ctx) {
|
|
@@ -67719,6 +67816,7 @@ class SessionMonitor {
|
|
|
67719
67816
|
}
|
|
67720
67817
|
// src/operations/plugin/handler.ts
|
|
67721
67818
|
import { spawn as spawn7 } from "child_process";
|
|
67819
|
+
init_logger();
|
|
67722
67820
|
var log27 = createLogger("plugin");
|
|
67723
67821
|
var sessionLog7 = createSessionLog(log27);
|
|
67724
67822
|
async function runPluginCommand(args, cwd, timeout2 = 60000) {
|
|
@@ -67939,6 +68037,7 @@ class SessionRegistry {
|
|
|
67939
68037
|
}
|
|
67940
68038
|
|
|
67941
68039
|
// src/session/manager.ts
|
|
68040
|
+
init_logger();
|
|
67942
68041
|
var log28 = createLogger("manager");
|
|
67943
68042
|
|
|
67944
68043
|
class SessionManager extends EventEmitter4 {
|
|
@@ -68710,6 +68809,40 @@ class SessionManager extends EventEmitter4 {
|
|
|
68710
68809
|
return;
|
|
68711
68810
|
await listWorktreesCommand(session);
|
|
68712
68811
|
}
|
|
68812
|
+
async listWorktreesWithoutSession(platformId, threadId) {
|
|
68813
|
+
const platform = this.platforms.get(platformId);
|
|
68814
|
+
if (!platform)
|
|
68815
|
+
return;
|
|
68816
|
+
const formatter = platform.getFormatter();
|
|
68817
|
+
const message = await buildWorktreeListMessageFromDir(this.workingDir, formatter, this.workingDir);
|
|
68818
|
+
if (message === null) {
|
|
68819
|
+
await platform.createPost(`❌ Current directory is not a git repository`, threadId);
|
|
68820
|
+
return;
|
|
68821
|
+
}
|
|
68822
|
+
await platform.createPost(message, threadId);
|
|
68823
|
+
}
|
|
68824
|
+
async switchToWorktreeWithoutSession(platformId, threadId, branchOrPath) {
|
|
68825
|
+
const platform = this.platforms.get(platformId);
|
|
68826
|
+
if (!platform)
|
|
68827
|
+
return;
|
|
68828
|
+
const formatter = platform.getFormatter();
|
|
68829
|
+
const { listWorktrees: listWorktrees2, getRepositoryRoot: getRepositoryRoot2, isGitRepository: isGitRepository2 } = await Promise.resolve().then(() => (init_worktree(), exports_worktree));
|
|
68830
|
+
const isRepo = await isGitRepository2(this.workingDir);
|
|
68831
|
+
if (!isRepo) {
|
|
68832
|
+
await platform.createPost(`❌ Current directory is not a git repository`, threadId);
|
|
68833
|
+
return;
|
|
68834
|
+
}
|
|
68835
|
+
const repoRoot = await getRepositoryRoot2(this.workingDir);
|
|
68836
|
+
const worktrees = await listWorktrees2(repoRoot);
|
|
68837
|
+
const target = worktrees.find((wt) => wt.branch === branchOrPath || wt.path === branchOrPath || wt.path.endsWith(`/${branchOrPath}`));
|
|
68838
|
+
if (!target) {
|
|
68839
|
+
await platform.createPost(`❌ No worktree found for ${formatter.formatCode(branchOrPath)}`, threadId);
|
|
68840
|
+
return;
|
|
68841
|
+
}
|
|
68842
|
+
await platform.createPost(`✅ Switched to worktree ${formatter.formatCode(target.branch)} at ${formatter.formatCode(target.path)}
|
|
68843
|
+
|
|
68844
|
+
Mention me to start a session in this worktree.`, threadId);
|
|
68845
|
+
}
|
|
68713
68846
|
async removeWorktreeCommand(threadId, branchOrPath, username) {
|
|
68714
68847
|
const session = this.findSessionByThreadId(threadId);
|
|
68715
68848
|
if (!session)
|
|
@@ -68782,7 +68915,7 @@ class SessionManager extends EventEmitter4 {
|
|
|
68782
68915
|
applySideConversationLimits(session) {
|
|
68783
68916
|
const MAX_COUNT = 5;
|
|
68784
68917
|
const MAX_TOTAL_CHARS = 2000;
|
|
68785
|
-
const MAX_AGE_MS =
|
|
68918
|
+
const MAX_AGE_MS = 1800000;
|
|
68786
68919
|
const now = Date.now();
|
|
68787
68920
|
let convs = session.pendingSideConversations || [];
|
|
68788
68921
|
convs = convs.filter((c) => now - c.timestamp.getTime() < MAX_AGE_MS);
|
|
@@ -68804,7 +68937,11 @@ class SessionManager extends EventEmitter4 {
|
|
|
68804
68937
|
const threadId = replyToPostId || "";
|
|
68805
68938
|
const session = this.registry.find(platformId, threadId);
|
|
68806
68939
|
if (session) {
|
|
68807
|
-
|
|
68940
|
+
if (initialOptions?.switchToExisting) {
|
|
68941
|
+
await this.switchToWorktree(session.threadId, branch, username);
|
|
68942
|
+
} else {
|
|
68943
|
+
await this.createAndSwitchToWorktree(session.threadId, branch, username);
|
|
68944
|
+
}
|
|
68808
68945
|
}
|
|
68809
68946
|
}
|
|
68810
68947
|
setShuttingDown() {
|
|
@@ -79307,6 +79444,9 @@ async function startUI(options2) {
|
|
|
79307
79444
|
return createUIProvider(options2);
|
|
79308
79445
|
}
|
|
79309
79446
|
|
|
79447
|
+
// src/index.ts
|
|
79448
|
+
init_logger();
|
|
79449
|
+
|
|
79310
79450
|
// src/message-handler.ts
|
|
79311
79451
|
async function handleMessage(client, session, post2, user, options2) {
|
|
79312
79452
|
const { platformId, logger, onKill } = options2;
|
|
@@ -79485,7 +79625,7 @@ async function handleMessage(client, session, post2, user, options2) {
|
|
|
79485
79625
|
prompt = prompt.replace(/on branch\s+\S+/i, "").trim();
|
|
79486
79626
|
}
|
|
79487
79627
|
}
|
|
79488
|
-
if (!prompt.trim() && !files?.length) {
|
|
79628
|
+
if (!prompt.trim() && !files?.length && !worktreeBranch) {
|
|
79489
79629
|
await client.createPost(`Mention me with your request`, threadRoot);
|
|
79490
79630
|
return;
|
|
79491
79631
|
}
|
|
@@ -79506,9 +79646,11 @@ async function handleMessage(client, session, post2, user, options2) {
|
|
|
79506
79646
|
}
|
|
79507
79647
|
|
|
79508
79648
|
// src/auto-update/manager.ts
|
|
79649
|
+
init_logger();
|
|
79509
79650
|
import { EventEmitter as EventEmitter9 } from "events";
|
|
79510
79651
|
|
|
79511
79652
|
// src/auto-update/checker.ts
|
|
79653
|
+
init_logger();
|
|
79512
79654
|
import { EventEmitter as EventEmitter7 } from "events";
|
|
79513
79655
|
var log29 = createLogger("checker");
|
|
79514
79656
|
var PACKAGE_NAME = "claude-threads";
|
|
@@ -79639,6 +79781,7 @@ class UpdateChecker extends EventEmitter7 {
|
|
|
79639
79781
|
}
|
|
79640
79782
|
|
|
79641
79783
|
// src/auto-update/scheduler.ts
|
|
79784
|
+
init_logger();
|
|
79642
79785
|
import { EventEmitter as EventEmitter8 } from "events";
|
|
79643
79786
|
|
|
79644
79787
|
// src/auto-update/types.ts
|
|
@@ -79906,6 +80049,7 @@ class UpdateScheduler extends EventEmitter8 {
|
|
|
79906
80049
|
}
|
|
79907
80050
|
|
|
79908
80051
|
// src/auto-update/installer.ts
|
|
80052
|
+
init_logger();
|
|
79909
80053
|
import { spawn as spawn8, spawnSync } from "child_process";
|
|
79910
80054
|
import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "fs";
|
|
79911
80055
|
import { dirname as dirname8, resolve as resolve6 } from "path";
|
|
@@ -38568,15 +38568,35 @@ var handleWorktree = async (ctx, args) => {
|
|
|
38568
38568
|
}
|
|
38569
38569
|
switch (subcommandOrBranch) {
|
|
38570
38570
|
case "list":
|
|
38571
|
-
|
|
38571
|
+
if (ctx.commandContext === "first-message") {
|
|
38572
|
+
await ctx.sessionManager.listWorktreesWithoutSession(ctx.client.platformId, ctx.threadId);
|
|
38573
|
+
} else {
|
|
38574
|
+
await ctx.sessionManager.listWorktreesCommand(ctx.threadId, ctx.username);
|
|
38575
|
+
}
|
|
38572
38576
|
return { handled: true };
|
|
38573
|
-
case "switch":
|
|
38577
|
+
case "switch": {
|
|
38574
38578
|
if (!subArgs) {
|
|
38575
38579
|
await ctx.client.createPost(`❌ Usage: ${ctx.formatter.formatCode("!worktree switch <branch>")}`, ctx.threadId);
|
|
38576
38580
|
return { handled: true };
|
|
38577
38581
|
}
|
|
38578
|
-
|
|
38582
|
+
const switchParts = subArgs.split(/\s+/);
|
|
38583
|
+
const branchName = switchParts[0];
|
|
38584
|
+
const remainingPrompt = switchParts.slice(1).join(" ").trim();
|
|
38585
|
+
if (ctx.commandContext === "first-message") {
|
|
38586
|
+
if (remainingPrompt) {
|
|
38587
|
+
return {
|
|
38588
|
+
worktreeBranch: branchName,
|
|
38589
|
+
continueProcessing: false,
|
|
38590
|
+
remainingText: remainingPrompt,
|
|
38591
|
+
sessionOptions: { switchToExisting: true }
|
|
38592
|
+
};
|
|
38593
|
+
}
|
|
38594
|
+
await ctx.sessionManager.switchToWorktreeWithoutSession(ctx.client.platformId, ctx.threadId, branchName);
|
|
38595
|
+
return { handled: true };
|
|
38596
|
+
}
|
|
38597
|
+
await ctx.sessionManager.switchToWorktree(ctx.threadId, branchName, ctx.username);
|
|
38579
38598
|
return { handled: true };
|
|
38599
|
+
}
|
|
38580
38600
|
case "remove":
|
|
38581
38601
|
if (!subArgs) {
|
|
38582
38602
|
await ctx.client.createPost(`❌ Usage: ${ctx.formatter.formatCode("!worktree remove <branch>")}`, ctx.threadId);
|