@staff0rd/assist 0.196.1 → 0.198.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/README.md +1 -0
- package/dist/commands/sessions/web/bundle.js +20 -20
- package/dist/index.js +606 -295
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.198.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -928,10 +928,10 @@ function writeSignal(event, data) {
|
|
|
928
928
|
|
|
929
929
|
// src/commands/backlog/readSignal.ts
|
|
930
930
|
function readSignal() {
|
|
931
|
-
const
|
|
932
|
-
if (!existsSync5(
|
|
931
|
+
const path53 = getSignalPath();
|
|
932
|
+
if (!existsSync5(path53)) return void 0;
|
|
933
933
|
try {
|
|
934
|
-
return JSON.parse(readFileSync5(
|
|
934
|
+
return JSON.parse(readFileSync5(path53, "utf-8"));
|
|
935
935
|
} catch {
|
|
936
936
|
return void 0;
|
|
937
937
|
}
|
|
@@ -1676,10 +1676,10 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
1676
1676
|
// src/shared/loadRawYaml.ts
|
|
1677
1677
|
import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
|
|
1678
1678
|
import { parse as parseYaml2 } from "yaml";
|
|
1679
|
-
function loadRawYaml(
|
|
1680
|
-
if (!existsSync8(
|
|
1679
|
+
function loadRawYaml(path53) {
|
|
1680
|
+
if (!existsSync8(path53)) return {};
|
|
1681
1681
|
try {
|
|
1682
|
-
const content = readFileSync7(
|
|
1682
|
+
const content = readFileSync7(path53, "utf-8");
|
|
1683
1683
|
return parseYaml2(content) || {};
|
|
1684
1684
|
} catch {
|
|
1685
1685
|
return {};
|
|
@@ -1845,6 +1845,17 @@ function loadConfig() {
|
|
|
1845
1845
|
const merged = { ...globalRaw, ...projectRaw };
|
|
1846
1846
|
return assistConfigSchema.parse(merged);
|
|
1847
1847
|
}
|
|
1848
|
+
function loadConfigFrom(startDir) {
|
|
1849
|
+
const found = findConfigUp(startDir);
|
|
1850
|
+
const configPath = found ?? join8(startDir, "assist.yml");
|
|
1851
|
+
const globalRaw = loadRawYaml(getGlobalConfigPath());
|
|
1852
|
+
const projectRaw = loadRawYaml(configPath);
|
|
1853
|
+
const merged = { ...globalRaw, ...projectRaw };
|
|
1854
|
+
return {
|
|
1855
|
+
config: assistConfigSchema.parse(merged),
|
|
1856
|
+
configDir: dirname2(configPath)
|
|
1857
|
+
};
|
|
1858
|
+
}
|
|
1848
1859
|
function loadProjectConfig() {
|
|
1849
1860
|
return loadRawYaml(getConfigPath());
|
|
1850
1861
|
}
|
|
@@ -2588,9 +2599,9 @@ function getSetupHandlers(hasVite, hasTypescript, hasOpenColor) {
|
|
|
2588
2599
|
}
|
|
2589
2600
|
|
|
2590
2601
|
// src/commands/verify/init/index.ts
|
|
2591
|
-
async function runSelectedSetups(selected, packageJsonPath, writer,
|
|
2602
|
+
async function runSelectedSetups(selected, packageJsonPath, writer, handlers2) {
|
|
2592
2603
|
for (const choice of selected) {
|
|
2593
|
-
await
|
|
2604
|
+
await handlers2[choice]?.(packageJsonPath, writer);
|
|
2594
2605
|
}
|
|
2595
2606
|
console.log(chalk33.green(`
|
|
2596
2607
|
Added ${selected.length} verify script(s):`));
|
|
@@ -2621,12 +2632,12 @@ async function init2() {
|
|
|
2621
2632
|
const selected = await promptForScripts(getAvailableOptions(setup2));
|
|
2622
2633
|
if (!selected) return;
|
|
2623
2634
|
const writer = setup2.hasConfigScripts ? setupVerifyRunEntry : (name, cmd) => setupVerifyScript(packageJsonPath, name, cmd);
|
|
2624
|
-
const
|
|
2635
|
+
const handlers2 = getSetupHandlers(
|
|
2625
2636
|
setup2.hasVite,
|
|
2626
2637
|
setup2.hasTypescript,
|
|
2627
2638
|
setup2.hasOpenColor
|
|
2628
2639
|
);
|
|
2629
|
-
await runSelectedSetups(selected, packageJsonPath, writer,
|
|
2640
|
+
await runSelectedSetups(selected, packageJsonPath, writer, handlers2);
|
|
2630
2641
|
}
|
|
2631
2642
|
|
|
2632
2643
|
// src/commands/vscode/init/index.ts
|
|
@@ -2746,14 +2757,14 @@ function applySelections(selected, setup2) {
|
|
|
2746
2757
|
removeVscodeFromGitignore();
|
|
2747
2758
|
ensureVscodeFolder();
|
|
2748
2759
|
const launchType = setup2.hasVite ? "vite" : "tsup";
|
|
2749
|
-
const
|
|
2760
|
+
const handlers2 = {
|
|
2750
2761
|
launch: () => createLaunchJson(launchType),
|
|
2751
2762
|
settings: () => {
|
|
2752
2763
|
createSettingsJson();
|
|
2753
2764
|
createExtensionsJson();
|
|
2754
2765
|
}
|
|
2755
2766
|
};
|
|
2756
|
-
for (const choice of selected)
|
|
2767
|
+
for (const choice of selected) handlers2[choice]?.();
|
|
2757
2768
|
}
|
|
2758
2769
|
async function promptForOptions(options2) {
|
|
2759
2770
|
console.log(chalk35.bold("Available VS Code configurations to add:\n"));
|
|
@@ -5047,9 +5058,9 @@ var __dirname4 = dirname15(__filename2);
|
|
|
5047
5058
|
function packageRoot() {
|
|
5048
5059
|
return __dirname4;
|
|
5049
5060
|
}
|
|
5050
|
-
function readLines(
|
|
5051
|
-
if (!existsSync20(
|
|
5052
|
-
return readFileSync16(
|
|
5061
|
+
function readLines(path53) {
|
|
5062
|
+
if (!existsSync20(path53)) return [];
|
|
5063
|
+
return readFileSync16(path53, "utf-8").split("\n").filter((line) => line.trim() !== "");
|
|
5053
5064
|
}
|
|
5054
5065
|
var cachedReads;
|
|
5055
5066
|
var cachedWrites;
|
|
@@ -5494,14 +5505,14 @@ function showProgress(p, label2) {
|
|
|
5494
5505
|
const pct = Math.round(p.done / p.total * 100);
|
|
5495
5506
|
process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
|
|
5496
5507
|
}
|
|
5497
|
-
async function resolveCommand(cli,
|
|
5498
|
-
showProgress(p,
|
|
5499
|
-
const subHelp = await runHelp([cli, ...
|
|
5508
|
+
async function resolveCommand(cli, path53, description, depth, p) {
|
|
5509
|
+
showProgress(p, path53.join(" "));
|
|
5510
|
+
const subHelp = await runHelp([cli, ...path53]);
|
|
5500
5511
|
if (!subHelp || !hasSubcommands(subHelp)) {
|
|
5501
|
-
return [{ path:
|
|
5512
|
+
return [{ path: path53, description }];
|
|
5502
5513
|
}
|
|
5503
|
-
const children = await discoverAt(cli,
|
|
5504
|
-
return children.length > 0 ? children : [{ path:
|
|
5514
|
+
const children = await discoverAt(cli, path53, depth + 1, p);
|
|
5515
|
+
return children.length > 0 ? children : [{ path: path53, description }];
|
|
5505
5516
|
}
|
|
5506
5517
|
async function discoverAt(cli, parentPath, depth, p) {
|
|
5507
5518
|
if (depth > SAFETY_DEPTH) return [];
|
|
@@ -5649,9 +5660,9 @@ function logPath(cli) {
|
|
|
5649
5660
|
return join20(homedir5(), ".assist", `cli-discover-${safeName}.log`);
|
|
5650
5661
|
}
|
|
5651
5662
|
function readCache(cli) {
|
|
5652
|
-
const
|
|
5653
|
-
if (!existsSync22(
|
|
5654
|
-
return readFileSync18(
|
|
5663
|
+
const path53 = logPath(cli);
|
|
5664
|
+
if (!existsSync22(path53)) return void 0;
|
|
5665
|
+
return readFileSync18(path53, "utf-8");
|
|
5655
5666
|
}
|
|
5656
5667
|
function writeCache(cli, output) {
|
|
5657
5668
|
const dir = join20(homedir5(), ".assist");
|
|
@@ -6293,8 +6304,8 @@ function stepIntoNested(container, key, nextKey) {
|
|
|
6293
6304
|
}
|
|
6294
6305
|
return ensureObject(container, resolved);
|
|
6295
6306
|
}
|
|
6296
|
-
function setNestedValue(obj,
|
|
6297
|
-
const keys =
|
|
6307
|
+
function setNestedValue(obj, path53, value) {
|
|
6308
|
+
const keys = path53.split(".");
|
|
6298
6309
|
const result = { ...obj };
|
|
6299
6310
|
let current = result;
|
|
6300
6311
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -6374,9 +6385,9 @@ function isTraversable(value) {
|
|
|
6374
6385
|
function stepInto(current, key) {
|
|
6375
6386
|
return isTraversable(current) ? current[key] : void 0;
|
|
6376
6387
|
}
|
|
6377
|
-
function getNestedValue(obj,
|
|
6388
|
+
function getNestedValue(obj, path53) {
|
|
6378
6389
|
let current = obj;
|
|
6379
|
-
for (const key of
|
|
6390
|
+
for (const key of path53.split(".")) current = stepInto(current, key);
|
|
6380
6391
|
return current;
|
|
6381
6392
|
}
|
|
6382
6393
|
|
|
@@ -7717,10 +7728,10 @@ function getStorePath(filename) {
|
|
|
7717
7728
|
return join27(getStoreDir(), filename);
|
|
7718
7729
|
}
|
|
7719
7730
|
function loadJson(filename) {
|
|
7720
|
-
const
|
|
7721
|
-
if (existsSync29(
|
|
7731
|
+
const path53 = getStorePath(filename);
|
|
7732
|
+
if (existsSync29(path53)) {
|
|
7722
7733
|
try {
|
|
7723
|
-
return JSON.parse(readFileSync25(
|
|
7734
|
+
return JSON.parse(readFileSync25(path53, "utf-8"));
|
|
7724
7735
|
} catch {
|
|
7725
7736
|
return {};
|
|
7726
7737
|
}
|
|
@@ -8167,7 +8178,7 @@ function validateLine(line) {
|
|
|
8167
8178
|
process.exit(1);
|
|
8168
8179
|
}
|
|
8169
8180
|
}
|
|
8170
|
-
function comment2(
|
|
8181
|
+
function comment2(path53, line, body) {
|
|
8171
8182
|
validateBody(body);
|
|
8172
8183
|
validateLine(line);
|
|
8173
8184
|
try {
|
|
@@ -8187,7 +8198,7 @@ function comment2(path52, line, body) {
|
|
|
8187
8198
|
"-f",
|
|
8188
8199
|
`body=${body}`,
|
|
8189
8200
|
"-f",
|
|
8190
|
-
`path=${
|
|
8201
|
+
`path=${path53}`,
|
|
8191
8202
|
"-F",
|
|
8192
8203
|
`line=${line}`
|
|
8193
8204
|
],
|
|
@@ -8196,7 +8207,7 @@ function comment2(path52, line, body) {
|
|
|
8196
8207
|
if (result.status !== 0) {
|
|
8197
8208
|
throw new Error(result.stderr || result.stdout);
|
|
8198
8209
|
}
|
|
8199
|
-
console.log(`Added review comment on ${
|
|
8210
|
+
console.log(`Added review comment on ${path53}:${line}`);
|
|
8200
8211
|
} finally {
|
|
8201
8212
|
unlinkSync6(queryFile);
|
|
8202
8213
|
}
|
|
@@ -8695,8 +8706,8 @@ function registerPrs(program2) {
|
|
|
8695
8706
|
prsCommand.command("wontfix <comment-id> <reason>").description("Reply with reason and resolve thread").action((commentId, reason) => {
|
|
8696
8707
|
wontfix(Number.parseInt(commentId, 10), reason);
|
|
8697
8708
|
});
|
|
8698
|
-
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((
|
|
8699
|
-
comment2(
|
|
8709
|
+
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path53, line, body) => {
|
|
8710
|
+
comment2(path53, Number.parseInt(line, 10), body);
|
|
8700
8711
|
});
|
|
8701
8712
|
}
|
|
8702
8713
|
|
|
@@ -8948,10 +8959,10 @@ function resolveOpSecret(reference) {
|
|
|
8948
8959
|
}
|
|
8949
8960
|
|
|
8950
8961
|
// src/commands/ravendb/ravenFetch.ts
|
|
8951
|
-
async function ravenFetch(connection,
|
|
8962
|
+
async function ravenFetch(connection, path53) {
|
|
8952
8963
|
const apiKey = resolveOpSecret(connection.apiKeyRef);
|
|
8953
8964
|
let accessToken = await getAccessToken(apiKey);
|
|
8954
|
-
const url = `${connection.url}${
|
|
8965
|
+
const url = `${connection.url}${path53}`;
|
|
8955
8966
|
const headers = {
|
|
8956
8967
|
Authorization: `Bearer ${accessToken}`,
|
|
8957
8968
|
"Content-Type": "application/json"
|
|
@@ -9041,16 +9052,16 @@ import chalk112 from "chalk";
|
|
|
9041
9052
|
// src/commands/ravendb/buildQueryPath.ts
|
|
9042
9053
|
function buildQueryPath(opts) {
|
|
9043
9054
|
const db = encodeURIComponent(opts.db);
|
|
9044
|
-
let
|
|
9055
|
+
let path53;
|
|
9045
9056
|
if (opts.collection) {
|
|
9046
|
-
|
|
9057
|
+
path53 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
|
|
9047
9058
|
} else {
|
|
9048
|
-
|
|
9059
|
+
path53 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
|
|
9049
9060
|
}
|
|
9050
9061
|
if (opts.query) {
|
|
9051
|
-
|
|
9062
|
+
path53 += `&query=${encodeURIComponent(opts.query)}`;
|
|
9052
9063
|
}
|
|
9053
|
-
return
|
|
9064
|
+
return path53;
|
|
9054
9065
|
}
|
|
9055
9066
|
|
|
9056
9067
|
// src/commands/ravendb/fetchAllPages.ts
|
|
@@ -9059,7 +9070,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
9059
9070
|
let start3 = 0;
|
|
9060
9071
|
while (true) {
|
|
9061
9072
|
const effectivePageSize = opts.limit !== void 0 ? Math.min(opts.pageSize, opts.limit - allResults.length) : opts.pageSize;
|
|
9062
|
-
const
|
|
9073
|
+
const path53 = buildQueryPath({
|
|
9063
9074
|
db: connection.database,
|
|
9064
9075
|
collection: opts.collection,
|
|
9065
9076
|
start: start3,
|
|
@@ -9067,7 +9078,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
9067
9078
|
sort: opts.sort,
|
|
9068
9079
|
query: opts.query
|
|
9069
9080
|
});
|
|
9070
|
-
const data = await ravenFetch(connection,
|
|
9081
|
+
const data = await ravenFetch(connection, path53);
|
|
9071
9082
|
const results = data.Results ?? [];
|
|
9072
9083
|
const totalResults = data.TotalResults ?? 0;
|
|
9073
9084
|
if (results.length === 0) break;
|
|
@@ -10238,8 +10249,8 @@ function findRootParent(file, importedBy, visited) {
|
|
|
10238
10249
|
function clusterFiles(graph) {
|
|
10239
10250
|
const clusters = /* @__PURE__ */ new Map();
|
|
10240
10251
|
for (const file of graph.files) {
|
|
10241
|
-
const
|
|
10242
|
-
if (
|
|
10252
|
+
const basename10 = path39.basename(file, path39.extname(file));
|
|
10253
|
+
if (basename10 === "index") continue;
|
|
10243
10254
|
const importers = graph.importedBy.get(file);
|
|
10244
10255
|
if (!importers || importers.size !== 1) continue;
|
|
10245
10256
|
const parent = [...importers][0];
|
|
@@ -10689,8 +10700,8 @@ import chalk129 from "chalk";
|
|
|
10689
10700
|
|
|
10690
10701
|
// src/commands/seq/fetchSeq.ts
|
|
10691
10702
|
import chalk126 from "chalk";
|
|
10692
|
-
async function fetchSeq(conn,
|
|
10693
|
-
const url = `${conn.url}${
|
|
10703
|
+
async function fetchSeq(conn, path53, params) {
|
|
10704
|
+
const url = `${conn.url}${path53}?${params}`;
|
|
10694
10705
|
const response = await fetch(url, {
|
|
10695
10706
|
headers: {
|
|
10696
10707
|
Accept: "application/json",
|
|
@@ -12340,11 +12351,11 @@ function findLinkIndex() {
|
|
|
12340
12351
|
function parseLinkArgs() {
|
|
12341
12352
|
const idx = findLinkIndex();
|
|
12342
12353
|
if (idx === -1) return null;
|
|
12343
|
-
const
|
|
12354
|
+
const path53 = process.argv[idx + 1];
|
|
12344
12355
|
const rest = process.argv.slice(idx + 2);
|
|
12345
12356
|
const { value: prefix2 } = extractOption(rest, "--prefix");
|
|
12346
12357
|
if (!prefix2) return null;
|
|
12347
|
-
return { path:
|
|
12358
|
+
return { path: path53, prefix: prefix2 };
|
|
12348
12359
|
}
|
|
12349
12360
|
function hasDuplicateLink(runList, linkPath) {
|
|
12350
12361
|
return runList.some(
|
|
@@ -12590,11 +12601,319 @@ function screenshot(processName) {
|
|
|
12590
12601
|
}
|
|
12591
12602
|
}
|
|
12592
12603
|
|
|
12604
|
+
// src/commands/sessions/summarise/index.ts
|
|
12605
|
+
import * as fs27 from "fs";
|
|
12606
|
+
import chalk134 from "chalk";
|
|
12607
|
+
|
|
12608
|
+
// src/commands/sessions/web/discoverSessions.ts
|
|
12609
|
+
import * as fs24 from "fs";
|
|
12610
|
+
import * as os from "os";
|
|
12611
|
+
import * as path47 from "path";
|
|
12612
|
+
|
|
12613
|
+
// src/commands/sessions/web/parseSessionFile.ts
|
|
12614
|
+
import * as fs23 from "fs";
|
|
12615
|
+
import * as path46 from "path";
|
|
12616
|
+
|
|
12617
|
+
// src/commands/sessions/web/extractSessionMeta.ts
|
|
12618
|
+
function extractSessionMeta(lines) {
|
|
12619
|
+
let sessionId = "";
|
|
12620
|
+
let cwd = "";
|
|
12621
|
+
let timestamp = "";
|
|
12622
|
+
let name = "";
|
|
12623
|
+
for (const line of lines) {
|
|
12624
|
+
const entry = safeParse(line);
|
|
12625
|
+
if (!entry) continue;
|
|
12626
|
+
sessionId ||= typeof entry.sessionId === "string" ? entry.sessionId : "";
|
|
12627
|
+
timestamp ||= typeof entry.timestamp === "string" ? entry.timestamp : "";
|
|
12628
|
+
cwd ||= typeof entry.cwd === "string" ? entry.cwd : "";
|
|
12629
|
+
if (entry.type === "user" && !entry.isMeta) {
|
|
12630
|
+
name = extractName(entry);
|
|
12631
|
+
break;
|
|
12632
|
+
}
|
|
12633
|
+
}
|
|
12634
|
+
return { sessionId, cwd, timestamp, name };
|
|
12635
|
+
}
|
|
12636
|
+
function safeParse(line) {
|
|
12637
|
+
try {
|
|
12638
|
+
return JSON.parse(line);
|
|
12639
|
+
} catch {
|
|
12640
|
+
return null;
|
|
12641
|
+
}
|
|
12642
|
+
}
|
|
12643
|
+
function extractName(entry) {
|
|
12644
|
+
const msg = entry.message;
|
|
12645
|
+
const content = msg?.content;
|
|
12646
|
+
const text = typeof content === "string" ? content : Array.isArray(content) ? content.find((c) => c.type === "text")?.text ?? "" : "";
|
|
12647
|
+
return text.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
|
|
12648
|
+
}
|
|
12649
|
+
|
|
12650
|
+
// src/commands/sessions/web/parseSessionFile.ts
|
|
12651
|
+
async function parseSessionFile(filePath) {
|
|
12652
|
+
let handle;
|
|
12653
|
+
try {
|
|
12654
|
+
handle = await fs23.promises.open(filePath, "r");
|
|
12655
|
+
const buf = Buffer.alloc(16384);
|
|
12656
|
+
const { bytesRead } = await handle.read(buf, 0, buf.length, 0);
|
|
12657
|
+
const lines = buf.toString("utf8", 0, bytesRead).split("\n").filter(Boolean);
|
|
12658
|
+
const meta = extractSessionMeta(lines);
|
|
12659
|
+
if (!meta.sessionId) return null;
|
|
12660
|
+
const timestamp = meta.timestamp || (await fs23.promises.stat(filePath)).mtime.toISOString();
|
|
12661
|
+
const project = meta.cwd ? path46.basename(meta.cwd) : dirNameToProject(filePath);
|
|
12662
|
+
return {
|
|
12663
|
+
sessionId: meta.sessionId,
|
|
12664
|
+
name: meta.name || `Session ${meta.sessionId.slice(0, 8)}`,
|
|
12665
|
+
project,
|
|
12666
|
+
cwd: meta.cwd,
|
|
12667
|
+
timestamp
|
|
12668
|
+
};
|
|
12669
|
+
} catch {
|
|
12670
|
+
return null;
|
|
12671
|
+
} finally {
|
|
12672
|
+
await handle?.close();
|
|
12673
|
+
}
|
|
12674
|
+
}
|
|
12675
|
+
function dirNameToProject(filePath) {
|
|
12676
|
+
const dirName = path46.basename(path46.dirname(filePath));
|
|
12677
|
+
const parts = dirName.split("--");
|
|
12678
|
+
return parts[parts.length - 1].replace(/-/g, "/");
|
|
12679
|
+
}
|
|
12680
|
+
|
|
12681
|
+
// src/commands/sessions/web/discoverSessions.ts
|
|
12682
|
+
async function discoverSessionJsonlPaths() {
|
|
12683
|
+
const projectsDir = path47.join(os.homedir(), ".claude", "projects");
|
|
12684
|
+
let projectDirs;
|
|
12685
|
+
try {
|
|
12686
|
+
projectDirs = await fs24.promises.readdir(projectsDir);
|
|
12687
|
+
} catch {
|
|
12688
|
+
return [];
|
|
12689
|
+
}
|
|
12690
|
+
const paths = [];
|
|
12691
|
+
await Promise.all(
|
|
12692
|
+
projectDirs.map(async (dirName) => {
|
|
12693
|
+
const dirPath = path47.join(projectsDir, dirName);
|
|
12694
|
+
let entries;
|
|
12695
|
+
try {
|
|
12696
|
+
entries = await fs24.promises.readdir(dirPath);
|
|
12697
|
+
} catch {
|
|
12698
|
+
return;
|
|
12699
|
+
}
|
|
12700
|
+
const jsonlFiles = entries.filter((e) => e.endsWith(".jsonl"));
|
|
12701
|
+
for (const file of jsonlFiles) {
|
|
12702
|
+
paths.push(path47.join(dirPath, file));
|
|
12703
|
+
}
|
|
12704
|
+
})
|
|
12705
|
+
);
|
|
12706
|
+
return paths;
|
|
12707
|
+
}
|
|
12708
|
+
async function discoverSessions() {
|
|
12709
|
+
const paths = await discoverSessionJsonlPaths();
|
|
12710
|
+
const sessions = [];
|
|
12711
|
+
await Promise.all(
|
|
12712
|
+
paths.map(async (filePath) => {
|
|
12713
|
+
const session = await parseSessionFile(filePath);
|
|
12714
|
+
if (session) sessions.push(session);
|
|
12715
|
+
})
|
|
12716
|
+
);
|
|
12717
|
+
sessions.sort(
|
|
12718
|
+
(a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
|
12719
|
+
);
|
|
12720
|
+
return sessions;
|
|
12721
|
+
}
|
|
12722
|
+
|
|
12723
|
+
// src/commands/sessions/summarise/shared.ts
|
|
12724
|
+
import * as fs25 from "fs";
|
|
12725
|
+
function writeSummary(jsonlPath, summary) {
|
|
12726
|
+
fs25.writeFileSync(summaryPathFor(jsonlPath), `${summary.trim()}
|
|
12727
|
+
`, "utf8");
|
|
12728
|
+
}
|
|
12729
|
+
function hasSummary(jsonlPath) {
|
|
12730
|
+
return fs25.existsSync(summaryPathFor(jsonlPath));
|
|
12731
|
+
}
|
|
12732
|
+
function summaryPathFor(jsonlPath) {
|
|
12733
|
+
return jsonlPath.replace(/\.jsonl$/, ".summary");
|
|
12734
|
+
}
|
|
12735
|
+
|
|
12736
|
+
// src/commands/sessions/summarise/summariseSession.ts
|
|
12737
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
12738
|
+
|
|
12739
|
+
// src/commands/sessions/summarise/iterateUserMessages.ts
|
|
12740
|
+
import * as fs26 from "fs";
|
|
12741
|
+
function* iterateUserMessages(filePath, maxBytes = 65536) {
|
|
12742
|
+
let content;
|
|
12743
|
+
try {
|
|
12744
|
+
const fd = fs26.openSync(filePath, "r");
|
|
12745
|
+
try {
|
|
12746
|
+
const buf = Buffer.alloc(maxBytes);
|
|
12747
|
+
const bytesRead = fs26.readSync(fd, buf, 0, buf.length, 0);
|
|
12748
|
+
content = buf.toString("utf8", 0, bytesRead);
|
|
12749
|
+
} finally {
|
|
12750
|
+
fs26.closeSync(fd);
|
|
12751
|
+
}
|
|
12752
|
+
} catch {
|
|
12753
|
+
return;
|
|
12754
|
+
}
|
|
12755
|
+
for (const line of content.split("\n")) {
|
|
12756
|
+
if (!line) continue;
|
|
12757
|
+
let entry;
|
|
12758
|
+
try {
|
|
12759
|
+
entry = JSON.parse(line);
|
|
12760
|
+
} catch {
|
|
12761
|
+
continue;
|
|
12762
|
+
}
|
|
12763
|
+
if (entry.type !== "user") continue;
|
|
12764
|
+
const msg = entry.message;
|
|
12765
|
+
const c = msg?.content;
|
|
12766
|
+
if (typeof c === "string") {
|
|
12767
|
+
yield c;
|
|
12768
|
+
} else if (Array.isArray(c)) {
|
|
12769
|
+
const text = c.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n");
|
|
12770
|
+
if (text) yield text;
|
|
12771
|
+
}
|
|
12772
|
+
}
|
|
12773
|
+
}
|
|
12774
|
+
|
|
12775
|
+
// src/commands/sessions/summarise/extractFirstUserMessage.ts
|
|
12776
|
+
function extractFirstUserMessage(filePath) {
|
|
12777
|
+
for (const text of iterateUserMessages(filePath)) {
|
|
12778
|
+
return truncate2(text);
|
|
12779
|
+
}
|
|
12780
|
+
return void 0;
|
|
12781
|
+
}
|
|
12782
|
+
function truncate2(text, maxChars = 500) {
|
|
12783
|
+
const trimmed = text.trim();
|
|
12784
|
+
if (trimmed.length <= maxChars) return trimmed;
|
|
12785
|
+
return `${trimmed.slice(0, maxChars)}\u2026`;
|
|
12786
|
+
}
|
|
12787
|
+
|
|
12788
|
+
// src/commands/sessions/summarise/scanSessionBacklogRefs.ts
|
|
12789
|
+
function scanSessionBacklogRefs(filePath) {
|
|
12790
|
+
const ids = /* @__PURE__ */ new Set();
|
|
12791
|
+
for (const text of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
|
|
12792
|
+
for (const id of extractBacklogIds(text)) {
|
|
12793
|
+
ids.add(id);
|
|
12794
|
+
}
|
|
12795
|
+
}
|
|
12796
|
+
return [...ids].sort((a, b) => a - b);
|
|
12797
|
+
}
|
|
12798
|
+
function extractBacklogIds(text) {
|
|
12799
|
+
const ids = [];
|
|
12800
|
+
for (const m of text.matchAll(/backlog\s+run\s+(\d+)/gi)) {
|
|
12801
|
+
ids.push(Number.parseInt(m[1], 10));
|
|
12802
|
+
}
|
|
12803
|
+
for (const m of text.matchAll(/backlog\s+(?:item\s+)?#(\d+)/gi)) {
|
|
12804
|
+
ids.push(Number.parseInt(m[1], 10));
|
|
12805
|
+
}
|
|
12806
|
+
for (const m of text.matchAll(/backlog\s+phase-done\s+(\d+)/gi)) {
|
|
12807
|
+
ids.push(Number.parseInt(m[1], 10));
|
|
12808
|
+
}
|
|
12809
|
+
for (const m of text.matchAll(/backlog\s+comment\s+(\d+)/gi)) {
|
|
12810
|
+
ids.push(Number.parseInt(m[1], 10));
|
|
12811
|
+
}
|
|
12812
|
+
for (const m of text.matchAll(/(?:^|[\s(])#(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
|
|
12813
|
+
ids.push(Number.parseInt(m[1], 10));
|
|
12814
|
+
}
|
|
12815
|
+
return ids;
|
|
12816
|
+
}
|
|
12817
|
+
|
|
12818
|
+
// src/commands/sessions/summarise/summariseSession.ts
|
|
12819
|
+
function summariseSession(jsonlPath) {
|
|
12820
|
+
const firstMessage = extractFirstUserMessage(jsonlPath);
|
|
12821
|
+
const backlogIds = scanSessionBacklogRefs(jsonlPath);
|
|
12822
|
+
if (!firstMessage && backlogIds.length === 0) {
|
|
12823
|
+
return void 0;
|
|
12824
|
+
}
|
|
12825
|
+
const prompt = buildPrompt2(firstMessage, backlogIds);
|
|
12826
|
+
try {
|
|
12827
|
+
const output = execFileSync2("claude", ["-p", "--model", "haiku", prompt], {
|
|
12828
|
+
encoding: "utf8",
|
|
12829
|
+
timeout: 3e4,
|
|
12830
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
12831
|
+
});
|
|
12832
|
+
const summary = output.trim();
|
|
12833
|
+
if (!summary) return void 0;
|
|
12834
|
+
return summary.split("\n")[0].replace(/^["']|["']$/g, "").trim();
|
|
12835
|
+
} catch {
|
|
12836
|
+
return void 0;
|
|
12837
|
+
}
|
|
12838
|
+
}
|
|
12839
|
+
function buildPrompt2(firstMessage, backlogIds) {
|
|
12840
|
+
const parts = [
|
|
12841
|
+
"Summarise this Claude Code session in ONE short sentence (under 100 chars).",
|
|
12842
|
+
"Return ONLY the summary, no quotes or explanation."
|
|
12843
|
+
];
|
|
12844
|
+
if (backlogIds.length > 0) {
|
|
12845
|
+
const refs = backlogIds.map((id) => `#${id}`).join(", ");
|
|
12846
|
+
parts.push(
|
|
12847
|
+
`The session references backlog item(s) ${refs}. Start the summary with "Backlog ${refs} \u2014" if relevant.`
|
|
12848
|
+
);
|
|
12849
|
+
}
|
|
12850
|
+
if (firstMessage) {
|
|
12851
|
+
parts.push(`First user message:
|
|
12852
|
+
${firstMessage}`);
|
|
12853
|
+
}
|
|
12854
|
+
return parts.join("\n");
|
|
12855
|
+
}
|
|
12856
|
+
|
|
12857
|
+
// src/commands/sessions/summarise/index.ts
|
|
12858
|
+
async function summarise3(options2) {
|
|
12859
|
+
const files = await discoverSessionJsonlPaths();
|
|
12860
|
+
if (files.length === 0) {
|
|
12861
|
+
console.log(chalk134.yellow("No sessions found."));
|
|
12862
|
+
return;
|
|
12863
|
+
}
|
|
12864
|
+
const toProcess = selectCandidates(files, options2);
|
|
12865
|
+
if (toProcess.length === 0) {
|
|
12866
|
+
console.log(chalk134.green("All sessions already summarised."));
|
|
12867
|
+
return;
|
|
12868
|
+
}
|
|
12869
|
+
console.log(
|
|
12870
|
+
chalk134.cyan(
|
|
12871
|
+
`Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
|
|
12872
|
+
)
|
|
12873
|
+
);
|
|
12874
|
+
const { succeeded, failed } = processSessions(toProcess);
|
|
12875
|
+
console.log(
|
|
12876
|
+
chalk134.green(`Done: ${succeeded} summarised`) + (failed > 0 ? chalk134.yellow(`, ${failed} skipped`) : "")
|
|
12877
|
+
);
|
|
12878
|
+
}
|
|
12879
|
+
function selectCandidates(files, options2) {
|
|
12880
|
+
const candidates = options2.force ? files : files.filter((f) => !hasSummary(f));
|
|
12881
|
+
candidates.sort((a, b) => {
|
|
12882
|
+
try {
|
|
12883
|
+
return fs27.statSync(b).mtimeMs - fs27.statSync(a).mtimeMs;
|
|
12884
|
+
} catch {
|
|
12885
|
+
return 0;
|
|
12886
|
+
}
|
|
12887
|
+
});
|
|
12888
|
+
const limit = options2.limit ? Number.parseInt(options2.limit, 10) : void 0;
|
|
12889
|
+
return limit && limit > 0 ? candidates.slice(0, limit) : candidates;
|
|
12890
|
+
}
|
|
12891
|
+
function processSessions(files) {
|
|
12892
|
+
let succeeded = 0;
|
|
12893
|
+
let failed = 0;
|
|
12894
|
+
for (let i = 0; i < files.length; i++) {
|
|
12895
|
+
const file = files[i];
|
|
12896
|
+
process.stdout.write(chalk134.dim(` [${i + 1}/${files.length}] `));
|
|
12897
|
+
const summary = summariseSession(file);
|
|
12898
|
+
if (summary) {
|
|
12899
|
+
writeSummary(file, summary);
|
|
12900
|
+
succeeded++;
|
|
12901
|
+
process.stdout.write(`${chalk134.green("\u2713")} ${summary}
|
|
12902
|
+
`);
|
|
12903
|
+
} else {
|
|
12904
|
+
failed++;
|
|
12905
|
+
process.stdout.write(` ${chalk134.yellow("skip")}
|
|
12906
|
+
`);
|
|
12907
|
+
}
|
|
12908
|
+
}
|
|
12909
|
+
return { succeeded, failed };
|
|
12910
|
+
}
|
|
12911
|
+
|
|
12593
12912
|
// src/commands/sessions/web/index.ts
|
|
12594
12913
|
import { WebSocketServer } from "ws";
|
|
12595
12914
|
|
|
12596
12915
|
// src/commands/sessions/web/handleRequest.ts
|
|
12597
|
-
import { readFileSync as
|
|
12916
|
+
import { readFileSync as readFileSync35 } from "fs";
|
|
12598
12917
|
import { createRequire as createRequire2 } from "module";
|
|
12599
12918
|
|
|
12600
12919
|
// src/commands/sessions/web/getHtml.ts
|
|
@@ -12629,7 +12948,7 @@ function createCssHandler(packageEntry) {
|
|
|
12629
12948
|
return (_req, res) => {
|
|
12630
12949
|
if (!cache) {
|
|
12631
12950
|
const resolved = require3.resolve(packageEntry);
|
|
12632
|
-
cache =
|
|
12951
|
+
cache = readFileSync35(resolved, "utf-8");
|
|
12633
12952
|
}
|
|
12634
12953
|
res.writeHead(200, { "Content-Type": "text/css" });
|
|
12635
12954
|
res.end(cache);
|
|
@@ -12646,46 +12965,79 @@ var routes3 = {
|
|
|
12646
12965
|
};
|
|
12647
12966
|
var handleRequest3 = createFallbackHandler(routes3, htmlHandler2);
|
|
12648
12967
|
|
|
12649
|
-
// src/commands/sessions/web/
|
|
12650
|
-
function
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
break;
|
|
12663
|
-
case "resize":
|
|
12664
|
-
manager.resizeSession(
|
|
12665
|
-
data.sessionId,
|
|
12666
|
-
data.cols,
|
|
12667
|
-
data.rows
|
|
12668
|
-
);
|
|
12669
|
-
break;
|
|
12670
|
-
case "resume": {
|
|
12671
|
-
const id = manager.resume(
|
|
12672
|
-
data.sessionId,
|
|
12673
|
-
data.cwd,
|
|
12674
|
-
data.name
|
|
12675
|
-
);
|
|
12676
|
-
ws.send(JSON.stringify({ type: "created", sessionId: id }));
|
|
12677
|
-
break;
|
|
12678
|
-
}
|
|
12679
|
-
case "dismiss":
|
|
12680
|
-
manager.dismissSession(data.sessionId);
|
|
12681
|
-
break;
|
|
12682
|
-
case "history":
|
|
12683
|
-
manager.getHistory().then((history) => {
|
|
12684
|
-
ws.send(JSON.stringify({ type: "history", sessions: history }));
|
|
12685
|
-
});
|
|
12686
|
-
break;
|
|
12968
|
+
// src/commands/sessions/web/handleRunConfigs.ts
|
|
12969
|
+
function handleRunConfigs(ws, cwd) {
|
|
12970
|
+
try {
|
|
12971
|
+
const { config, configDir } = loadConfigFrom(cwd);
|
|
12972
|
+
const configs = resolveRunConfigs(config.run, configDir);
|
|
12973
|
+
ws.send(
|
|
12974
|
+
JSON.stringify({
|
|
12975
|
+
type: "run-configs",
|
|
12976
|
+
configs: configs.map(({ name, params }) => ({ name, params }))
|
|
12977
|
+
})
|
|
12978
|
+
);
|
|
12979
|
+
} catch {
|
|
12980
|
+
ws.send(JSON.stringify({ type: "run-configs", configs: [] }));
|
|
12687
12981
|
}
|
|
12688
12982
|
}
|
|
12983
|
+
|
|
12984
|
+
// src/commands/sessions/web/dispatchMessage.ts
|
|
12985
|
+
function sendCreated(ws, id) {
|
|
12986
|
+
ws.send(JSON.stringify({ type: "created", sessionId: id }));
|
|
12987
|
+
}
|
|
12988
|
+
function handleCreate(ws, manager, data) {
|
|
12989
|
+
sendCreated(
|
|
12990
|
+
ws,
|
|
12991
|
+
manager.spawn(
|
|
12992
|
+
data.prompt,
|
|
12993
|
+
data.cwd
|
|
12994
|
+
)
|
|
12995
|
+
);
|
|
12996
|
+
}
|
|
12997
|
+
function handleCreateRun(ws, manager, data) {
|
|
12998
|
+
sendCreated(
|
|
12999
|
+
ws,
|
|
13000
|
+
manager.spawnRun(
|
|
13001
|
+
data.runName,
|
|
13002
|
+
data.runArgs ?? [],
|
|
13003
|
+
data.cwd
|
|
13004
|
+
)
|
|
13005
|
+
);
|
|
13006
|
+
}
|
|
13007
|
+
function handleResume(ws, manager, data) {
|
|
13008
|
+
sendCreated(
|
|
13009
|
+
ws,
|
|
13010
|
+
manager.resume(
|
|
13011
|
+
data.sessionId,
|
|
13012
|
+
data.cwd,
|
|
13013
|
+
data.name
|
|
13014
|
+
)
|
|
13015
|
+
);
|
|
13016
|
+
}
|
|
13017
|
+
function runConfigs(ws, _manager, data) {
|
|
13018
|
+
handleRunConfigs(ws, data.cwd);
|
|
13019
|
+
}
|
|
13020
|
+
function handleHistory(ws, manager) {
|
|
13021
|
+
manager.getHistory().then((history) => {
|
|
13022
|
+
ws.send(JSON.stringify({ type: "history", sessions: history }));
|
|
13023
|
+
});
|
|
13024
|
+
}
|
|
13025
|
+
var handlers = {
|
|
13026
|
+
create: handleCreate,
|
|
13027
|
+
"create-run": handleCreateRun,
|
|
13028
|
+
resume: handleResume,
|
|
13029
|
+
"run-configs": runConfigs,
|
|
13030
|
+
history: handleHistory,
|
|
13031
|
+
input: (_ws, m, d) => m.writeToSession(d.sessionId, d.data),
|
|
13032
|
+
resize: (_ws, m, d) => m.resizeSession(d.sessionId, d.cols, d.rows),
|
|
13033
|
+
retry: (_ws, m, d) => m.retrySession(d.sessionId),
|
|
13034
|
+
dismiss: (_ws, m, d) => m.dismissSession(d.sessionId)
|
|
13035
|
+
};
|
|
13036
|
+
function dispatchMessage(ws, manager, data) {
|
|
13037
|
+
handlers[data.type]?.(ws, manager, data);
|
|
13038
|
+
}
|
|
13039
|
+
|
|
13040
|
+
// src/commands/sessions/web/handleSocket.ts
|
|
12689
13041
|
function handleSocket(ws, manager) {
|
|
12690
13042
|
manager.addClient(ws);
|
|
12691
13043
|
ws.on("message", (msg) => {
|
|
@@ -12695,42 +13047,58 @@ function handleSocket(ws, manager) {
|
|
|
12695
13047
|
} catch {
|
|
12696
13048
|
return;
|
|
12697
13049
|
}
|
|
12698
|
-
|
|
13050
|
+
dispatchMessage(ws, manager, data);
|
|
12699
13051
|
});
|
|
12700
13052
|
ws.on("close", () => {
|
|
12701
13053
|
manager.removeClient(ws);
|
|
12702
13054
|
});
|
|
12703
13055
|
}
|
|
12704
13056
|
|
|
12705
|
-
// src/commands/sessions/web/
|
|
13057
|
+
// src/commands/sessions/web/repoPrefix.ts
|
|
13058
|
+
import * as path48 from "path";
|
|
13059
|
+
function repoPrefix(cwd) {
|
|
13060
|
+
if (!cwd) return "";
|
|
13061
|
+
return `${path48.basename(cwd)}/`;
|
|
13062
|
+
}
|
|
13063
|
+
|
|
13064
|
+
// src/commands/sessions/web/spawnPty.ts
|
|
12706
13065
|
import * as pty from "node-pty";
|
|
12707
|
-
function
|
|
13066
|
+
function spawnPty(args, cwd) {
|
|
12708
13067
|
const shell = process.platform === "win32" ? "cmd.exe" : process.env.SHELL ?? "bash";
|
|
12709
|
-
const
|
|
12710
|
-
return pty.spawn(shell,
|
|
13068
|
+
const shellArgs = process.platform === "win32" ? ["/c", ...args] : ["-c", `exec ${args.map(shellEscape).join(" ")}`];
|
|
13069
|
+
return pty.spawn(shell, shellArgs, {
|
|
12711
13070
|
name: "xterm-256color",
|
|
12712
13071
|
cols: 120,
|
|
12713
13072
|
rows: 30,
|
|
12714
|
-
cwd:
|
|
13073
|
+
cwd: cwd ?? process.cwd(),
|
|
12715
13074
|
env: { ...process.env }
|
|
12716
13075
|
});
|
|
12717
13076
|
}
|
|
12718
|
-
function buildArgs(opts) {
|
|
12719
|
-
const claudeArgs = opts.resumeSessionId ? ["claude", "--resume", opts.resumeSessionId] : opts.prompt ? ["claude", opts.prompt] : ["claude"];
|
|
12720
|
-
if (process.platform === "win32") {
|
|
12721
|
-
return ["/c", ...claudeArgs];
|
|
12722
|
-
}
|
|
12723
|
-
return ["-c", `exec ${claudeArgs.map(shellEscape).join(" ")}`];
|
|
12724
|
-
}
|
|
12725
13077
|
function shellEscape(s) {
|
|
12726
13078
|
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
12727
13079
|
}
|
|
12728
13080
|
|
|
13081
|
+
// src/commands/sessions/web/spawnClaude.ts
|
|
13082
|
+
function spawnClaude2(opts = {}) {
|
|
13083
|
+
return spawnPty(buildArgs(opts), opts.cwd);
|
|
13084
|
+
}
|
|
13085
|
+
function buildArgs(opts) {
|
|
13086
|
+
if (opts.resumeSessionId) return ["claude", "--resume", opts.resumeSessionId];
|
|
13087
|
+
if (opts.prompt) return ["claude", opts.prompt];
|
|
13088
|
+
return ["claude"];
|
|
13089
|
+
}
|
|
13090
|
+
|
|
13091
|
+
// src/commands/sessions/web/spawnRun.ts
|
|
13092
|
+
function spawnRun(opts) {
|
|
13093
|
+
return spawnPty(["assist", "run", opts.name, ...opts.args ?? []], opts.cwd);
|
|
13094
|
+
}
|
|
13095
|
+
|
|
12729
13096
|
// src/commands/sessions/web/createSession.ts
|
|
12730
13097
|
function createSession(id, prompt, cwd) {
|
|
12731
13098
|
return {
|
|
12732
13099
|
id,
|
|
12733
|
-
name: prompt?.slice(0, 40) || `Session ${id}`,
|
|
13100
|
+
name: `${repoPrefix(cwd)}${prompt?.slice(0, 40) || `Session ${id}`}`,
|
|
13101
|
+
commandType: "claude",
|
|
12734
13102
|
status: "running",
|
|
12735
13103
|
startedAt: Date.now(),
|
|
12736
13104
|
pty: spawnClaude2({ prompt, cwd }),
|
|
@@ -12739,10 +13107,27 @@ function createSession(id, prompt, cwd) {
|
|
|
12739
13107
|
lastResizeAt: 0
|
|
12740
13108
|
};
|
|
12741
13109
|
}
|
|
13110
|
+
function createRunSession(id, runName, runArgs, cwd) {
|
|
13111
|
+
return {
|
|
13112
|
+
id,
|
|
13113
|
+
name: `${repoPrefix(cwd)}run: ${runName}`,
|
|
13114
|
+
commandType: "run",
|
|
13115
|
+
status: "running",
|
|
13116
|
+
startedAt: Date.now(),
|
|
13117
|
+
pty: spawnRun({ name: runName, args: runArgs, cwd }),
|
|
13118
|
+
scrollback: "",
|
|
13119
|
+
idleTimer: null,
|
|
13120
|
+
lastResizeAt: 0,
|
|
13121
|
+
runName,
|
|
13122
|
+
runArgs,
|
|
13123
|
+
cwd
|
|
13124
|
+
};
|
|
13125
|
+
}
|
|
12742
13126
|
function resumeSession(id, sessionId, cwd, name) {
|
|
12743
13127
|
return {
|
|
12744
13128
|
id,
|
|
12745
|
-
name: name ? `${name.slice(0, 36)} (R)` : `Resume ${sessionId.slice(0, 8)}`,
|
|
13129
|
+
name: `${repoPrefix(cwd)}${name ? `${name.slice(0, 36)} (R)` : `Resume ${sessionId.slice(0, 8)}`}`,
|
|
13130
|
+
commandType: "claude",
|
|
12746
13131
|
status: "running",
|
|
12747
13132
|
startedAt: Date.now(),
|
|
12748
13133
|
pty: spawnClaude2({ resumeSessionId: sessionId, cwd }),
|
|
@@ -12752,112 +13137,23 @@ function resumeSession(id, sessionId, cwd, name) {
|
|
|
12752
13137
|
};
|
|
12753
13138
|
}
|
|
12754
13139
|
|
|
12755
|
-
// src/commands/sessions/web/
|
|
12756
|
-
|
|
12757
|
-
|
|
12758
|
-
import * as path47 from "path";
|
|
12759
|
-
|
|
12760
|
-
// src/commands/sessions/web/parseSessionFile.ts
|
|
12761
|
-
import * as fs23 from "fs";
|
|
12762
|
-
import * as path46 from "path";
|
|
12763
|
-
|
|
12764
|
-
// src/commands/sessions/web/extractSessionMeta.ts
|
|
12765
|
-
function extractSessionMeta(lines) {
|
|
12766
|
-
let sessionId = "";
|
|
12767
|
-
let cwd = "";
|
|
12768
|
-
let timestamp = "";
|
|
12769
|
-
let name = "";
|
|
12770
|
-
for (const line of lines) {
|
|
12771
|
-
const entry = safeParse(line);
|
|
12772
|
-
if (!entry) continue;
|
|
12773
|
-
sessionId ||= typeof entry.sessionId === "string" ? entry.sessionId : "";
|
|
12774
|
-
timestamp ||= typeof entry.timestamp === "string" ? entry.timestamp : "";
|
|
12775
|
-
cwd ||= typeof entry.cwd === "string" ? entry.cwd : "";
|
|
12776
|
-
if (entry.type === "user" && !entry.isMeta) {
|
|
12777
|
-
name = extractName(entry);
|
|
12778
|
-
break;
|
|
12779
|
-
}
|
|
12780
|
-
}
|
|
12781
|
-
return { sessionId, cwd, timestamp, name };
|
|
12782
|
-
}
|
|
12783
|
-
function safeParse(line) {
|
|
12784
|
-
try {
|
|
12785
|
-
return JSON.parse(line);
|
|
12786
|
-
} catch {
|
|
12787
|
-
return null;
|
|
12788
|
-
}
|
|
12789
|
-
}
|
|
12790
|
-
function extractName(entry) {
|
|
12791
|
-
const msg = entry.message;
|
|
12792
|
-
const content = msg?.content;
|
|
12793
|
-
const text = typeof content === "string" ? content : Array.isArray(content) ? content.find((c) => c.type === "text")?.text ?? "" : "";
|
|
12794
|
-
return text.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
|
|
13140
|
+
// src/commands/sessions/web/wsBroadcast.ts
|
|
13141
|
+
function wsSend(ws, msg) {
|
|
13142
|
+
if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(msg));
|
|
12795
13143
|
}
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
try {
|
|
12801
|
-
handle = await fs23.promises.open(filePath, "r");
|
|
12802
|
-
const buf = Buffer.alloc(16384);
|
|
12803
|
-
const { bytesRead } = await handle.read(buf, 0, buf.length, 0);
|
|
12804
|
-
const lines = buf.toString("utf8", 0, bytesRead).split("\n").filter(Boolean);
|
|
12805
|
-
const meta = extractSessionMeta(lines);
|
|
12806
|
-
if (!meta.sessionId) return null;
|
|
12807
|
-
const timestamp = meta.timestamp || (await fs23.promises.stat(filePath)).mtime.toISOString();
|
|
12808
|
-
const project = meta.cwd ? path46.basename(meta.cwd) : dirNameToProject(filePath);
|
|
12809
|
-
return {
|
|
12810
|
-
sessionId: meta.sessionId,
|
|
12811
|
-
name: meta.name || `Session ${meta.sessionId.slice(0, 8)}`,
|
|
12812
|
-
project,
|
|
12813
|
-
cwd: meta.cwd,
|
|
12814
|
-
timestamp
|
|
12815
|
-
};
|
|
12816
|
-
} catch {
|
|
12817
|
-
return null;
|
|
12818
|
-
} finally {
|
|
12819
|
-
await handle?.close();
|
|
13144
|
+
function wsBroadcast(clients, msg) {
|
|
13145
|
+
const json = JSON.stringify(msg);
|
|
13146
|
+
for (const ws of clients) {
|
|
13147
|
+
if (ws.readyState === ws.OPEN) ws.send(json);
|
|
12820
13148
|
}
|
|
12821
13149
|
}
|
|
12822
|
-
function dirNameToProject(filePath) {
|
|
12823
|
-
const dirName = path46.basename(path46.dirname(filePath));
|
|
12824
|
-
const parts = dirName.split("--");
|
|
12825
|
-
return parts[parts.length - 1].replace(/-/g, "/");
|
|
12826
|
-
}
|
|
12827
13150
|
|
|
12828
|
-
// src/commands/sessions/web/
|
|
12829
|
-
|
|
12830
|
-
const
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
projectDirs = await fs24.promises.readdir(projectsDir);
|
|
12834
|
-
} catch {
|
|
12835
|
-
return [];
|
|
13151
|
+
// src/commands/sessions/web/replayScrollback.ts
|
|
13152
|
+
function replayScrollback(sessions, ws) {
|
|
13153
|
+
for (const s of sessions.values()) {
|
|
13154
|
+
if (s.scrollback)
|
|
13155
|
+
wsSend(ws, { type: "output", sessionId: s.id, data: s.scrollback });
|
|
12836
13156
|
}
|
|
12837
|
-
const sessions = [];
|
|
12838
|
-
await Promise.all(
|
|
12839
|
-
projectDirs.map(async (dirName) => {
|
|
12840
|
-
const dirPath = path47.join(projectsDir, dirName);
|
|
12841
|
-
let entries;
|
|
12842
|
-
try {
|
|
12843
|
-
entries = await fs24.promises.readdir(dirPath);
|
|
12844
|
-
} catch {
|
|
12845
|
-
return;
|
|
12846
|
-
}
|
|
12847
|
-
const jsonlFiles = entries.filter((e) => e.endsWith(".jsonl"));
|
|
12848
|
-
await Promise.all(
|
|
12849
|
-
jsonlFiles.map(async (file) => {
|
|
12850
|
-
const filePath = path47.join(dirPath, file);
|
|
12851
|
-
const session = await parseSessionFile(filePath);
|
|
12852
|
-
if (session) sessions.push(session);
|
|
12853
|
-
})
|
|
12854
|
-
);
|
|
12855
|
-
})
|
|
12856
|
-
);
|
|
12857
|
-
sessions.sort(
|
|
12858
|
-
(a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
|
12859
|
-
);
|
|
12860
|
-
return sessions;
|
|
12861
13157
|
}
|
|
12862
13158
|
|
|
12863
13159
|
// src/commands/sessions/web/scheduleIdle.ts
|
|
@@ -12873,17 +13169,6 @@ function clearIdle(session) {
|
|
|
12873
13169
|
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
12874
13170
|
}
|
|
12875
13171
|
|
|
12876
|
-
// src/commands/sessions/web/wsBroadcast.ts
|
|
12877
|
-
function wsSend(ws, msg) {
|
|
12878
|
-
if (ws.readyState === ws.OPEN) ws.send(JSON.stringify(msg));
|
|
12879
|
-
}
|
|
12880
|
-
function wsBroadcast(clients, msg) {
|
|
12881
|
-
const json = JSON.stringify(msg);
|
|
12882
|
-
for (const ws of clients) {
|
|
12883
|
-
if (ws.readyState === ws.OPEN) ws.send(json);
|
|
12884
|
-
}
|
|
12885
|
-
}
|
|
12886
|
-
|
|
12887
13172
|
// src/commands/sessions/web/wirePtyEvents.ts
|
|
12888
13173
|
var MAX_SCROLLBACK = 256 * 1024;
|
|
12889
13174
|
var RESIZE_GRACE_MS = 500;
|
|
@@ -12909,6 +13194,39 @@ function wirePtyEvents(session, clients, onStatusChange) {
|
|
|
12909
13194
|
});
|
|
12910
13195
|
}
|
|
12911
13196
|
|
|
13197
|
+
// src/commands/sessions/web/retrySession.ts
|
|
13198
|
+
function retrySession(session, clients, onStatusChange) {
|
|
13199
|
+
if (session.commandType !== "run" || !session.runName) return false;
|
|
13200
|
+
if (session.status !== "done") session.pty.kill();
|
|
13201
|
+
clearIdle(session);
|
|
13202
|
+
session.scrollback = "";
|
|
13203
|
+
session.status = "running";
|
|
13204
|
+
session.startedAt = Date.now();
|
|
13205
|
+
session.pty = spawnRun({
|
|
13206
|
+
name: session.runName,
|
|
13207
|
+
args: session.runArgs,
|
|
13208
|
+
cwd: session.cwd
|
|
13209
|
+
});
|
|
13210
|
+
wsBroadcast(clients, { type: "clear", sessionId: session.id });
|
|
13211
|
+
wirePtyEvents(session, clients, onStatusChange);
|
|
13212
|
+
scheduleIdle(session, () => onStatusChange(session, "waiting"));
|
|
13213
|
+
return true;
|
|
13214
|
+
}
|
|
13215
|
+
|
|
13216
|
+
// src/commands/sessions/web/toSessionInfo.ts
|
|
13217
|
+
function toSessionInfo({
|
|
13218
|
+
id,
|
|
13219
|
+
name,
|
|
13220
|
+
commandType,
|
|
13221
|
+
status: status2,
|
|
13222
|
+
startedAt,
|
|
13223
|
+
runName,
|
|
13224
|
+
runArgs,
|
|
13225
|
+
cwd
|
|
13226
|
+
}) {
|
|
13227
|
+
return { id, name, commandType, status: status2, startedAt, runName, runArgs, cwd };
|
|
13228
|
+
}
|
|
13229
|
+
|
|
12912
13230
|
// src/commands/sessions/web/writeToSession.ts
|
|
12913
13231
|
function writeToSession(sessions, id, data) {
|
|
12914
13232
|
const s = sessions.get(id);
|
|
@@ -12947,39 +13265,34 @@ var SessionManager = class {
|
|
|
12947
13265
|
cwd: this.repoCwd,
|
|
12948
13266
|
sessions: this.listSessions()
|
|
12949
13267
|
});
|
|
12950
|
-
this.
|
|
12951
|
-
}
|
|
12952
|
-
replayScrollback(ws) {
|
|
12953
|
-
for (const s of this.sessions.values()) {
|
|
12954
|
-
if (s.scrollback)
|
|
12955
|
-
wsSend(ws, { type: "output", sessionId: s.id, data: s.scrollback });
|
|
12956
|
-
}
|
|
13268
|
+
replayScrollback(this.sessions, ws);
|
|
12957
13269
|
}
|
|
12958
13270
|
removeClient(ws) {
|
|
12959
13271
|
this.clients.delete(ws);
|
|
12960
13272
|
}
|
|
12961
|
-
|
|
12962
|
-
const id = String(this.nextId++);
|
|
12963
|
-
const session = createSession(id, prompt, cwd);
|
|
13273
|
+
add(session) {
|
|
12964
13274
|
this.wire(session);
|
|
12965
|
-
return id;
|
|
13275
|
+
return session.id;
|
|
13276
|
+
}
|
|
13277
|
+
spawn(prompt, cwd) {
|
|
13278
|
+
return this.add(createSession(String(this.nextId++), prompt, cwd));
|
|
13279
|
+
}
|
|
13280
|
+
spawnRun(runName, runArgs, cwd) {
|
|
13281
|
+
return this.add(
|
|
13282
|
+
createRunSession(String(this.nextId++), runName, runArgs, cwd)
|
|
13283
|
+
);
|
|
12966
13284
|
}
|
|
12967
13285
|
resume(sessionId, cwd, name) {
|
|
12968
|
-
|
|
12969
|
-
const session = resumeSession(id, sessionId, cwd, name);
|
|
12970
|
-
this.wire(session);
|
|
12971
|
-
return id;
|
|
13286
|
+
return this.add(resumeSession(String(this.nextId++), sessionId, cwd, name));
|
|
12972
13287
|
}
|
|
13288
|
+
onStatusChange = (s, status2) => {
|
|
13289
|
+
s.status = status2;
|
|
13290
|
+
this.notify();
|
|
13291
|
+
};
|
|
12973
13292
|
wire(session) {
|
|
12974
13293
|
this.sessions.set(session.id, session);
|
|
12975
|
-
wirePtyEvents(session, this.clients,
|
|
12976
|
-
|
|
12977
|
-
this.notify();
|
|
12978
|
-
});
|
|
12979
|
-
scheduleIdle(session, () => {
|
|
12980
|
-
session.status = "waiting";
|
|
12981
|
-
this.notify();
|
|
12982
|
-
});
|
|
13294
|
+
wirePtyEvents(session, this.clients, this.onStatusChange);
|
|
13295
|
+
scheduleIdle(session, () => this.onStatusChange(session, "waiting"));
|
|
12983
13296
|
this.notify();
|
|
12984
13297
|
}
|
|
12985
13298
|
writeToSession(id, data) {
|
|
@@ -12988,18 +13301,15 @@ var SessionManager = class {
|
|
|
12988
13301
|
resizeSession(id, cols, rows) {
|
|
12989
13302
|
resizeSession(this.sessions, id, cols, rows);
|
|
12990
13303
|
}
|
|
13304
|
+
retrySession(id) {
|
|
13305
|
+
const s = this.sessions.get(id);
|
|
13306
|
+
if (s && retrySession(s, this.clients, this.onStatusChange)) this.notify();
|
|
13307
|
+
}
|
|
12991
13308
|
dismissSession(id) {
|
|
12992
13309
|
if (dismissSession(this.sessions, id)) this.notify();
|
|
12993
13310
|
}
|
|
12994
13311
|
listSessions() {
|
|
12995
|
-
return [...this.sessions.values()].map(
|
|
12996
|
-
({ id, name, status: status2, startedAt }) => ({
|
|
12997
|
-
id,
|
|
12998
|
-
name,
|
|
12999
|
-
status: status2,
|
|
13000
|
-
startedAt
|
|
13001
|
-
})
|
|
13002
|
-
);
|
|
13312
|
+
return [...this.sessions.values()].map(toSessionInfo);
|
|
13003
13313
|
}
|
|
13004
13314
|
async getHistory() {
|
|
13005
13315
|
return discoverSessions();
|
|
@@ -13033,13 +13343,14 @@ async function web3(options2) {
|
|
|
13033
13343
|
function registerSessions(program2) {
|
|
13034
13344
|
const cmd = program2.command("sessions").description("Web dashboard for Claude Code sessions").action(() => web3({ port: "3100" }));
|
|
13035
13345
|
cmd.command("web").description("Start the sessions web dashboard").option("-p, --port <number>", "Port to listen on", "3100").action(web3);
|
|
13346
|
+
cmd.command("summarise").description("Generate one-line summaries for Claude sessions").option("-f, --force", "Re-generate all summaries, even existing ones").option("-n, --limit <count>", "Maximum number of sessions to summarise").action(summarise3);
|
|
13036
13347
|
}
|
|
13037
13348
|
|
|
13038
13349
|
// src/commands/statusLine.ts
|
|
13039
|
-
import
|
|
13350
|
+
import chalk136 from "chalk";
|
|
13040
13351
|
|
|
13041
13352
|
// src/commands/buildLimitsSegment.ts
|
|
13042
|
-
import
|
|
13353
|
+
import chalk135 from "chalk";
|
|
13043
13354
|
var FIVE_HOUR_SECONDS = 5 * 3600;
|
|
13044
13355
|
var SEVEN_DAY_SECONDS = 7 * 86400;
|
|
13045
13356
|
function formatTimeLeft(resetsAt) {
|
|
@@ -13062,10 +13373,10 @@ function projectUsage(pct, resetsAt, windowSeconds) {
|
|
|
13062
13373
|
function colorizeRateLimit(pct, resetsAt, windowSeconds) {
|
|
13063
13374
|
const label2 = `${Math.round(pct)}%`;
|
|
13064
13375
|
const projected = projectUsage(pct, resetsAt, windowSeconds);
|
|
13065
|
-
if (projected == null) return
|
|
13066
|
-
if (projected > 100) return
|
|
13067
|
-
if (projected > 75) return
|
|
13068
|
-
return
|
|
13376
|
+
if (projected == null) return chalk135.green(label2);
|
|
13377
|
+
if (projected > 100) return chalk135.red(label2);
|
|
13378
|
+
if (projected > 75) return chalk135.yellow(label2);
|
|
13379
|
+
return chalk135.green(label2);
|
|
13069
13380
|
}
|
|
13070
13381
|
function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel) {
|
|
13071
13382
|
const timeLabel = resetsAt ? formatTimeLeft(resetsAt) : fallbackLabel;
|
|
@@ -13091,14 +13402,14 @@ function buildLimitsSegment(rateLimits) {
|
|
|
13091
13402
|
}
|
|
13092
13403
|
|
|
13093
13404
|
// src/commands/statusLine.ts
|
|
13094
|
-
|
|
13405
|
+
chalk136.level = 3;
|
|
13095
13406
|
function formatNumber(num) {
|
|
13096
13407
|
return num.toLocaleString("en-US");
|
|
13097
13408
|
}
|
|
13098
13409
|
function colorizePercent(pct) {
|
|
13099
13410
|
const label2 = `${Math.round(pct)}%`;
|
|
13100
|
-
if (pct > 80) return
|
|
13101
|
-
if (pct > 40) return
|
|
13411
|
+
if (pct > 80) return chalk136.red(label2);
|
|
13412
|
+
if (pct > 40) return chalk136.yellow(label2);
|
|
13102
13413
|
return label2;
|
|
13103
13414
|
}
|
|
13104
13415
|
async function statusLine() {
|
|
@@ -13113,29 +13424,29 @@ async function statusLine() {
|
|
|
13113
13424
|
}
|
|
13114
13425
|
|
|
13115
13426
|
// src/commands/sync.ts
|
|
13116
|
-
import * as
|
|
13427
|
+
import * as fs30 from "fs";
|
|
13117
13428
|
import * as os2 from "os";
|
|
13118
|
-
import * as
|
|
13429
|
+
import * as path51 from "path";
|
|
13119
13430
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
13120
13431
|
|
|
13121
13432
|
// src/commands/sync/syncClaudeMd.ts
|
|
13122
|
-
import * as
|
|
13123
|
-
import * as
|
|
13124
|
-
import
|
|
13433
|
+
import * as fs28 from "fs";
|
|
13434
|
+
import * as path49 from "path";
|
|
13435
|
+
import chalk137 from "chalk";
|
|
13125
13436
|
async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
13126
|
-
const source =
|
|
13127
|
-
const target =
|
|
13128
|
-
const sourceContent =
|
|
13129
|
-
if (
|
|
13130
|
-
const targetContent =
|
|
13437
|
+
const source = path49.join(claudeDir, "CLAUDE.md");
|
|
13438
|
+
const target = path49.join(targetBase, "CLAUDE.md");
|
|
13439
|
+
const sourceContent = fs28.readFileSync(source, "utf-8");
|
|
13440
|
+
if (fs28.existsSync(target)) {
|
|
13441
|
+
const targetContent = fs28.readFileSync(target, "utf-8");
|
|
13131
13442
|
if (sourceContent !== targetContent) {
|
|
13132
13443
|
console.log(
|
|
13133
|
-
|
|
13444
|
+
chalk137.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
13134
13445
|
);
|
|
13135
13446
|
console.log();
|
|
13136
13447
|
printDiff(targetContent, sourceContent);
|
|
13137
13448
|
const confirm = options2?.yes || await promptConfirm(
|
|
13138
|
-
|
|
13449
|
+
chalk137.red("Overwrite existing CLAUDE.md?"),
|
|
13139
13450
|
false
|
|
13140
13451
|
);
|
|
13141
13452
|
if (!confirm) {
|
|
@@ -13144,21 +13455,21 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
|
13144
13455
|
}
|
|
13145
13456
|
}
|
|
13146
13457
|
}
|
|
13147
|
-
|
|
13458
|
+
fs28.copyFileSync(source, target);
|
|
13148
13459
|
console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
|
|
13149
13460
|
}
|
|
13150
13461
|
|
|
13151
13462
|
// src/commands/sync/syncSettings.ts
|
|
13152
|
-
import * as
|
|
13153
|
-
import * as
|
|
13154
|
-
import
|
|
13463
|
+
import * as fs29 from "fs";
|
|
13464
|
+
import * as path50 from "path";
|
|
13465
|
+
import chalk138 from "chalk";
|
|
13155
13466
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
13156
|
-
const source =
|
|
13157
|
-
const target =
|
|
13158
|
-
const sourceContent =
|
|
13467
|
+
const source = path50.join(claudeDir, "settings.json");
|
|
13468
|
+
const target = path50.join(targetBase, "settings.json");
|
|
13469
|
+
const sourceContent = fs29.readFileSync(source, "utf-8");
|
|
13159
13470
|
const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
|
|
13160
|
-
if (
|
|
13161
|
-
const targetContent =
|
|
13471
|
+
if (fs29.existsSync(target)) {
|
|
13472
|
+
const targetContent = fs29.readFileSync(target, "utf-8");
|
|
13162
13473
|
const normalizedTarget = JSON.stringify(
|
|
13163
13474
|
JSON.parse(targetContent),
|
|
13164
13475
|
null,
|
|
@@ -13167,14 +13478,14 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
13167
13478
|
if (mergedContent !== normalizedTarget) {
|
|
13168
13479
|
if (!options2?.yes) {
|
|
13169
13480
|
console.log(
|
|
13170
|
-
|
|
13481
|
+
chalk138.yellow(
|
|
13171
13482
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
13172
13483
|
)
|
|
13173
13484
|
);
|
|
13174
13485
|
console.log();
|
|
13175
13486
|
printDiff(targetContent, mergedContent);
|
|
13176
13487
|
const confirm = await promptConfirm(
|
|
13177
|
-
|
|
13488
|
+
chalk138.red("Overwrite existing settings.json?"),
|
|
13178
13489
|
false
|
|
13179
13490
|
);
|
|
13180
13491
|
if (!confirm) {
|
|
@@ -13184,29 +13495,29 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
13184
13495
|
}
|
|
13185
13496
|
}
|
|
13186
13497
|
}
|
|
13187
|
-
|
|
13498
|
+
fs29.writeFileSync(target, mergedContent);
|
|
13188
13499
|
console.log("Copied settings.json to ~/.claude/settings.json");
|
|
13189
13500
|
}
|
|
13190
13501
|
|
|
13191
13502
|
// src/commands/sync.ts
|
|
13192
13503
|
var __filename4 = fileURLToPath7(import.meta.url);
|
|
13193
|
-
var __dirname7 =
|
|
13504
|
+
var __dirname7 = path51.dirname(__filename4);
|
|
13194
13505
|
async function sync(options2) {
|
|
13195
13506
|
const config = loadConfig();
|
|
13196
13507
|
const yes = options2?.yes ?? config.sync.autoConfirm;
|
|
13197
|
-
const claudeDir =
|
|
13198
|
-
const targetBase =
|
|
13508
|
+
const claudeDir = path51.join(__dirname7, "..", "claude");
|
|
13509
|
+
const targetBase = path51.join(os2.homedir(), ".claude");
|
|
13199
13510
|
syncCommands(claudeDir, targetBase);
|
|
13200
13511
|
await syncSettings(claudeDir, targetBase, { yes });
|
|
13201
13512
|
await syncClaudeMd(claudeDir, targetBase, { yes });
|
|
13202
13513
|
}
|
|
13203
13514
|
function syncCommands(claudeDir, targetBase) {
|
|
13204
|
-
const sourceDir =
|
|
13205
|
-
const targetDir =
|
|
13206
|
-
|
|
13207
|
-
const files =
|
|
13515
|
+
const sourceDir = path51.join(claudeDir, "commands");
|
|
13516
|
+
const targetDir = path51.join(targetBase, "commands");
|
|
13517
|
+
fs30.mkdirSync(targetDir, { recursive: true });
|
|
13518
|
+
const files = fs30.readdirSync(sourceDir);
|
|
13208
13519
|
for (const file of files) {
|
|
13209
|
-
|
|
13520
|
+
fs30.copyFileSync(path51.join(sourceDir, file), path51.join(targetDir, file));
|
|
13210
13521
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
13211
13522
|
}
|
|
13212
13523
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
@@ -13214,15 +13525,15 @@ function syncCommands(claudeDir, targetBase) {
|
|
|
13214
13525
|
|
|
13215
13526
|
// src/commands/update.ts
|
|
13216
13527
|
import { execSync as execSync42 } from "child_process";
|
|
13217
|
-
import * as
|
|
13528
|
+
import * as path52 from "path";
|
|
13218
13529
|
function isGlobalNpmInstall(dir) {
|
|
13219
13530
|
try {
|
|
13220
|
-
const resolved =
|
|
13221
|
-
if (resolved.split(
|
|
13531
|
+
const resolved = path52.resolve(dir);
|
|
13532
|
+
if (resolved.split(path52.sep).includes("node_modules")) {
|
|
13222
13533
|
return true;
|
|
13223
13534
|
}
|
|
13224
13535
|
const globalPrefix = execSync42("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
13225
|
-
return resolved.toLowerCase().startsWith(
|
|
13536
|
+
return resolved.toLowerCase().startsWith(path52.resolve(globalPrefix).toLowerCase());
|
|
13226
13537
|
} catch {
|
|
13227
13538
|
return false;
|
|
13228
13539
|
}
|