@staff0rd/assist 0.192.0 → 0.193.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/sessions/web/bundle.js +24 -24
- package/dist/index.js +240 -87
- 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.193.1",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -792,6 +792,7 @@ function buildAuthoredPhasePrompt(item, phaseNumber, phase) {
|
|
|
792
792
|
...buildContextLines(item, phaseNumber, phase),
|
|
793
793
|
"",
|
|
794
794
|
"Focus ONLY on this phase. Do not work on other phases.",
|
|
795
|
+
"If you need to modify backlog items, run `assist backlog --help` to discover available commands.",
|
|
795
796
|
"When you have completed all tasks for this phase, run /verify to check your work.",
|
|
796
797
|
...buildManualCheckLines(manualChecks),
|
|
797
798
|
"",
|
|
@@ -842,12 +843,14 @@ function buildReviewPrompt(item, phaseNumber) {
|
|
|
842
843
|
item.description ? `Description: ${item.description}` : "",
|
|
843
844
|
"",
|
|
844
845
|
"This is the auto-generated review phase. Verify each acceptance criterion against the current implementation.",
|
|
846
|
+
"If you need to modify backlog items, run `assist backlog --help` to discover available commands.",
|
|
845
847
|
"For each criterion, inspect the code and report PASS or FAIL with a brief explanation:",
|
|
846
848
|
"",
|
|
847
849
|
acLines,
|
|
848
850
|
...buildCommentLines(item.comments),
|
|
849
851
|
"",
|
|
850
|
-
"If any criterion fails, fix the issue and re-verify
|
|
852
|
+
"If any criterion fails, fix the issue and re-verify.",
|
|
853
|
+
`If a criterion still fails after fixing, use \`assist backlog rewind ${item.id} <phase> --reason "<reason>"\` to return to the appropriate phase instead of using phase-done.`,
|
|
851
854
|
"",
|
|
852
855
|
`Post concise comments for any notable findings or changes using \`assist backlog comment ${item.id} "<text>"\`.`,
|
|
853
856
|
"",
|
|
@@ -925,10 +928,10 @@ function writeSignal(event, data) {
|
|
|
925
928
|
|
|
926
929
|
// src/commands/backlog/readSignal.ts
|
|
927
930
|
function readSignal() {
|
|
928
|
-
const
|
|
929
|
-
if (!existsSync5(
|
|
931
|
+
const path52 = getSignalPath();
|
|
932
|
+
if (!existsSync5(path52)) return void 0;
|
|
930
933
|
try {
|
|
931
|
-
return JSON.parse(readFileSync5(
|
|
934
|
+
return JSON.parse(readFileSync5(path52, "utf-8"));
|
|
932
935
|
} catch {
|
|
933
936
|
return void 0;
|
|
934
937
|
}
|
|
@@ -981,10 +984,10 @@ import { stringify as stringifyYaml } from "yaml";
|
|
|
981
984
|
// src/shared/loadRawYaml.ts
|
|
982
985
|
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
983
986
|
import { parse as parseYaml2 } from "yaml";
|
|
984
|
-
function loadRawYaml(
|
|
985
|
-
if (!existsSync7(
|
|
987
|
+
function loadRawYaml(path52) {
|
|
988
|
+
if (!existsSync7(path52)) return {};
|
|
986
989
|
try {
|
|
987
|
-
const content = readFileSync6(
|
|
990
|
+
const content = readFileSync6(path52, "utf-8");
|
|
988
991
|
return parseYaml2(content) || {};
|
|
989
992
|
} catch {
|
|
990
993
|
return {};
|
|
@@ -4955,9 +4958,9 @@ var __dirname4 = dirname15(__filename2);
|
|
|
4955
4958
|
function packageRoot() {
|
|
4956
4959
|
return __dirname4;
|
|
4957
4960
|
}
|
|
4958
|
-
function readLines(
|
|
4959
|
-
if (!existsSync20(
|
|
4960
|
-
return readFileSync16(
|
|
4961
|
+
function readLines(path52) {
|
|
4962
|
+
if (!existsSync20(path52)) return [];
|
|
4963
|
+
return readFileSync16(path52, "utf-8").split("\n").filter((line) => line.trim() !== "");
|
|
4961
4964
|
}
|
|
4962
4965
|
var cachedReads;
|
|
4963
4966
|
var cachedWrites;
|
|
@@ -5390,14 +5393,14 @@ function showProgress(p, label2) {
|
|
|
5390
5393
|
const pct = Math.round(p.done / p.total * 100);
|
|
5391
5394
|
process.stderr.write(`\r\x1B[K[${pct}%] Scanning ${label2}...`);
|
|
5392
5395
|
}
|
|
5393
|
-
async function resolveCommand(cli,
|
|
5394
|
-
showProgress(p,
|
|
5395
|
-
const subHelp = await runHelp([cli, ...
|
|
5396
|
+
async function resolveCommand(cli, path52, description, depth, p) {
|
|
5397
|
+
showProgress(p, path52.join(" "));
|
|
5398
|
+
const subHelp = await runHelp([cli, ...path52]);
|
|
5396
5399
|
if (!subHelp || !hasSubcommands(subHelp)) {
|
|
5397
|
-
return [{ path:
|
|
5400
|
+
return [{ path: path52, description }];
|
|
5398
5401
|
}
|
|
5399
|
-
const children = await discoverAt(cli,
|
|
5400
|
-
return children.length > 0 ? children : [{ path:
|
|
5402
|
+
const children = await discoverAt(cli, path52, depth + 1, p);
|
|
5403
|
+
return children.length > 0 ? children : [{ path: path52, description }];
|
|
5401
5404
|
}
|
|
5402
5405
|
async function discoverAt(cli, parentPath, depth, p) {
|
|
5403
5406
|
if (depth > SAFETY_DEPTH) return [];
|
|
@@ -5545,9 +5548,9 @@ function logPath(cli) {
|
|
|
5545
5548
|
return join19(homedir4(), ".assist", `cli-discover-${safeName}.log`);
|
|
5546
5549
|
}
|
|
5547
5550
|
function readCache(cli) {
|
|
5548
|
-
const
|
|
5549
|
-
if (!existsSync22(
|
|
5550
|
-
return readFileSync18(
|
|
5551
|
+
const path52 = logPath(cli);
|
|
5552
|
+
if (!existsSync22(path52)) return void 0;
|
|
5553
|
+
return readFileSync18(path52, "utf-8");
|
|
5551
5554
|
}
|
|
5552
5555
|
function writeCache(cli, output) {
|
|
5553
5556
|
const dir = join19(homedir4(), ".assist");
|
|
@@ -6189,8 +6192,8 @@ function stepIntoNested(container, key, nextKey) {
|
|
|
6189
6192
|
}
|
|
6190
6193
|
return ensureObject(container, resolved);
|
|
6191
6194
|
}
|
|
6192
|
-
function setNestedValue(obj,
|
|
6193
|
-
const keys =
|
|
6195
|
+
function setNestedValue(obj, path52, value) {
|
|
6196
|
+
const keys = path52.split(".");
|
|
6194
6197
|
const result = { ...obj };
|
|
6195
6198
|
let current = result;
|
|
6196
6199
|
for (let i = 0; i < keys.length - 1; i++) {
|
|
@@ -6270,9 +6273,9 @@ function isTraversable(value) {
|
|
|
6270
6273
|
function stepInto(current, key) {
|
|
6271
6274
|
return isTraversable(current) ? current[key] : void 0;
|
|
6272
6275
|
}
|
|
6273
|
-
function getNestedValue(obj,
|
|
6276
|
+
function getNestedValue(obj, path52) {
|
|
6274
6277
|
let current = obj;
|
|
6275
|
-
for (const key of
|
|
6278
|
+
for (const key of path52.split(".")) current = stepInto(current, key);
|
|
6276
6279
|
return current;
|
|
6277
6280
|
}
|
|
6278
6281
|
|
|
@@ -7613,10 +7616,10 @@ function getStorePath(filename) {
|
|
|
7613
7616
|
return join26(getStoreDir(), filename);
|
|
7614
7617
|
}
|
|
7615
7618
|
function loadJson(filename) {
|
|
7616
|
-
const
|
|
7617
|
-
if (existsSync29(
|
|
7619
|
+
const path52 = getStorePath(filename);
|
|
7620
|
+
if (existsSync29(path52)) {
|
|
7618
7621
|
try {
|
|
7619
|
-
return JSON.parse(readFileSync25(
|
|
7622
|
+
return JSON.parse(readFileSync25(path52, "utf-8"));
|
|
7620
7623
|
} catch {
|
|
7621
7624
|
return {};
|
|
7622
7625
|
}
|
|
@@ -8009,7 +8012,7 @@ function validateLine(line) {
|
|
|
8009
8012
|
process.exit(1);
|
|
8010
8013
|
}
|
|
8011
8014
|
}
|
|
8012
|
-
function comment2(
|
|
8015
|
+
function comment2(path52, line, body) {
|
|
8013
8016
|
validateBody(body);
|
|
8014
8017
|
validateLine(line);
|
|
8015
8018
|
try {
|
|
@@ -8029,7 +8032,7 @@ function comment2(path50, line, body) {
|
|
|
8029
8032
|
"-f",
|
|
8030
8033
|
`body=${body}`,
|
|
8031
8034
|
"-f",
|
|
8032
|
-
`path=${
|
|
8035
|
+
`path=${path52}`,
|
|
8033
8036
|
"-F",
|
|
8034
8037
|
`line=${line}`
|
|
8035
8038
|
],
|
|
@@ -8038,7 +8041,7 @@ function comment2(path50, line, body) {
|
|
|
8038
8041
|
if (result.status !== 0) {
|
|
8039
8042
|
throw new Error(result.stderr || result.stdout);
|
|
8040
8043
|
}
|
|
8041
|
-
console.log(`Added review comment on ${
|
|
8044
|
+
console.log(`Added review comment on ${path52}:${line}`);
|
|
8042
8045
|
} finally {
|
|
8043
8046
|
unlinkSync6(queryFile);
|
|
8044
8047
|
}
|
|
@@ -8537,8 +8540,8 @@ function registerPrs(program2) {
|
|
|
8537
8540
|
prsCommand.command("wontfix <comment-id> <reason>").description("Reply with reason and resolve thread").action((commentId, reason) => {
|
|
8538
8541
|
wontfix(Number.parseInt(commentId, 10), reason);
|
|
8539
8542
|
});
|
|
8540
|
-
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((
|
|
8541
|
-
comment2(
|
|
8543
|
+
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path52, line, body) => {
|
|
8544
|
+
comment2(path52, Number.parseInt(line, 10), body);
|
|
8542
8545
|
});
|
|
8543
8546
|
}
|
|
8544
8547
|
|
|
@@ -8790,10 +8793,10 @@ function resolveOpSecret(reference) {
|
|
|
8790
8793
|
}
|
|
8791
8794
|
|
|
8792
8795
|
// src/commands/ravendb/ravenFetch.ts
|
|
8793
|
-
async function ravenFetch(connection,
|
|
8796
|
+
async function ravenFetch(connection, path52) {
|
|
8794
8797
|
const apiKey = resolveOpSecret(connection.apiKeyRef);
|
|
8795
8798
|
let accessToken = await getAccessToken(apiKey);
|
|
8796
|
-
const url = `${connection.url}${
|
|
8799
|
+
const url = `${connection.url}${path52}`;
|
|
8797
8800
|
const headers = {
|
|
8798
8801
|
Authorization: `Bearer ${accessToken}`,
|
|
8799
8802
|
"Content-Type": "application/json"
|
|
@@ -8883,16 +8886,16 @@ import chalk111 from "chalk";
|
|
|
8883
8886
|
// src/commands/ravendb/buildQueryPath.ts
|
|
8884
8887
|
function buildQueryPath(opts) {
|
|
8885
8888
|
const db = encodeURIComponent(opts.db);
|
|
8886
|
-
let
|
|
8889
|
+
let path52;
|
|
8887
8890
|
if (opts.collection) {
|
|
8888
|
-
|
|
8891
|
+
path52 = `/databases/${db}/indexes/dynamic/${encodeURIComponent(opts.collection)}?start=${opts.start}&pageSize=${opts.pageSize}&sort=${encodeURIComponent(opts.sort)}`;
|
|
8889
8892
|
} else {
|
|
8890
|
-
|
|
8893
|
+
path52 = `/databases/${db}/queries?start=${opts.start}&pageSize=${opts.pageSize}`;
|
|
8891
8894
|
}
|
|
8892
8895
|
if (opts.query) {
|
|
8893
|
-
|
|
8896
|
+
path52 += `&query=${encodeURIComponent(opts.query)}`;
|
|
8894
8897
|
}
|
|
8895
|
-
return
|
|
8898
|
+
return path52;
|
|
8896
8899
|
}
|
|
8897
8900
|
|
|
8898
8901
|
// src/commands/ravendb/fetchAllPages.ts
|
|
@@ -8901,7 +8904,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
8901
8904
|
let start3 = 0;
|
|
8902
8905
|
while (true) {
|
|
8903
8906
|
const effectivePageSize = opts.limit !== void 0 ? Math.min(opts.pageSize, opts.limit - allResults.length) : opts.pageSize;
|
|
8904
|
-
const
|
|
8907
|
+
const path52 = buildQueryPath({
|
|
8905
8908
|
db: connection.database,
|
|
8906
8909
|
collection: opts.collection,
|
|
8907
8910
|
start: start3,
|
|
@@ -8909,7 +8912,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
8909
8912
|
sort: opts.sort,
|
|
8910
8913
|
query: opts.query
|
|
8911
8914
|
});
|
|
8912
|
-
const data = await ravenFetch(connection,
|
|
8915
|
+
const data = await ravenFetch(connection, path52);
|
|
8913
8916
|
const results = data.Results ?? [];
|
|
8914
8917
|
const totalResults = data.TotalResults ?? 0;
|
|
8915
8918
|
if (results.length === 0) break;
|
|
@@ -10080,8 +10083,8 @@ function findRootParent(file, importedBy, visited) {
|
|
|
10080
10083
|
function clusterFiles(graph) {
|
|
10081
10084
|
const clusters = /* @__PURE__ */ new Map();
|
|
10082
10085
|
for (const file of graph.files) {
|
|
10083
|
-
const
|
|
10084
|
-
if (
|
|
10086
|
+
const basename8 = path39.basename(file, path39.extname(file));
|
|
10087
|
+
if (basename8 === "index") continue;
|
|
10085
10088
|
const importers = graph.importedBy.get(file);
|
|
10086
10089
|
if (!importers || importers.size !== 1) continue;
|
|
10087
10090
|
const parent = [...importers][0];
|
|
@@ -10531,8 +10534,8 @@ import chalk128 from "chalk";
|
|
|
10531
10534
|
|
|
10532
10535
|
// src/commands/seq/fetchSeq.ts
|
|
10533
10536
|
import chalk125 from "chalk";
|
|
10534
|
-
async function fetchSeq(conn,
|
|
10535
|
-
const url = `${conn.url}${
|
|
10537
|
+
async function fetchSeq(conn, path52, params) {
|
|
10538
|
+
const url = `${conn.url}${path52}?${params}`;
|
|
10536
10539
|
const response = await fetch(url, {
|
|
10537
10540
|
headers: {
|
|
10538
10541
|
Accept: "application/json",
|
|
@@ -12136,11 +12139,11 @@ function findLinkIndex() {
|
|
|
12136
12139
|
function parseLinkArgs() {
|
|
12137
12140
|
const idx = findLinkIndex();
|
|
12138
12141
|
if (idx === -1) return null;
|
|
12139
|
-
const
|
|
12142
|
+
const path52 = process.argv[idx + 1];
|
|
12140
12143
|
const rest = process.argv.slice(idx + 2);
|
|
12141
12144
|
const { value: prefix2 } = extractOption(rest, "--prefix");
|
|
12142
12145
|
if (!prefix2) return null;
|
|
12143
|
-
return { path:
|
|
12146
|
+
return { path: path52, prefix: prefix2 };
|
|
12144
12147
|
}
|
|
12145
12148
|
function hasDuplicateLink(runList, linkPath) {
|
|
12146
12149
|
return runList.some(
|
|
@@ -12468,9 +12471,23 @@ function handleSocket(ws, manager) {
|
|
|
12468
12471
|
data.rows
|
|
12469
12472
|
);
|
|
12470
12473
|
break;
|
|
12474
|
+
case "resume": {
|
|
12475
|
+
const id = manager.resume(
|
|
12476
|
+
data.sessionId,
|
|
12477
|
+
data.cwd,
|
|
12478
|
+
data.name
|
|
12479
|
+
);
|
|
12480
|
+
ws.send(JSON.stringify({ type: "created", sessionId: id }));
|
|
12481
|
+
break;
|
|
12482
|
+
}
|
|
12471
12483
|
case "dismiss":
|
|
12472
12484
|
manager.dismissSession(data.sessionId);
|
|
12473
12485
|
break;
|
|
12486
|
+
case "history":
|
|
12487
|
+
manager.getHistory().then((history) => {
|
|
12488
|
+
ws.send(JSON.stringify({ type: "history", sessions: history }));
|
|
12489
|
+
});
|
|
12490
|
+
break;
|
|
12474
12491
|
}
|
|
12475
12492
|
});
|
|
12476
12493
|
ws.on("close", () => {
|
|
@@ -12480,22 +12497,26 @@ function handleSocket(ws, manager) {
|
|
|
12480
12497
|
|
|
12481
12498
|
// src/commands/sessions/web/spawnClaude.ts
|
|
12482
12499
|
import * as pty from "node-pty";
|
|
12483
|
-
function spawnClaude2(
|
|
12500
|
+
function spawnClaude2(opts = {}) {
|
|
12484
12501
|
const shell = process.platform === "win32" ? "cmd.exe" : process.env.SHELL ?? "bash";
|
|
12485
|
-
const args = buildArgs(
|
|
12502
|
+
const args = buildArgs(opts);
|
|
12486
12503
|
return pty.spawn(shell, args, {
|
|
12487
12504
|
name: "xterm-256color",
|
|
12488
12505
|
cols: 120,
|
|
12489
12506
|
rows: 30,
|
|
12490
|
-
cwd: process.cwd(),
|
|
12507
|
+
cwd: opts.cwd ?? process.cwd(),
|
|
12491
12508
|
env: { ...process.env }
|
|
12492
12509
|
});
|
|
12493
12510
|
}
|
|
12494
|
-
function buildArgs(
|
|
12511
|
+
function buildArgs(opts) {
|
|
12512
|
+
const claudeArgs = opts.resumeSessionId ? ["claude", "--resume", opts.resumeSessionId] : opts.prompt ? ["claude", opts.prompt] : ["claude"];
|
|
12495
12513
|
if (process.platform === "win32") {
|
|
12496
|
-
return
|
|
12514
|
+
return ["/c", ...claudeArgs];
|
|
12497
12515
|
}
|
|
12498
|
-
return
|
|
12516
|
+
return ["-c", `exec ${claudeArgs.map(shellEscape).join(" ")}`];
|
|
12517
|
+
}
|
|
12518
|
+
function shellEscape(s) {
|
|
12519
|
+
return `'${s.replace(/'/g, "'\\''")}'`;
|
|
12499
12520
|
}
|
|
12500
12521
|
|
|
12501
12522
|
// src/commands/sessions/web/createSession.ts
|
|
@@ -12505,13 +12526,133 @@ function createSession(id, prompt) {
|
|
|
12505
12526
|
name: prompt?.slice(0, 40) || `Session ${id}`,
|
|
12506
12527
|
status: "running",
|
|
12507
12528
|
startedAt: Date.now(),
|
|
12508
|
-
pty: spawnClaude2(prompt),
|
|
12529
|
+
pty: spawnClaude2({ prompt }),
|
|
12530
|
+
scrollback: "",
|
|
12531
|
+
idleTimer: null,
|
|
12532
|
+
lastResizeAt: 0
|
|
12533
|
+
};
|
|
12534
|
+
}
|
|
12535
|
+
function resumeSession(id, sessionId, cwd, name) {
|
|
12536
|
+
return {
|
|
12537
|
+
id,
|
|
12538
|
+
name: name ? `${name.slice(0, 36)} (R)` : `Resume ${sessionId.slice(0, 8)}`,
|
|
12539
|
+
status: "running",
|
|
12540
|
+
startedAt: Date.now(),
|
|
12541
|
+
pty: spawnClaude2({ resumeSessionId: sessionId, cwd }),
|
|
12509
12542
|
scrollback: "",
|
|
12510
12543
|
idleTimer: null,
|
|
12511
12544
|
lastResizeAt: 0
|
|
12512
12545
|
};
|
|
12513
12546
|
}
|
|
12514
12547
|
|
|
12548
|
+
// src/commands/sessions/web/discoverSessions.ts
|
|
12549
|
+
import * as fs24 from "fs";
|
|
12550
|
+
import * as os from "os";
|
|
12551
|
+
import * as path47 from "path";
|
|
12552
|
+
|
|
12553
|
+
// src/commands/sessions/web/parseSessionFile.ts
|
|
12554
|
+
import * as fs23 from "fs";
|
|
12555
|
+
import * as path46 from "path";
|
|
12556
|
+
|
|
12557
|
+
// src/commands/sessions/web/extractSessionMeta.ts
|
|
12558
|
+
function extractSessionMeta(lines) {
|
|
12559
|
+
let sessionId = "";
|
|
12560
|
+
let cwd = "";
|
|
12561
|
+
let timestamp = "";
|
|
12562
|
+
let name = "";
|
|
12563
|
+
for (const line of lines) {
|
|
12564
|
+
const entry = safeParse(line);
|
|
12565
|
+
if (!entry) continue;
|
|
12566
|
+
sessionId ||= typeof entry.sessionId === "string" ? entry.sessionId : "";
|
|
12567
|
+
timestamp ||= typeof entry.timestamp === "string" ? entry.timestamp : "";
|
|
12568
|
+
cwd ||= typeof entry.cwd === "string" ? entry.cwd : "";
|
|
12569
|
+
if (entry.type === "user" && !entry.isMeta) {
|
|
12570
|
+
name = extractName(entry);
|
|
12571
|
+
break;
|
|
12572
|
+
}
|
|
12573
|
+
}
|
|
12574
|
+
return { sessionId, cwd, timestamp, name };
|
|
12575
|
+
}
|
|
12576
|
+
function safeParse(line) {
|
|
12577
|
+
try {
|
|
12578
|
+
return JSON.parse(line);
|
|
12579
|
+
} catch {
|
|
12580
|
+
return null;
|
|
12581
|
+
}
|
|
12582
|
+
}
|
|
12583
|
+
function extractName(entry) {
|
|
12584
|
+
const msg = entry.message;
|
|
12585
|
+
const content = msg?.content;
|
|
12586
|
+
const text = typeof content === "string" ? content : Array.isArray(content) ? content.find((c) => c.type === "text")?.text ?? "" : "";
|
|
12587
|
+
return text.replace(/<command-[^>]*>[^<]*<\/command-[^>]*>/g, "").trim().slice(0, 80);
|
|
12588
|
+
}
|
|
12589
|
+
|
|
12590
|
+
// src/commands/sessions/web/parseSessionFile.ts
|
|
12591
|
+
async function parseSessionFile(filePath) {
|
|
12592
|
+
let handle;
|
|
12593
|
+
try {
|
|
12594
|
+
handle = await fs23.promises.open(filePath, "r");
|
|
12595
|
+
const buf = Buffer.alloc(16384);
|
|
12596
|
+
const { bytesRead } = await handle.read(buf, 0, buf.length, 0);
|
|
12597
|
+
const lines = buf.toString("utf8", 0, bytesRead).split("\n").filter(Boolean);
|
|
12598
|
+
const meta = extractSessionMeta(lines);
|
|
12599
|
+
if (!meta.sessionId) return null;
|
|
12600
|
+
const timestamp = meta.timestamp || (await fs23.promises.stat(filePath)).mtime.toISOString();
|
|
12601
|
+
const project = meta.cwd ? path46.basename(meta.cwd) : dirNameToProject(filePath);
|
|
12602
|
+
return {
|
|
12603
|
+
sessionId: meta.sessionId,
|
|
12604
|
+
name: meta.name || `Session ${meta.sessionId.slice(0, 8)}`,
|
|
12605
|
+
project,
|
|
12606
|
+
cwd: meta.cwd,
|
|
12607
|
+
timestamp
|
|
12608
|
+
};
|
|
12609
|
+
} catch {
|
|
12610
|
+
return null;
|
|
12611
|
+
} finally {
|
|
12612
|
+
await handle?.close();
|
|
12613
|
+
}
|
|
12614
|
+
}
|
|
12615
|
+
function dirNameToProject(filePath) {
|
|
12616
|
+
const dirName = path46.basename(path46.dirname(filePath));
|
|
12617
|
+
const parts = dirName.split("--");
|
|
12618
|
+
return parts[parts.length - 1].replace(/-/g, "/");
|
|
12619
|
+
}
|
|
12620
|
+
|
|
12621
|
+
// src/commands/sessions/web/discoverSessions.ts
|
|
12622
|
+
async function discoverSessions() {
|
|
12623
|
+
const projectsDir = path47.join(os.homedir(), ".claude", "projects");
|
|
12624
|
+
let projectDirs;
|
|
12625
|
+
try {
|
|
12626
|
+
projectDirs = await fs24.promises.readdir(projectsDir);
|
|
12627
|
+
} catch {
|
|
12628
|
+
return [];
|
|
12629
|
+
}
|
|
12630
|
+
const sessions = [];
|
|
12631
|
+
await Promise.all(
|
|
12632
|
+
projectDirs.map(async (dirName) => {
|
|
12633
|
+
const dirPath = path47.join(projectsDir, dirName);
|
|
12634
|
+
let entries;
|
|
12635
|
+
try {
|
|
12636
|
+
entries = await fs24.promises.readdir(dirPath);
|
|
12637
|
+
} catch {
|
|
12638
|
+
return;
|
|
12639
|
+
}
|
|
12640
|
+
const jsonlFiles = entries.filter((e) => e.endsWith(".jsonl"));
|
|
12641
|
+
await Promise.all(
|
|
12642
|
+
jsonlFiles.map(async (file) => {
|
|
12643
|
+
const filePath = path47.join(dirPath, file);
|
|
12644
|
+
const session = await parseSessionFile(filePath);
|
|
12645
|
+
if (session) sessions.push(session);
|
|
12646
|
+
})
|
|
12647
|
+
);
|
|
12648
|
+
})
|
|
12649
|
+
);
|
|
12650
|
+
sessions.sort(
|
|
12651
|
+
(a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
|
12652
|
+
);
|
|
12653
|
+
return sessions;
|
|
12654
|
+
}
|
|
12655
|
+
|
|
12515
12656
|
// src/commands/sessions/web/scheduleIdle.ts
|
|
12516
12657
|
var IDLE_MS = 3e3;
|
|
12517
12658
|
function scheduleIdle(session, onIdle) {
|
|
@@ -12580,7 +12721,17 @@ var SessionManager = class {
|
|
|
12580
12721
|
spawn(prompt) {
|
|
12581
12722
|
const id = String(this.nextId++);
|
|
12582
12723
|
const session = createSession(id, prompt);
|
|
12583
|
-
this.
|
|
12724
|
+
this.wire(session);
|
|
12725
|
+
return id;
|
|
12726
|
+
}
|
|
12727
|
+
resume(sessionId, cwd, name) {
|
|
12728
|
+
const id = String(this.nextId++);
|
|
12729
|
+
const session = resumeSession(id, sessionId, cwd, name);
|
|
12730
|
+
this.wire(session);
|
|
12731
|
+
return id;
|
|
12732
|
+
}
|
|
12733
|
+
wire(session) {
|
|
12734
|
+
this.sessions.set(session.id, session);
|
|
12584
12735
|
wirePtyEvents(session, this.clients, (s, status2) => {
|
|
12585
12736
|
s.status = status2;
|
|
12586
12737
|
this.notify();
|
|
@@ -12590,7 +12741,6 @@ var SessionManager = class {
|
|
|
12590
12741
|
this.notify();
|
|
12591
12742
|
});
|
|
12592
12743
|
this.notify();
|
|
12593
|
-
return id;
|
|
12594
12744
|
}
|
|
12595
12745
|
writeToSession(id, data) {
|
|
12596
12746
|
const s = this.sessions.get(id);
|
|
@@ -12621,6 +12771,9 @@ var SessionManager = class {
|
|
|
12621
12771
|
})
|
|
12622
12772
|
);
|
|
12623
12773
|
}
|
|
12774
|
+
async getHistory() {
|
|
12775
|
+
return discoverSessions();
|
|
12776
|
+
}
|
|
12624
12777
|
notify() {
|
|
12625
12778
|
wsBroadcast(this.clients, {
|
|
12626
12779
|
type: "sessions",
|
|
@@ -12730,21 +12883,21 @@ async function statusLine() {
|
|
|
12730
12883
|
}
|
|
12731
12884
|
|
|
12732
12885
|
// src/commands/sync.ts
|
|
12733
|
-
import * as
|
|
12734
|
-
import * as
|
|
12735
|
-
import * as
|
|
12886
|
+
import * as fs27 from "fs";
|
|
12887
|
+
import * as os2 from "os";
|
|
12888
|
+
import * as path50 from "path";
|
|
12736
12889
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
12737
12890
|
|
|
12738
12891
|
// src/commands/sync/syncClaudeMd.ts
|
|
12739
|
-
import * as
|
|
12740
|
-
import * as
|
|
12892
|
+
import * as fs25 from "fs";
|
|
12893
|
+
import * as path48 from "path";
|
|
12741
12894
|
import chalk135 from "chalk";
|
|
12742
12895
|
async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
12743
|
-
const source =
|
|
12744
|
-
const target =
|
|
12745
|
-
const sourceContent =
|
|
12746
|
-
if (
|
|
12747
|
-
const targetContent =
|
|
12896
|
+
const source = path48.join(claudeDir, "CLAUDE.md");
|
|
12897
|
+
const target = path48.join(targetBase, "CLAUDE.md");
|
|
12898
|
+
const sourceContent = fs25.readFileSync(source, "utf-8");
|
|
12899
|
+
if (fs25.existsSync(target)) {
|
|
12900
|
+
const targetContent = fs25.readFileSync(target, "utf-8");
|
|
12748
12901
|
if (sourceContent !== targetContent) {
|
|
12749
12902
|
console.log(
|
|
12750
12903
|
chalk135.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
@@ -12761,21 +12914,21 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
|
12761
12914
|
}
|
|
12762
12915
|
}
|
|
12763
12916
|
}
|
|
12764
|
-
|
|
12917
|
+
fs25.copyFileSync(source, target);
|
|
12765
12918
|
console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
|
|
12766
12919
|
}
|
|
12767
12920
|
|
|
12768
12921
|
// src/commands/sync/syncSettings.ts
|
|
12769
|
-
import * as
|
|
12770
|
-
import * as
|
|
12922
|
+
import * as fs26 from "fs";
|
|
12923
|
+
import * as path49 from "path";
|
|
12771
12924
|
import chalk136 from "chalk";
|
|
12772
12925
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
12773
|
-
const source =
|
|
12774
|
-
const target =
|
|
12775
|
-
const sourceContent =
|
|
12926
|
+
const source = path49.join(claudeDir, "settings.json");
|
|
12927
|
+
const target = path49.join(targetBase, "settings.json");
|
|
12928
|
+
const sourceContent = fs26.readFileSync(source, "utf-8");
|
|
12776
12929
|
const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
|
|
12777
|
-
if (
|
|
12778
|
-
const targetContent =
|
|
12930
|
+
if (fs26.existsSync(target)) {
|
|
12931
|
+
const targetContent = fs26.readFileSync(target, "utf-8");
|
|
12779
12932
|
const normalizedTarget = JSON.stringify(
|
|
12780
12933
|
JSON.parse(targetContent),
|
|
12781
12934
|
null,
|
|
@@ -12801,29 +12954,29 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
12801
12954
|
}
|
|
12802
12955
|
}
|
|
12803
12956
|
}
|
|
12804
|
-
|
|
12957
|
+
fs26.writeFileSync(target, mergedContent);
|
|
12805
12958
|
console.log("Copied settings.json to ~/.claude/settings.json");
|
|
12806
12959
|
}
|
|
12807
12960
|
|
|
12808
12961
|
// src/commands/sync.ts
|
|
12809
12962
|
var __filename4 = fileURLToPath7(import.meta.url);
|
|
12810
|
-
var __dirname7 =
|
|
12963
|
+
var __dirname7 = path50.dirname(__filename4);
|
|
12811
12964
|
async function sync(options2) {
|
|
12812
12965
|
const config = loadConfig();
|
|
12813
12966
|
const yes = options2?.yes ?? config.sync.autoConfirm;
|
|
12814
|
-
const claudeDir =
|
|
12815
|
-
const targetBase =
|
|
12967
|
+
const claudeDir = path50.join(__dirname7, "..", "claude");
|
|
12968
|
+
const targetBase = path50.join(os2.homedir(), ".claude");
|
|
12816
12969
|
syncCommands(claudeDir, targetBase);
|
|
12817
12970
|
await syncSettings(claudeDir, targetBase, { yes });
|
|
12818
12971
|
await syncClaudeMd(claudeDir, targetBase, { yes });
|
|
12819
12972
|
}
|
|
12820
12973
|
function syncCommands(claudeDir, targetBase) {
|
|
12821
|
-
const sourceDir =
|
|
12822
|
-
const targetDir =
|
|
12823
|
-
|
|
12824
|
-
const files =
|
|
12974
|
+
const sourceDir = path50.join(claudeDir, "commands");
|
|
12975
|
+
const targetDir = path50.join(targetBase, "commands");
|
|
12976
|
+
fs27.mkdirSync(targetDir, { recursive: true });
|
|
12977
|
+
const files = fs27.readdirSync(sourceDir);
|
|
12825
12978
|
for (const file of files) {
|
|
12826
|
-
|
|
12979
|
+
fs27.copyFileSync(path50.join(sourceDir, file), path50.join(targetDir, file));
|
|
12827
12980
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
12828
12981
|
}
|
|
12829
12982
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
@@ -12831,15 +12984,15 @@ function syncCommands(claudeDir, targetBase) {
|
|
|
12831
12984
|
|
|
12832
12985
|
// src/commands/update.ts
|
|
12833
12986
|
import { execSync as execSync42 } from "child_process";
|
|
12834
|
-
import * as
|
|
12987
|
+
import * as path51 from "path";
|
|
12835
12988
|
function isGlobalNpmInstall(dir) {
|
|
12836
12989
|
try {
|
|
12837
|
-
const resolved =
|
|
12838
|
-
if (resolved.split(
|
|
12990
|
+
const resolved = path51.resolve(dir);
|
|
12991
|
+
if (resolved.split(path51.sep).includes("node_modules")) {
|
|
12839
12992
|
return true;
|
|
12840
12993
|
}
|
|
12841
12994
|
const globalPrefix = execSync42("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
12842
|
-
return resolved.toLowerCase().startsWith(
|
|
12995
|
+
return resolved.toLowerCase().startsWith(path51.resolve(globalPrefix).toLowerCase());
|
|
12843
12996
|
} catch {
|
|
12844
12997
|
return false;
|
|
12845
12998
|
}
|