@staff0rd/assist 0.220.1 → 0.221.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 +5 -0
- package/claude/commands/handover.md +54 -0
- package/claude/commands/recall.md +52 -0
- package/claude/settings.json +10 -0
- package/dist/index.js +712 -325
- 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.221.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -3704,19 +3704,19 @@ function moveCaseInsensitive(absSource, absDest) {
|
|
|
3704
3704
|
fs8.renameSync(absSource, tmp);
|
|
3705
3705
|
fs8.renameSync(tmp, absDest);
|
|
3706
3706
|
}
|
|
3707
|
-
function applyMoves(project, moves, cwd,
|
|
3707
|
+
function applyMoves(project, moves, cwd, emit2) {
|
|
3708
3708
|
for (const { sourcePath, destPath } of moves) {
|
|
3709
3709
|
const start3 = performance.now();
|
|
3710
3710
|
const absSource = path16.resolve(sourcePath);
|
|
3711
3711
|
const absDest = path16.resolve(destPath);
|
|
3712
3712
|
for (const r of renameExports(project, absSource, absDest)) {
|
|
3713
|
-
|
|
3713
|
+
emit2(` Renamed export ${r} in ${sourcePath}`);
|
|
3714
3714
|
}
|
|
3715
3715
|
const sourceFile = project.getSourceFile(absSource);
|
|
3716
3716
|
if (sourceFile) sourceFile.move(absDest);
|
|
3717
3717
|
const ms = (performance.now() - start3).toFixed(0);
|
|
3718
3718
|
const rel = `${path16.relative(cwd, absSource)} \u2192 ${path16.relative(cwd, absDest)}`;
|
|
3719
|
-
|
|
3719
|
+
emit2(` Renamed ${rel} (${ms}ms)`);
|
|
3720
3720
|
}
|
|
3721
3721
|
project.saveSync();
|
|
3722
3722
|
for (const { sourcePath, destPath } of moves) {
|
|
@@ -4070,10 +4070,10 @@ function printTaskStatuses(tasks) {
|
|
|
4070
4070
|
}
|
|
4071
4071
|
|
|
4072
4072
|
// src/commands/verify/run/createTimerCallback/index.ts
|
|
4073
|
-
function logFailedScripts(
|
|
4073
|
+
function logFailedScripts(failed2) {
|
|
4074
4074
|
console.error(`
|
|
4075
|
-
${
|
|
4076
|
-
for (const f of
|
|
4075
|
+
${failed2.length} script(s) failed:`);
|
|
4076
|
+
for (const f of failed2) {
|
|
4077
4077
|
console.error(` - ${f.script} (exit code ${f.code})`);
|
|
4078
4078
|
}
|
|
4079
4079
|
}
|
|
@@ -4150,9 +4150,9 @@ function runEntry(entry, onComplete) {
|
|
|
4150
4150
|
});
|
|
4151
4151
|
});
|
|
4152
4152
|
}
|
|
4153
|
-
function exitIfFailed(
|
|
4154
|
-
if (
|
|
4155
|
-
logFailedScripts(
|
|
4153
|
+
function exitIfFailed(failed2) {
|
|
4154
|
+
if (failed2.length === 0) return;
|
|
4155
|
+
logFailedScripts(failed2);
|
|
4156
4156
|
process.exit(1);
|
|
4157
4157
|
}
|
|
4158
4158
|
function runAllEntries(entries, timer) {
|
|
@@ -8392,6 +8392,303 @@ function registerDotnet(program2) {
|
|
|
8392
8392
|
cmd.command("in-sln").description("Check whether a .csproj is referenced by any .sln file").argument("<csproj>", "Path to a .csproj file").action(inSln);
|
|
8393
8393
|
}
|
|
8394
8394
|
|
|
8395
|
+
// src/commands/handover/archive.ts
|
|
8396
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync7, renameSync as renameSync2 } from "fs";
|
|
8397
|
+
import { join as join31 } from "path";
|
|
8398
|
+
|
|
8399
|
+
// src/commands/handover/formatArchiveTimestamp.ts
|
|
8400
|
+
function formatArchiveTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
8401
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
8402
|
+
const yyyy = date.getUTCFullYear().toString();
|
|
8403
|
+
const mm = pad(date.getUTCMonth() + 1);
|
|
8404
|
+
const dd = pad(date.getUTCDate());
|
|
8405
|
+
const hh = pad(date.getUTCHours());
|
|
8406
|
+
const mi = pad(date.getUTCMinutes());
|
|
8407
|
+
const ss = pad(date.getUTCSeconds());
|
|
8408
|
+
return `${yyyy}-${mm}-${dd}T${hh}${mi}${ss}Z`;
|
|
8409
|
+
}
|
|
8410
|
+
|
|
8411
|
+
// src/commands/handover/getHandoverArchiveDir.ts
|
|
8412
|
+
import { join as join29 } from "path";
|
|
8413
|
+
function getHandoverArchiveDir(cwd = process.cwd()) {
|
|
8414
|
+
return join29(cwd, ".assist", "handovers", "archive");
|
|
8415
|
+
}
|
|
8416
|
+
|
|
8417
|
+
// src/commands/handover/getHandoverPath.ts
|
|
8418
|
+
import { join as join30 } from "path";
|
|
8419
|
+
function getHandoverPath(cwd = process.cwd()) {
|
|
8420
|
+
return join30(cwd, ".assist", "HANDOVER.md");
|
|
8421
|
+
}
|
|
8422
|
+
|
|
8423
|
+
// src/commands/handover/archive.ts
|
|
8424
|
+
var MAX_COLLISION_SUFFIX = 99;
|
|
8425
|
+
function buildArchiveFilename(timestamp, suffix) {
|
|
8426
|
+
const base = suffix ? `${timestamp}-${suffix}` : timestamp;
|
|
8427
|
+
return `${base}.md`;
|
|
8428
|
+
}
|
|
8429
|
+
function resolveCollisionPath(archiveDir, timestamp, suffix) {
|
|
8430
|
+
const initial = join31(archiveDir, buildArchiveFilename(timestamp, suffix));
|
|
8431
|
+
if (!existsSync30(initial)) return initial;
|
|
8432
|
+
for (let i = 1; i <= MAX_COLLISION_SUFFIX; i++) {
|
|
8433
|
+
const collisionSuffix = suffix ? `${suffix}-${i}` : `${i}`;
|
|
8434
|
+
const candidate = join31(
|
|
8435
|
+
archiveDir,
|
|
8436
|
+
buildArchiveFilename(timestamp, collisionSuffix)
|
|
8437
|
+
);
|
|
8438
|
+
if (!existsSync30(candidate)) return candidate;
|
|
8439
|
+
}
|
|
8440
|
+
throw new Error(
|
|
8441
|
+
`Exhausted collision suffixes (1-${MAX_COLLISION_SUFFIX}) for ${timestamp}`
|
|
8442
|
+
);
|
|
8443
|
+
}
|
|
8444
|
+
function archive(options2 = {}) {
|
|
8445
|
+
const cwd = options2.cwd ?? process.cwd();
|
|
8446
|
+
const handoverPath = getHandoverPath(cwd);
|
|
8447
|
+
if (!existsSync30(handoverPath)) return void 0;
|
|
8448
|
+
const archiveDir = getHandoverArchiveDir(cwd);
|
|
8449
|
+
mkdirSync7(archiveDir, { recursive: true });
|
|
8450
|
+
const timestamp = formatArchiveTimestamp(options2.now);
|
|
8451
|
+
const destination = resolveCollisionPath(
|
|
8452
|
+
archiveDir,
|
|
8453
|
+
timestamp,
|
|
8454
|
+
options2.suffix
|
|
8455
|
+
);
|
|
8456
|
+
renameSync2(handoverPath, destination);
|
|
8457
|
+
return destination;
|
|
8458
|
+
}
|
|
8459
|
+
|
|
8460
|
+
// src/commands/handover/load.ts
|
|
8461
|
+
import { existsSync as existsSync31, readFileSync as readFileSync27 } from "fs";
|
|
8462
|
+
|
|
8463
|
+
// src/commands/handover/parseLoadInput.ts
|
|
8464
|
+
async function parseLoadInput(stdin) {
|
|
8465
|
+
try {
|
|
8466
|
+
const raw = await stdin();
|
|
8467
|
+
if (!raw.trim()) return {};
|
|
8468
|
+
return JSON.parse(raw);
|
|
8469
|
+
} catch {
|
|
8470
|
+
return {};
|
|
8471
|
+
}
|
|
8472
|
+
}
|
|
8473
|
+
|
|
8474
|
+
// src/commands/handover/findRecentSessionJsonl.ts
|
|
8475
|
+
import { readdirSync as readdirSync5, statSync as statSync4 } from "fs";
|
|
8476
|
+
import { homedir as homedir8 } from "os";
|
|
8477
|
+
import { join as join32 } from "path";
|
|
8478
|
+
|
|
8479
|
+
// src/commands/handover/encodeCwdForProjects.ts
|
|
8480
|
+
function encodeCwdForProjects(cwd) {
|
|
8481
|
+
return cwd.replace(/[\\/.]/g, "-");
|
|
8482
|
+
}
|
|
8483
|
+
|
|
8484
|
+
// src/commands/sessions/summarise/iterateUserEntries.ts
|
|
8485
|
+
import * as fs17 from "fs";
|
|
8486
|
+
|
|
8487
|
+
// src/commands/sessions/summarise/parseUserLine.ts
|
|
8488
|
+
function parseUserLine(line) {
|
|
8489
|
+
let entry;
|
|
8490
|
+
try {
|
|
8491
|
+
entry = JSON.parse(line);
|
|
8492
|
+
} catch {
|
|
8493
|
+
return void 0;
|
|
8494
|
+
}
|
|
8495
|
+
if (entry.type !== "user") return void 0;
|
|
8496
|
+
const msg = entry.message;
|
|
8497
|
+
const c = msg?.content;
|
|
8498
|
+
let text;
|
|
8499
|
+
if (typeof c === "string") {
|
|
8500
|
+
text = c;
|
|
8501
|
+
} else if (Array.isArray(c)) {
|
|
8502
|
+
const collected = c.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n");
|
|
8503
|
+
text = collected || void 0;
|
|
8504
|
+
}
|
|
8505
|
+
if (!text) return void 0;
|
|
8506
|
+
return {
|
|
8507
|
+
text,
|
|
8508
|
+
entrypoint: typeof entry.entrypoint === "string" ? entry.entrypoint : void 0
|
|
8509
|
+
};
|
|
8510
|
+
}
|
|
8511
|
+
|
|
8512
|
+
// src/commands/sessions/summarise/iterateUserEntries.ts
|
|
8513
|
+
function* iterateUserEntries(filePath) {
|
|
8514
|
+
let content;
|
|
8515
|
+
try {
|
|
8516
|
+
content = fs17.readFileSync(filePath, "utf8");
|
|
8517
|
+
} catch {
|
|
8518
|
+
return;
|
|
8519
|
+
}
|
|
8520
|
+
for (const line of content.split("\n")) {
|
|
8521
|
+
if (!line) continue;
|
|
8522
|
+
const entry = parseUserLine(line);
|
|
8523
|
+
if (entry) yield entry;
|
|
8524
|
+
}
|
|
8525
|
+
}
|
|
8526
|
+
|
|
8527
|
+
// src/commands/handover/isSdkCliOnly.ts
|
|
8528
|
+
function isSdkCliOnly(jsonlPath) {
|
|
8529
|
+
for (const entry of iterateUserEntries(jsonlPath)) {
|
|
8530
|
+
if (entry.entrypoint !== "sdk-cli") return false;
|
|
8531
|
+
}
|
|
8532
|
+
return true;
|
|
8533
|
+
}
|
|
8534
|
+
|
|
8535
|
+
// src/commands/handover/findRecentSessionJsonl.ts
|
|
8536
|
+
function getProjectDir(cwd) {
|
|
8537
|
+
return join32(homedir8(), ".claude", "projects", encodeCwdForProjects(cwd));
|
|
8538
|
+
}
|
|
8539
|
+
function findRecentSessionJsonl(cwd, options2 = {}) {
|
|
8540
|
+
const projectDir = options2.projectDir ?? getProjectDir(cwd);
|
|
8541
|
+
let entries;
|
|
8542
|
+
try {
|
|
8543
|
+
entries = readdirSync5(projectDir);
|
|
8544
|
+
} catch {
|
|
8545
|
+
return void 0;
|
|
8546
|
+
}
|
|
8547
|
+
const jsonls = entries.filter((f) => f.endsWith(".jsonl")).map((name) => {
|
|
8548
|
+
const path52 = join32(projectDir, name);
|
|
8549
|
+
let mtime = 0;
|
|
8550
|
+
try {
|
|
8551
|
+
mtime = statSync4(path52).mtimeMs;
|
|
8552
|
+
} catch {
|
|
8553
|
+
return void 0;
|
|
8554
|
+
}
|
|
8555
|
+
return { path: path52, name, mtime };
|
|
8556
|
+
}).filter((x) => !!x).sort((a, b) => b.mtime - a.mtime);
|
|
8557
|
+
for (const { path: path52, name } of jsonls) {
|
|
8558
|
+
const sessionId = name.replace(/\.jsonl$/, "");
|
|
8559
|
+
if (options2.excludeSessionId && sessionId === options2.excludeSessionId)
|
|
8560
|
+
continue;
|
|
8561
|
+
if (isSdkCliOnly(path52)) continue;
|
|
8562
|
+
return path52;
|
|
8563
|
+
}
|
|
8564
|
+
return void 0;
|
|
8565
|
+
}
|
|
8566
|
+
|
|
8567
|
+
// src/commands/handover/summarise.ts
|
|
8568
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
8569
|
+
var SUMMARISE_RECURSION_GUARD = "_CLAUDE_HOOK_SUMMARISE_RUNNING";
|
|
8570
|
+
var MAX_TURNS = 15;
|
|
8571
|
+
var MAX_PAYLOAD_BYTES = 8 * 1024;
|
|
8572
|
+
var PROMPT_TEMPLATE = [
|
|
8573
|
+
"Summarise what the user has been working on in this Claude Code session in ONE short sentence (under 100 chars).",
|
|
8574
|
+
"Return ONLY the summary, no quotes, prefix, or explanation.",
|
|
8575
|
+
"",
|
|
8576
|
+
"Last user turns (most recent last):"
|
|
8577
|
+
].join("\n");
|
|
8578
|
+
function summarise(jsonlPath) {
|
|
8579
|
+
const entries = [...iterateUserEntries(jsonlPath)];
|
|
8580
|
+
const humanEntries = entries.filter((e) => e.entrypoint !== "sdk-cli");
|
|
8581
|
+
if (humanEntries.length === 0) return "";
|
|
8582
|
+
const turns = humanEntries.map((e) => stripPreludes(e.text)).filter((t) => t.length > 0).slice(-MAX_TURNS);
|
|
8583
|
+
if (turns.length === 0) return "";
|
|
8584
|
+
const payload = capPayload(turns.join("\n---\n"), MAX_PAYLOAD_BYTES);
|
|
8585
|
+
const prompt = `${PROMPT_TEMPLATE}
|
|
8586
|
+
${payload}`;
|
|
8587
|
+
try {
|
|
8588
|
+
const output = execFileSync4("claude", ["-p", "--model", "haiku", prompt], {
|
|
8589
|
+
encoding: "utf8",
|
|
8590
|
+
timeout: 3e4,
|
|
8591
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
8592
|
+
env: { ...process.env, [SUMMARISE_RECURSION_GUARD]: "1" }
|
|
8593
|
+
});
|
|
8594
|
+
return normaliseOutput(output);
|
|
8595
|
+
} catch {
|
|
8596
|
+
return "";
|
|
8597
|
+
}
|
|
8598
|
+
}
|
|
8599
|
+
function stripPreludes(text) {
|
|
8600
|
+
return text.replace(/<command-name>[\s\S]*?<\/command-name>/g, "").replace(/<command-message>[\s\S]*?<\/command-message>/g, "").replace(/<command-args>[\s\S]*?<\/command-args>/g, "").replace(/<local-command-stdout>[\s\S]*?<\/local-command-stdout>/g, "").replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trim();
|
|
8601
|
+
}
|
|
8602
|
+
function capPayload(text, maxBytes) {
|
|
8603
|
+
const buf = Buffer.from(text, "utf8");
|
|
8604
|
+
if (buf.length <= maxBytes) return text;
|
|
8605
|
+
return buf.subarray(buf.length - maxBytes).toString("utf8");
|
|
8606
|
+
}
|
|
8607
|
+
function normaliseOutput(raw) {
|
|
8608
|
+
const firstLine = raw.trim().split("\n")[0] ?? "";
|
|
8609
|
+
return firstLine.replace(/^["']|["']$/g, "").trim();
|
|
8610
|
+
}
|
|
8611
|
+
|
|
8612
|
+
// src/commands/handover/resolveLoadOptions.ts
|
|
8613
|
+
function resolveLoadOptions(options2) {
|
|
8614
|
+
return {
|
|
8615
|
+
stdin: options2.stdin ?? readStdin,
|
|
8616
|
+
env: options2.env ?? process.env,
|
|
8617
|
+
cwdFallback: options2.cwdFallback ?? process.cwd(),
|
|
8618
|
+
summariseFn: options2.summariseFn ?? summarise,
|
|
8619
|
+
findRecentFn: options2.findRecentFn ?? ((cwd, sid) => findRecentSessionJsonl(cwd, { excludeSessionId: sid }))
|
|
8620
|
+
};
|
|
8621
|
+
}
|
|
8622
|
+
|
|
8623
|
+
// src/commands/handover/load.ts
|
|
8624
|
+
function loadFromHandover(cwd) {
|
|
8625
|
+
const handoverPath = getHandoverPath(cwd);
|
|
8626
|
+
if (!existsSync31(handoverPath)) return void 0;
|
|
8627
|
+
const content = readFileSync27(handoverPath, "utf-8");
|
|
8628
|
+
archive({ cwd });
|
|
8629
|
+
return {
|
|
8630
|
+
additionalContext: content,
|
|
8631
|
+
systemMessage: "Loaded handover from previous session"
|
|
8632
|
+
};
|
|
8633
|
+
}
|
|
8634
|
+
function loadFromPriorTranscript(cwd, sessionId, findRecent, summariseJsonl) {
|
|
8635
|
+
const jsonlPath = findRecent(cwd, sessionId);
|
|
8636
|
+
if (!jsonlPath) return void 0;
|
|
8637
|
+
const summary = summariseJsonl(jsonlPath);
|
|
8638
|
+
if (!summary) return void 0;
|
|
8639
|
+
const message = `Previous session: ${summary}`;
|
|
8640
|
+
return { additionalContext: message, systemMessage: message };
|
|
8641
|
+
}
|
|
8642
|
+
function emit(context) {
|
|
8643
|
+
const json = JSON.stringify({
|
|
8644
|
+
hookSpecificOutput: {
|
|
8645
|
+
hookEventName: "SessionStart",
|
|
8646
|
+
additionalContext: context.additionalContext
|
|
8647
|
+
},
|
|
8648
|
+
systemMessage: context.systemMessage
|
|
8649
|
+
});
|
|
8650
|
+
console.log(json);
|
|
8651
|
+
return json;
|
|
8652
|
+
}
|
|
8653
|
+
async function load(options2 = {}) {
|
|
8654
|
+
const opts = resolveLoadOptions(options2);
|
|
8655
|
+
if (opts.env[SUMMARISE_RECURSION_GUARD]) return null;
|
|
8656
|
+
const input = await parseLoadInput(opts.stdin);
|
|
8657
|
+
const cwd = input.cwd ?? opts.cwdFallback;
|
|
8658
|
+
const context = loadFromHandover(cwd) ?? loadFromPriorTranscript(
|
|
8659
|
+
cwd,
|
|
8660
|
+
input.session_id,
|
|
8661
|
+
opts.findRecentFn,
|
|
8662
|
+
opts.summariseFn
|
|
8663
|
+
);
|
|
8664
|
+
return context ? emit(context) : null;
|
|
8665
|
+
}
|
|
8666
|
+
|
|
8667
|
+
// src/commands/registerHandover.ts
|
|
8668
|
+
function registerHandover(program2) {
|
|
8669
|
+
const cmd = program2.command("handover").description("Session handover utilities");
|
|
8670
|
+
cmd.command("archive").description(
|
|
8671
|
+
"Archive the current .assist/HANDOVER.md to .assist/handovers/archive/"
|
|
8672
|
+
).option(
|
|
8673
|
+
"--suffix <suffix>",
|
|
8674
|
+
"Optional suffix appended to the archive filename"
|
|
8675
|
+
).action((options2) => {
|
|
8676
|
+
const dest = archive({ suffix: options2.suffix });
|
|
8677
|
+
if (dest) console.log(dest);
|
|
8678
|
+
});
|
|
8679
|
+
cmd.command("summarise").description(
|
|
8680
|
+
"Print a one-line summary of a session JSONL via claude -p --model haiku"
|
|
8681
|
+
).argument("<jsonl>", "Path to a session JSONL file").action((jsonl) => {
|
|
8682
|
+
const line = summarise(jsonl);
|
|
8683
|
+
if (line) console.log(line);
|
|
8684
|
+
});
|
|
8685
|
+
cmd.command("load").description(
|
|
8686
|
+
"SessionStart hook: archive prior handover and emit additionalContext (or fall back to prior-session summary)"
|
|
8687
|
+
).action(async () => {
|
|
8688
|
+
await load();
|
|
8689
|
+
});
|
|
8690
|
+
}
|
|
8691
|
+
|
|
8395
8692
|
// src/commands/jira/acceptanceCriteria.ts
|
|
8396
8693
|
import chalk97 from "chalk";
|
|
8397
8694
|
|
|
@@ -8401,52 +8698,52 @@ function renderInline(node) {
|
|
|
8401
8698
|
if (node.marks?.some((m) => m.type === "code")) return `\`${text}\``;
|
|
8402
8699
|
return text;
|
|
8403
8700
|
}
|
|
8404
|
-
function renderChildren(node,
|
|
8405
|
-
return renderNodes(node.content ?? [],
|
|
8701
|
+
function renderChildren(node, indent2) {
|
|
8702
|
+
return renderNodes(node.content ?? [], indent2);
|
|
8406
8703
|
}
|
|
8407
|
-
function renderOrderedList(node,
|
|
8704
|
+
function renderOrderedList(node, indent2) {
|
|
8408
8705
|
let counter = 0;
|
|
8409
8706
|
return (node.content ?? []).map((item) => {
|
|
8410
8707
|
counter++;
|
|
8411
|
-
return renderListItem(item,
|
|
8708
|
+
return renderListItem(item, indent2, `${counter}.`);
|
|
8412
8709
|
}).join("\n");
|
|
8413
8710
|
}
|
|
8414
|
-
function renderBulletList(node,
|
|
8415
|
-
return (node.content ?? []).map((item) => renderListItem(item,
|
|
8711
|
+
function renderBulletList(node, indent2) {
|
|
8712
|
+
return (node.content ?? []).map((item) => renderListItem(item, indent2, "-")).join("\n");
|
|
8416
8713
|
}
|
|
8417
|
-
function renderHeading(node,
|
|
8714
|
+
function renderHeading(node, indent2) {
|
|
8418
8715
|
const level = node.attrs?.level ?? 1;
|
|
8419
|
-
return `${"#".repeat(level)} ${renderChildren(node,
|
|
8716
|
+
return `${"#".repeat(level)} ${renderChildren(node, indent2)}`;
|
|
8420
8717
|
}
|
|
8421
8718
|
var renderers = {
|
|
8422
8719
|
text: (node) => renderInline(node),
|
|
8423
8720
|
paragraph: renderChildren,
|
|
8424
8721
|
orderedList: renderOrderedList,
|
|
8425
8722
|
bulletList: renderBulletList,
|
|
8426
|
-
listItem: (node,
|
|
8723
|
+
listItem: (node, indent2) => renderListItem(node, indent2, "-"),
|
|
8427
8724
|
heading: renderHeading,
|
|
8428
8725
|
doc: renderChildren
|
|
8429
8726
|
};
|
|
8430
|
-
function renderNode(node,
|
|
8727
|
+
function renderNode(node, indent2) {
|
|
8431
8728
|
const renderer = renderers[node.type];
|
|
8432
|
-
if (renderer) return renderer(node,
|
|
8433
|
-
return node.content ? renderChildren(node,
|
|
8729
|
+
if (renderer) return renderer(node, indent2);
|
|
8730
|
+
return node.content ? renderChildren(node, indent2) : "";
|
|
8434
8731
|
}
|
|
8435
|
-
function renderNodes(nodes,
|
|
8436
|
-
return nodes.map((node) => renderNode(node,
|
|
8732
|
+
function renderNodes(nodes, indent2) {
|
|
8733
|
+
return nodes.map((node) => renderNode(node, indent2)).join("");
|
|
8437
8734
|
}
|
|
8438
8735
|
function isListNode(node) {
|
|
8439
8736
|
return node.type === "orderedList" || node.type === "bulletList";
|
|
8440
8737
|
}
|
|
8441
|
-
function renderListChild(child,
|
|
8442
|
-
if (isListNode(child)) return renderNodes([child],
|
|
8443
|
-
if (child.type !== "paragraph") return renderNode(child,
|
|
8444
|
-
const text = renderChildren(child,
|
|
8738
|
+
function renderListChild(child, indent2, pad, marker, isFirst) {
|
|
8739
|
+
if (isListNode(child)) return renderNodes([child], indent2 + 1);
|
|
8740
|
+
if (child.type !== "paragraph") return renderNode(child, indent2);
|
|
8741
|
+
const text = renderChildren(child, indent2);
|
|
8445
8742
|
return isFirst ? `${pad}${marker} ${text}` : `${pad} ${text}`;
|
|
8446
8743
|
}
|
|
8447
|
-
function renderListItem(node,
|
|
8448
|
-
const pad = " ".repeat(
|
|
8449
|
-
return (node.content ?? []).map((child, i) => renderListChild(child,
|
|
8744
|
+
function renderListItem(node, indent2, marker) {
|
|
8745
|
+
const pad = " ".repeat(indent2);
|
|
8746
|
+
return (node.content ?? []).map((child, i) => renderListChild(child, indent2, pad, marker, i === 0)).join("\n");
|
|
8450
8747
|
}
|
|
8451
8748
|
function adfToText(doc) {
|
|
8452
8749
|
return renderNodes([doc], 0);
|
|
@@ -8507,20 +8804,20 @@ function acceptanceCriteria(issueKey) {
|
|
|
8507
8804
|
import { execSync as execSync25 } from "child_process";
|
|
8508
8805
|
|
|
8509
8806
|
// src/shared/loadJson.ts
|
|
8510
|
-
import { existsSync as
|
|
8511
|
-
import { homedir as
|
|
8512
|
-
import { join as
|
|
8807
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync8, readFileSync as readFileSync28, writeFileSync as writeFileSync20 } from "fs";
|
|
8808
|
+
import { homedir as homedir9 } from "os";
|
|
8809
|
+
import { join as join33 } from "path";
|
|
8513
8810
|
function getStoreDir() {
|
|
8514
|
-
return
|
|
8811
|
+
return join33(homedir9(), ".assist");
|
|
8515
8812
|
}
|
|
8516
8813
|
function getStorePath(filename) {
|
|
8517
|
-
return
|
|
8814
|
+
return join33(getStoreDir(), filename);
|
|
8518
8815
|
}
|
|
8519
8816
|
function loadJson(filename) {
|
|
8520
8817
|
const path52 = getStorePath(filename);
|
|
8521
|
-
if (
|
|
8818
|
+
if (existsSync32(path52)) {
|
|
8522
8819
|
try {
|
|
8523
|
-
return JSON.parse(
|
|
8820
|
+
return JSON.parse(readFileSync28(path52, "utf-8"));
|
|
8524
8821
|
} catch {
|
|
8525
8822
|
return {};
|
|
8526
8823
|
}
|
|
@@ -8529,8 +8826,8 @@ function loadJson(filename) {
|
|
|
8529
8826
|
}
|
|
8530
8827
|
function saveJson(filename, data) {
|
|
8531
8828
|
const dir = getStoreDir();
|
|
8532
|
-
if (!
|
|
8533
|
-
|
|
8829
|
+
if (!existsSync32(dir)) {
|
|
8830
|
+
mkdirSync8(dir, { recursive: true });
|
|
8534
8831
|
}
|
|
8535
8832
|
writeFileSync20(getStorePath(filename), JSON.stringify(data, null, 2));
|
|
8536
8833
|
}
|
|
@@ -8619,12 +8916,12 @@ function registerJira(program2) {
|
|
|
8619
8916
|
}
|
|
8620
8917
|
|
|
8621
8918
|
// src/commands/mermaid/index.ts
|
|
8622
|
-
import { mkdirSync as
|
|
8919
|
+
import { mkdirSync as mkdirSync9, readdirSync as readdirSync6 } from "fs";
|
|
8623
8920
|
import { resolve as resolve10 } from "path";
|
|
8624
8921
|
import chalk101 from "chalk";
|
|
8625
8922
|
|
|
8626
8923
|
// src/commands/mermaid/exportFile.ts
|
|
8627
|
-
import { readFileSync as
|
|
8924
|
+
import { readFileSync as readFileSync29, writeFileSync as writeFileSync21 } from "fs";
|
|
8628
8925
|
import { basename as basename7, extname, resolve as resolve9 } from "path";
|
|
8629
8926
|
import chalk100 from "chalk";
|
|
8630
8927
|
|
|
@@ -8650,7 +8947,7 @@ async function renderBlock(krokiUrl, source) {
|
|
|
8650
8947
|
|
|
8651
8948
|
// src/commands/mermaid/exportFile.ts
|
|
8652
8949
|
async function exportFile(file, outDir, krokiUrl, onlyIndex) {
|
|
8653
|
-
const content =
|
|
8950
|
+
const content = readFileSync29(file, "utf8");
|
|
8654
8951
|
const blocks = extractMermaidBlocks(content);
|
|
8655
8952
|
const stem = basename7(file, extname(file));
|
|
8656
8953
|
if (onlyIndex !== void 0) {
|
|
@@ -8688,7 +8985,7 @@ function extractMermaidBlocks(markdown) {
|
|
|
8688
8985
|
async function mermaidExport(file, options2 = {}) {
|
|
8689
8986
|
const { mermaid } = loadConfig();
|
|
8690
8987
|
const outDir = resolve10(process.cwd(), options2.out ?? ".");
|
|
8691
|
-
|
|
8988
|
+
mkdirSync9(outDir, { recursive: true });
|
|
8692
8989
|
if (options2.index !== void 0) {
|
|
8693
8990
|
if (!Number.isInteger(options2.index) || options2.index < 1) {
|
|
8694
8991
|
console.error(
|
|
@@ -8701,7 +8998,7 @@ async function mermaidExport(file, options2 = {}) {
|
|
|
8701
8998
|
process.exit(1);
|
|
8702
8999
|
}
|
|
8703
9000
|
}
|
|
8704
|
-
const files = file ? [file] :
|
|
9001
|
+
const files = file ? [file] : readdirSync6(process.cwd()).filter((name) => name.toLowerCase().endsWith(".md")).sort();
|
|
8705
9002
|
if (files.length === 0) {
|
|
8706
9003
|
console.log(chalk101.gray("No markdown files found in current directory."));
|
|
8707
9004
|
return;
|
|
@@ -9007,7 +9304,7 @@ function registerPrompts(program2) {
|
|
|
9007
9304
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9008
9305
|
import { unlinkSync as unlinkSync6, writeFileSync as writeFileSync22 } from "fs";
|
|
9009
9306
|
import { tmpdir as tmpdir4 } from "os";
|
|
9010
|
-
import { join as
|
|
9307
|
+
import { join as join34 } from "path";
|
|
9011
9308
|
function buildArgs2(queryFile, vars) {
|
|
9012
9309
|
const args = ["api", "graphql", "-F", `query=@${queryFile}`];
|
|
9013
9310
|
for (const [key, value] of Object.entries(vars)) {
|
|
@@ -9017,7 +9314,7 @@ function buildArgs2(queryFile, vars) {
|
|
|
9017
9314
|
return args;
|
|
9018
9315
|
}
|
|
9019
9316
|
function runGhGraphql(mutation, vars) {
|
|
9020
|
-
const queryFile =
|
|
9317
|
+
const queryFile = join34(tmpdir4(), `gh-query-${Date.now()}.graphql`);
|
|
9021
9318
|
writeFileSync22(queryFile, mutation);
|
|
9022
9319
|
try {
|
|
9023
9320
|
const result = spawnSync2("gh", buildArgs2(queryFile, vars), {
|
|
@@ -9211,26 +9508,26 @@ import { execSync as execSync29 } from "child_process";
|
|
|
9211
9508
|
import { execSync as execSync28 } from "child_process";
|
|
9212
9509
|
import { unlinkSync as unlinkSync8, writeFileSync as writeFileSync23 } from "fs";
|
|
9213
9510
|
import { tmpdir as tmpdir5 } from "os";
|
|
9214
|
-
import { join as
|
|
9511
|
+
import { join as join36 } from "path";
|
|
9215
9512
|
|
|
9216
9513
|
// src/commands/prs/loadCommentsCache.ts
|
|
9217
|
-
import { existsSync as
|
|
9218
|
-
import { join as
|
|
9514
|
+
import { existsSync as existsSync33, readFileSync as readFileSync30, unlinkSync as unlinkSync7 } from "fs";
|
|
9515
|
+
import { join as join35 } from "path";
|
|
9219
9516
|
import { parse as parse2 } from "yaml";
|
|
9220
9517
|
function getCachePath(prNumber) {
|
|
9221
|
-
return
|
|
9518
|
+
return join35(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`);
|
|
9222
9519
|
}
|
|
9223
9520
|
function loadCommentsCache(prNumber) {
|
|
9224
9521
|
const cachePath = getCachePath(prNumber);
|
|
9225
|
-
if (!
|
|
9522
|
+
if (!existsSync33(cachePath)) {
|
|
9226
9523
|
return null;
|
|
9227
9524
|
}
|
|
9228
|
-
const content =
|
|
9525
|
+
const content = readFileSync30(cachePath, "utf-8");
|
|
9229
9526
|
return parse2(content);
|
|
9230
9527
|
}
|
|
9231
9528
|
function deleteCommentsCache(prNumber) {
|
|
9232
9529
|
const cachePath = getCachePath(prNumber);
|
|
9233
|
-
if (
|
|
9530
|
+
if (existsSync33(cachePath)) {
|
|
9234
9531
|
unlinkSync7(cachePath);
|
|
9235
9532
|
console.log("No more unresolved line comments. Cache dropped.");
|
|
9236
9533
|
}
|
|
@@ -9245,7 +9542,7 @@ function replyToComment(org, repo, prNumber, commentId, message) {
|
|
|
9245
9542
|
}
|
|
9246
9543
|
function resolveThread(threadId) {
|
|
9247
9544
|
const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
|
|
9248
|
-
const queryFile =
|
|
9545
|
+
const queryFile = join36(tmpdir5(), `gh-mutation-${Date.now()}.graphql`);
|
|
9249
9546
|
writeFileSync23(queryFile, mutation);
|
|
9250
9547
|
try {
|
|
9251
9548
|
execSync28(
|
|
@@ -9327,18 +9624,18 @@ function fixed(commentId, sha) {
|
|
|
9327
9624
|
}
|
|
9328
9625
|
|
|
9329
9626
|
// src/commands/prs/listComments/index.ts
|
|
9330
|
-
import { existsSync as
|
|
9331
|
-
import { join as
|
|
9627
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync10, writeFileSync as writeFileSync25 } from "fs";
|
|
9628
|
+
import { join as join38 } from "path";
|
|
9332
9629
|
import { stringify } from "yaml";
|
|
9333
9630
|
|
|
9334
9631
|
// src/commands/prs/fetchThreadIds.ts
|
|
9335
9632
|
import { execSync as execSync30 } from "child_process";
|
|
9336
9633
|
import { unlinkSync as unlinkSync9, writeFileSync as writeFileSync24 } from "fs";
|
|
9337
9634
|
import { tmpdir as tmpdir6 } from "os";
|
|
9338
|
-
import { join as
|
|
9635
|
+
import { join as join37 } from "path";
|
|
9339
9636
|
var THREAD_QUERY = `query($owner: String!, $repo: String!, $prNumber: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $prNumber) { reviewThreads(first: 100) { nodes { id isResolved comments(first: 100) { nodes { databaseId } } } } } } }`;
|
|
9340
9637
|
function fetchThreadIds(org, repo, prNumber) {
|
|
9341
|
-
const queryFile =
|
|
9638
|
+
const queryFile = join37(tmpdir6(), `gh-query-${Date.now()}.graphql`);
|
|
9342
9639
|
writeFileSync24(queryFile, THREAD_QUERY);
|
|
9343
9640
|
try {
|
|
9344
9641
|
const result = execSync30(
|
|
@@ -9425,7 +9722,7 @@ function formatForHuman(comment3) {
|
|
|
9425
9722
|
""
|
|
9426
9723
|
].join("\n");
|
|
9427
9724
|
}
|
|
9428
|
-
function
|
|
9725
|
+
function summarise2(comments2) {
|
|
9429
9726
|
const lineCount = comments2.filter((c) => c.type === "line").length;
|
|
9430
9727
|
const reviewCount = comments2.filter((c) => c.type === "review").length;
|
|
9431
9728
|
const parts = [];
|
|
@@ -9444,7 +9741,7 @@ function printComments2(result) {
|
|
|
9444
9741
|
console.log(formatForHuman(comment3));
|
|
9445
9742
|
}
|
|
9446
9743
|
}
|
|
9447
|
-
console.log(
|
|
9744
|
+
console.log(summarise2(comments2));
|
|
9448
9745
|
if (cachePath) {
|
|
9449
9746
|
console.log(`Saved to ${cachePath}`);
|
|
9450
9747
|
}
|
|
@@ -9452,16 +9749,16 @@ function printComments2(result) {
|
|
|
9452
9749
|
|
|
9453
9750
|
// src/commands/prs/listComments/index.ts
|
|
9454
9751
|
function writeCommentsCache(prNumber, comments2) {
|
|
9455
|
-
const assistDir =
|
|
9456
|
-
if (!
|
|
9457
|
-
|
|
9752
|
+
const assistDir = join38(process.cwd(), ".assist");
|
|
9753
|
+
if (!existsSync34(assistDir)) {
|
|
9754
|
+
mkdirSync10(assistDir, { recursive: true });
|
|
9458
9755
|
}
|
|
9459
9756
|
const cacheData = {
|
|
9460
9757
|
prNumber,
|
|
9461
9758
|
fetchedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9462
9759
|
comments: comments2
|
|
9463
9760
|
};
|
|
9464
|
-
const cachePath =
|
|
9761
|
+
const cachePath = join38(assistDir, `pr-${prNumber}-comments.yaml`);
|
|
9465
9762
|
writeFileSync25(cachePath, stringify(cacheData));
|
|
9466
9763
|
}
|
|
9467
9764
|
function handleKnownErrors(error) {
|
|
@@ -9494,7 +9791,7 @@ async function listComments() {
|
|
|
9494
9791
|
];
|
|
9495
9792
|
updateCache(prNumber, allComments);
|
|
9496
9793
|
const hasLineComments = allComments.some((c) => c.type === "line");
|
|
9497
|
-
const cachePath = hasLineComments ?
|
|
9794
|
+
const cachePath = hasLineComments ? join38(process.cwd(), ".assist", `pr-${prNumber}-comments.yaml`) : null;
|
|
9498
9795
|
return { comments: allComments, cachePath };
|
|
9499
9796
|
} catch (error) {
|
|
9500
9797
|
const handled = handleKnownErrors(error);
|
|
@@ -10178,17 +10475,17 @@ Refactor check failed:
|
|
|
10178
10475
|
|
|
10179
10476
|
// src/commands/refactor/check/getViolations/index.ts
|
|
10180
10477
|
import { execSync as execSync36 } from "child_process";
|
|
10181
|
-
import
|
|
10478
|
+
import fs19 from "fs";
|
|
10182
10479
|
import { minimatch as minimatch4 } from "minimatch";
|
|
10183
10480
|
|
|
10184
10481
|
// src/commands/refactor/check/getViolations/getIgnoredFiles.ts
|
|
10185
|
-
import
|
|
10482
|
+
import fs18 from "fs";
|
|
10186
10483
|
var REFACTOR_YML_PATH = "refactor.yml";
|
|
10187
10484
|
function parseRefactorYml() {
|
|
10188
|
-
if (!
|
|
10485
|
+
if (!fs18.existsSync(REFACTOR_YML_PATH)) {
|
|
10189
10486
|
return [];
|
|
10190
10487
|
}
|
|
10191
|
-
const content =
|
|
10488
|
+
const content = fs18.readFileSync(REFACTOR_YML_PATH, "utf-8");
|
|
10192
10489
|
const entries = [];
|
|
10193
10490
|
const lines = content.split("\n");
|
|
10194
10491
|
let currentEntry = {};
|
|
@@ -10218,7 +10515,7 @@ function getIgnoredFiles() {
|
|
|
10218
10515
|
|
|
10219
10516
|
// src/commands/refactor/check/getViolations/index.ts
|
|
10220
10517
|
function countLines(filePath) {
|
|
10221
|
-
const content =
|
|
10518
|
+
const content = fs19.readFileSync(filePath, "utf-8");
|
|
10222
10519
|
return content.split("\n").length;
|
|
10223
10520
|
}
|
|
10224
10521
|
function getGitFiles(options2) {
|
|
@@ -10283,13 +10580,13 @@ function runScript(script, cwd) {
|
|
|
10283
10580
|
});
|
|
10284
10581
|
});
|
|
10285
10582
|
}
|
|
10286
|
-
function logFailures(
|
|
10287
|
-
for (const f of
|
|
10583
|
+
function logFailures(failed2) {
|
|
10584
|
+
for (const f of failed2) {
|
|
10288
10585
|
console.error(f.output);
|
|
10289
10586
|
}
|
|
10290
10587
|
console.error(`
|
|
10291
|
-
${
|
|
10292
|
-
for (const f of
|
|
10588
|
+
${failed2.length} verify script(s) failed:`);
|
|
10589
|
+
for (const f of failed2) {
|
|
10293
10590
|
console.error(` - ${f.script} (exit code ${f.code})`);
|
|
10294
10591
|
}
|
|
10295
10592
|
}
|
|
@@ -10300,9 +10597,9 @@ async function runVerifyQuietly() {
|
|
|
10300
10597
|
const results = await Promise.all(
|
|
10301
10598
|
result.verifyScripts.map((script) => runScript(script, packageDir))
|
|
10302
10599
|
);
|
|
10303
|
-
const
|
|
10304
|
-
if (
|
|
10305
|
-
logFailures(
|
|
10600
|
+
const failed2 = results.filter((r) => r.code !== 0);
|
|
10601
|
+
if (failed2.length > 0) {
|
|
10602
|
+
logFailures(failed2);
|
|
10306
10603
|
return false;
|
|
10307
10604
|
}
|
|
10308
10605
|
return true;
|
|
@@ -10924,12 +11221,12 @@ import chalk121 from "chalk";
|
|
|
10924
11221
|
import { Project as Project3 } from "ts-morph";
|
|
10925
11222
|
|
|
10926
11223
|
// src/commands/refactor/extract/findTsConfig.ts
|
|
10927
|
-
import
|
|
11224
|
+
import fs20 from "fs";
|
|
10928
11225
|
import path34 from "path";
|
|
10929
11226
|
import { Project as Project2 } from "ts-morph";
|
|
10930
11227
|
function findTsConfig(sourcePath) {
|
|
10931
11228
|
const rootConfig = path34.resolve("tsconfig.json");
|
|
10932
|
-
if (!
|
|
11229
|
+
if (!fs20.existsSync(rootConfig)) return rootConfig;
|
|
10933
11230
|
const tried = /* @__PURE__ */ new Set();
|
|
10934
11231
|
const candidates = [rootConfig, ...readReferences(rootConfig)];
|
|
10935
11232
|
for (const candidate of candidates) {
|
|
@@ -10937,7 +11234,7 @@ function findTsConfig(sourcePath) {
|
|
|
10937
11234
|
tried.add(candidate);
|
|
10938
11235
|
if (projectIncludes(candidate, sourcePath)) return candidate;
|
|
10939
11236
|
}
|
|
10940
|
-
const siblings =
|
|
11237
|
+
const siblings = fs20.readdirSync(path34.dirname(rootConfig)).filter((f) => /^tsconfig.*\.json$/.test(f)).map((f) => path34.resolve(path34.dirname(rootConfig), f));
|
|
10941
11238
|
for (const sibling of siblings) {
|
|
10942
11239
|
if (tried.has(sibling)) continue;
|
|
10943
11240
|
tried.add(sibling);
|
|
@@ -10946,8 +11243,8 @@ function findTsConfig(sourcePath) {
|
|
|
10946
11243
|
return rootConfig;
|
|
10947
11244
|
}
|
|
10948
11245
|
function readReferences(configPath) {
|
|
10949
|
-
if (!
|
|
10950
|
-
const raw =
|
|
11246
|
+
if (!fs20.existsSync(configPath)) return [];
|
|
11247
|
+
const raw = fs20.readFileSync(configPath, "utf-8");
|
|
10951
11248
|
const stripped = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
10952
11249
|
let parsed;
|
|
10953
11250
|
try {
|
|
@@ -10959,8 +11256,8 @@ function readReferences(configPath) {
|
|
|
10959
11256
|
const cwd = path34.dirname(configPath);
|
|
10960
11257
|
return parsed.references.map((ref) => {
|
|
10961
11258
|
const refPath = path34.resolve(cwd, ref.path);
|
|
10962
|
-
return
|
|
10963
|
-
}).filter((p) =>
|
|
11259
|
+
return fs20.statSync(refPath, { throwIfNoEntry: false })?.isDirectory() ? path34.join(refPath, "tsconfig.json") : refPath;
|
|
11260
|
+
}).filter((p) => fs20.existsSync(p));
|
|
10964
11261
|
}
|
|
10965
11262
|
function projectIncludes(configPath, sourcePath) {
|
|
10966
11263
|
try {
|
|
@@ -11010,25 +11307,25 @@ async function extract(file, functionName, destination, options2 = {}) {
|
|
|
11010
11307
|
}
|
|
11011
11308
|
|
|
11012
11309
|
// src/commands/refactor/ignore.ts
|
|
11013
|
-
import
|
|
11310
|
+
import fs21 from "fs";
|
|
11014
11311
|
import chalk123 from "chalk";
|
|
11015
11312
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
11016
11313
|
function ignore(file) {
|
|
11017
|
-
if (!
|
|
11314
|
+
if (!fs21.existsSync(file)) {
|
|
11018
11315
|
console.error(chalk123.red(`Error: File does not exist: ${file}`));
|
|
11019
11316
|
process.exit(1);
|
|
11020
11317
|
}
|
|
11021
|
-
const content =
|
|
11318
|
+
const content = fs21.readFileSync(file, "utf-8");
|
|
11022
11319
|
const lineCount = content.split("\n").length;
|
|
11023
11320
|
const maxLines = lineCount + 10;
|
|
11024
11321
|
const entry = `- file: ${file}
|
|
11025
11322
|
maxLines: ${maxLines}
|
|
11026
11323
|
`;
|
|
11027
|
-
if (
|
|
11028
|
-
const existing =
|
|
11029
|
-
|
|
11324
|
+
if (fs21.existsSync(REFACTOR_YML_PATH2)) {
|
|
11325
|
+
const existing = fs21.readFileSync(REFACTOR_YML_PATH2, "utf-8");
|
|
11326
|
+
fs21.writeFileSync(REFACTOR_YML_PATH2, existing + entry);
|
|
11030
11327
|
} else {
|
|
11031
|
-
|
|
11328
|
+
fs21.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
11032
11329
|
}
|
|
11033
11330
|
console.log(
|
|
11034
11331
|
chalk123.green(
|
|
@@ -11276,7 +11573,7 @@ function clusterFiles(graph) {
|
|
|
11276
11573
|
import path42 from "path";
|
|
11277
11574
|
|
|
11278
11575
|
// src/commands/refactor/restructure/computeRewrites/applyRewrites.ts
|
|
11279
|
-
import
|
|
11576
|
+
import fs22 from "fs";
|
|
11280
11577
|
function getOrCreateList(map, key) {
|
|
11281
11578
|
const list4 = map.get(key) ?? [];
|
|
11282
11579
|
if (!map.has(key)) map.set(key, list4);
|
|
@@ -11295,7 +11592,7 @@ function rewriteSpecifier(content, oldSpecifier, newSpecifier) {
|
|
|
11295
11592
|
return content.replace(pattern2, `$1${newSpecifier}$2`);
|
|
11296
11593
|
}
|
|
11297
11594
|
function applyFileRewrites(file, fileRewrites) {
|
|
11298
|
-
let content =
|
|
11595
|
+
let content = fs22.readFileSync(file, "utf-8");
|
|
11299
11596
|
for (const { oldSpecifier, newSpecifier } of fileRewrites) {
|
|
11300
11597
|
content = rewriteSpecifier(content, oldSpecifier, newSpecifier);
|
|
11301
11598
|
}
|
|
@@ -11423,27 +11720,27 @@ Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports r
|
|
|
11423
11720
|
}
|
|
11424
11721
|
|
|
11425
11722
|
// src/commands/refactor/restructure/executePlan.ts
|
|
11426
|
-
import
|
|
11723
|
+
import fs23 from "fs";
|
|
11427
11724
|
import path44 from "path";
|
|
11428
11725
|
import chalk127 from "chalk";
|
|
11429
11726
|
function executePlan(plan2) {
|
|
11430
11727
|
const updatedContents = applyRewrites(plan2.rewrites);
|
|
11431
11728
|
for (const [file, content] of updatedContents) {
|
|
11432
|
-
|
|
11729
|
+
fs23.writeFileSync(file, content, "utf-8");
|
|
11433
11730
|
console.log(
|
|
11434
11731
|
chalk127.cyan(` Rewrote imports in ${path44.relative(process.cwd(), file)}`)
|
|
11435
11732
|
);
|
|
11436
11733
|
}
|
|
11437
11734
|
for (const dir of plan2.newDirectories) {
|
|
11438
|
-
|
|
11735
|
+
fs23.mkdirSync(dir, { recursive: true });
|
|
11439
11736
|
console.log(chalk127.green(` Created ${path44.relative(process.cwd(), dir)}/`));
|
|
11440
11737
|
}
|
|
11441
11738
|
for (const move of plan2.moves) {
|
|
11442
11739
|
const targetDir = path44.dirname(move.to);
|
|
11443
|
-
if (!
|
|
11444
|
-
|
|
11740
|
+
if (!fs23.existsSync(targetDir)) {
|
|
11741
|
+
fs23.mkdirSync(targetDir, { recursive: true });
|
|
11445
11742
|
}
|
|
11446
|
-
|
|
11743
|
+
fs23.renameSync(move.from, move.to);
|
|
11447
11744
|
console.log(
|
|
11448
11745
|
chalk127.white(
|
|
11449
11746
|
` Moved ${path44.relative(process.cwd(), move.from)} \u2192 ${path44.relative(process.cwd(), move.to)}`
|
|
@@ -11455,10 +11752,10 @@ function executePlan(plan2) {
|
|
|
11455
11752
|
function removeEmptyDirectories(dirs) {
|
|
11456
11753
|
const unique = [...new Set(dirs)];
|
|
11457
11754
|
for (const dir of unique) {
|
|
11458
|
-
if (!
|
|
11459
|
-
const entries =
|
|
11755
|
+
if (!fs23.existsSync(dir)) continue;
|
|
11756
|
+
const entries = fs23.readdirSync(dir);
|
|
11460
11757
|
if (entries.length === 0) {
|
|
11461
|
-
|
|
11758
|
+
fs23.rmdirSync(dir);
|
|
11462
11759
|
console.log(
|
|
11463
11760
|
chalk127.dim(
|
|
11464
11761
|
` Removed empty directory ${path44.relative(process.cwd(), dir)}`
|
|
@@ -11472,18 +11769,18 @@ function removeEmptyDirectories(dirs) {
|
|
|
11472
11769
|
import path46 from "path";
|
|
11473
11770
|
|
|
11474
11771
|
// src/commands/refactor/restructure/planFileMoves/shared.ts
|
|
11475
|
-
import
|
|
11772
|
+
import fs24 from "fs";
|
|
11476
11773
|
function emptyResult() {
|
|
11477
11774
|
return { moves: [], directories: [], warnings: [] };
|
|
11478
11775
|
}
|
|
11479
11776
|
function checkDirConflict(result, label2, dir) {
|
|
11480
|
-
if (!
|
|
11777
|
+
if (!fs24.existsSync(dir)) return false;
|
|
11481
11778
|
result.warnings.push(`Skipping ${label2}: directory ${dir} already exists`);
|
|
11482
11779
|
return true;
|
|
11483
11780
|
}
|
|
11484
11781
|
|
|
11485
11782
|
// src/commands/refactor/restructure/planFileMoves/planDirectoryMoves.ts
|
|
11486
|
-
import
|
|
11783
|
+
import fs25 from "fs";
|
|
11487
11784
|
import path45 from "path";
|
|
11488
11785
|
function collectEntry(results, dir, entry) {
|
|
11489
11786
|
const full = path45.join(dir, entry.name);
|
|
@@ -11491,9 +11788,9 @@ function collectEntry(results, dir, entry) {
|
|
|
11491
11788
|
results.push(...items);
|
|
11492
11789
|
}
|
|
11493
11790
|
function listFilesRecursive(dir) {
|
|
11494
|
-
if (!
|
|
11791
|
+
if (!fs25.existsSync(dir)) return [];
|
|
11495
11792
|
const results = [];
|
|
11496
|
-
for (const entry of
|
|
11793
|
+
for (const entry of fs25.readdirSync(dir, { withFileTypes: true })) {
|
|
11497
11794
|
collectEntry(results, dir, entry);
|
|
11498
11795
|
}
|
|
11499
11796
|
return results;
|
|
@@ -11744,15 +12041,15 @@ ${context.diff.trimEnd()}
|
|
|
11744
12041
|
}
|
|
11745
12042
|
|
|
11746
12043
|
// src/commands/review/buildReviewPaths.ts
|
|
11747
|
-
import { join as
|
|
12044
|
+
import { join as join39 } from "path";
|
|
11748
12045
|
function buildReviewPaths(repoRoot, key) {
|
|
11749
|
-
const reviewDir =
|
|
12046
|
+
const reviewDir = join39(repoRoot, ".assist", "reviews", key);
|
|
11750
12047
|
return {
|
|
11751
12048
|
reviewDir,
|
|
11752
|
-
requestPath:
|
|
11753
|
-
claudePath:
|
|
11754
|
-
codexPath:
|
|
11755
|
-
synthesisPath:
|
|
12049
|
+
requestPath: join39(reviewDir, "request.md"),
|
|
12050
|
+
claudePath: join39(reviewDir, "claude.md"),
|
|
12051
|
+
codexPath: join39(reviewDir, "codex.md"),
|
|
12052
|
+
synthesisPath: join39(reviewDir, "synthesis.md")
|
|
11756
12053
|
};
|
|
11757
12054
|
}
|
|
11758
12055
|
|
|
@@ -11870,7 +12167,7 @@ function gatherContext() {
|
|
|
11870
12167
|
}
|
|
11871
12168
|
|
|
11872
12169
|
// src/commands/review/postReviewToPr.ts
|
|
11873
|
-
import { readFileSync as
|
|
12170
|
+
import { readFileSync as readFileSync31 } from "fs";
|
|
11874
12171
|
|
|
11875
12172
|
// src/commands/review/parseFindings.ts
|
|
11876
12173
|
var SEVERITIES = ["blocker", "major", "minor", "nit"];
|
|
@@ -12017,21 +12314,21 @@ function buildCommentBody(finding) {
|
|
|
12017
12314
|
}
|
|
12018
12315
|
function postFindings(findings) {
|
|
12019
12316
|
let posted = 0;
|
|
12020
|
-
let
|
|
12317
|
+
let failed2 = 0;
|
|
12021
12318
|
for (const finding of findings) {
|
|
12022
12319
|
const body = buildCommentBody(finding);
|
|
12023
12320
|
try {
|
|
12024
12321
|
comment2(finding.file, finding.line, body, finding.startLine);
|
|
12025
12322
|
posted++;
|
|
12026
12323
|
} catch (error) {
|
|
12027
|
-
|
|
12324
|
+
failed2++;
|
|
12028
12325
|
const message = error instanceof Error ? error.message : String(error);
|
|
12029
12326
|
console.error(
|
|
12030
12327
|
`Failed to post comment on ${finding.file}:${finding.line}: ${message}`
|
|
12031
12328
|
);
|
|
12032
12329
|
}
|
|
12033
12330
|
}
|
|
12034
|
-
return { posted, failed };
|
|
12331
|
+
return { posted, failed: failed2 };
|
|
12035
12332
|
}
|
|
12036
12333
|
|
|
12037
12334
|
// src/commands/review/submitPendingReview.ts
|
|
@@ -12100,7 +12397,7 @@ async function postReviewToPr(synthesisPath, options2) {
|
|
|
12100
12397
|
console.log("No PR found for current branch; nothing posted.");
|
|
12101
12398
|
return;
|
|
12102
12399
|
}
|
|
12103
|
-
const markdown =
|
|
12400
|
+
const markdown = readFileSync31(synthesisPath, "utf-8");
|
|
12104
12401
|
const findings = parseFindings(markdown);
|
|
12105
12402
|
if (findings.length === 0) {
|
|
12106
12403
|
console.log("Synthesis contains no findings; nothing to post.");
|
|
@@ -12177,14 +12474,14 @@ async function handlePostSynthesis(synthesisPath, options2) {
|
|
|
12177
12474
|
}
|
|
12178
12475
|
|
|
12179
12476
|
// src/commands/review/prepareReviewDir.ts
|
|
12180
|
-
import { existsSync as
|
|
12477
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync11, unlinkSync as unlinkSync10, writeFileSync as writeFileSync26 } from "fs";
|
|
12181
12478
|
function clearReviewFiles(paths) {
|
|
12182
12479
|
for (const path52 of [paths.claudePath, paths.codexPath, paths.synthesisPath]) {
|
|
12183
|
-
if (
|
|
12480
|
+
if (existsSync35(path52)) unlinkSync10(path52);
|
|
12184
12481
|
}
|
|
12185
12482
|
}
|
|
12186
12483
|
function prepareReviewDir(paths, requestBody, force) {
|
|
12187
|
-
|
|
12484
|
+
mkdirSync11(paths.reviewDir, { recursive: true });
|
|
12188
12485
|
if (force) clearReviewFiles(paths);
|
|
12189
12486
|
writeFileSync26(paths.requestPath, requestBody);
|
|
12190
12487
|
}
|
|
@@ -12225,11 +12522,11 @@ async function runApplySession(synthesisPath) {
|
|
|
12225
12522
|
}
|
|
12226
12523
|
|
|
12227
12524
|
// src/commands/review/cachedReviewerResult.ts
|
|
12228
|
-
import { statSync as
|
|
12525
|
+
import { statSync as statSync5 } from "fs";
|
|
12229
12526
|
function cachedReviewerResult(name, outputPath) {
|
|
12230
12527
|
let size;
|
|
12231
12528
|
try {
|
|
12232
|
-
size =
|
|
12529
|
+
size = statSync5(outputPath).size;
|
|
12233
12530
|
} catch {
|
|
12234
12531
|
return null;
|
|
12235
12532
|
}
|
|
@@ -12386,8 +12683,63 @@ function skippedCodexResult(outputPath) {
|
|
|
12386
12683
|
return { name: "codex", outputPath, exitCode: 0, stderr: "" };
|
|
12387
12684
|
}
|
|
12388
12685
|
|
|
12686
|
+
// src/commands/review/formatReviewerFailure.ts
|
|
12687
|
+
var FAST_FAIL_MS = 1e3;
|
|
12688
|
+
var STDOUT_TAIL_LINES = 20;
|
|
12689
|
+
function indent(text) {
|
|
12690
|
+
return text.split(/\r?\n/).map((line) => ` ${line}`);
|
|
12691
|
+
}
|
|
12692
|
+
function tailLines(text, maxLines) {
|
|
12693
|
+
const lines = text.split(/\r?\n/);
|
|
12694
|
+
return lines.length <= maxLines ? text : lines.slice(-maxLines).join("\n");
|
|
12695
|
+
}
|
|
12696
|
+
function isFastFail(input) {
|
|
12697
|
+
return input.exitCode !== 0 && input.elapsedMs !== void 0 && input.elapsedMs < FAST_FAIL_MS;
|
|
12698
|
+
}
|
|
12699
|
+
function fastFailHint(command) {
|
|
12700
|
+
return [
|
|
12701
|
+
`${command} exited almost immediately \u2014 it likely failed to start.`,
|
|
12702
|
+
`Common causes: not installed on PATH, not authenticated, or misconfigured.`,
|
|
12703
|
+
`Try \`${command} --version\` to confirm install, then \`${command} login\` if authentication may be missing.`
|
|
12704
|
+
];
|
|
12705
|
+
}
|
|
12706
|
+
function outputDetail(input) {
|
|
12707
|
+
const stderr = input.stderr.trim();
|
|
12708
|
+
if (stderr) return ["stderr:", ...indent(stderr)];
|
|
12709
|
+
const stdout = (input.stdout ?? "").trim();
|
|
12710
|
+
if (stdout) {
|
|
12711
|
+
return [
|
|
12712
|
+
`stdout (no stderr was captured \u2014 showing last ${STDOUT_TAIL_LINES} lines):`,
|
|
12713
|
+
...indent(tailLines(stdout, STDOUT_TAIL_LINES))
|
|
12714
|
+
];
|
|
12715
|
+
}
|
|
12716
|
+
return [
|
|
12717
|
+
"No stderr or stdout was captured. Check the review folder for partial output, or rerun with --verbose for full streaming output."
|
|
12718
|
+
];
|
|
12719
|
+
}
|
|
12720
|
+
function formatReviewerFailure(input) {
|
|
12721
|
+
const command = input.command ?? input.name;
|
|
12722
|
+
const seconds = Math.round((input.elapsedMs ?? 0) / 1e3);
|
|
12723
|
+
const headerLine = `${command} CLI exited with code ${input.exitCode} after ${seconds}s`;
|
|
12724
|
+
const detailLines = [];
|
|
12725
|
+
if (isFastFail(input)) detailLines.push(...fastFailHint(command));
|
|
12726
|
+
detailLines.push(...outputDetail(input));
|
|
12727
|
+
return { headerLine, detailLines };
|
|
12728
|
+
}
|
|
12729
|
+
function printReviewerFailure(input) {
|
|
12730
|
+
if (input.exitCode === 0) return;
|
|
12731
|
+
const diagnostic = formatReviewerFailure(input);
|
|
12732
|
+
console.error(`[${input.name}] ${diagnostic.headerLine}`);
|
|
12733
|
+
for (const line of diagnostic.detailLines) console.error(line);
|
|
12734
|
+
}
|
|
12735
|
+
|
|
12736
|
+
// src/commands/review/printReviewerFailures.ts
|
|
12737
|
+
function printReviewerFailures(results) {
|
|
12738
|
+
for (const r of results) printReviewerFailure(r);
|
|
12739
|
+
}
|
|
12740
|
+
|
|
12389
12741
|
// src/commands/review/runAndSynthesise.ts
|
|
12390
|
-
import { existsSync as
|
|
12742
|
+
import { existsSync as existsSync37, unlinkSync as unlinkSync12 } from "fs";
|
|
12391
12743
|
|
|
12392
12744
|
// src/commands/review/buildReviewerStdin.ts
|
|
12393
12745
|
var REVIEW_PROMPT = `You are acting as a reviewer for a proposed code change made by another engineer. The full review request \u2014 branch, base, changed files, and unified diff \u2014 is in request.md in the current working directory.
|
|
@@ -12450,26 +12802,26 @@ The review request is at: ${requestPath}
|
|
|
12450
12802
|
`;
|
|
12451
12803
|
}
|
|
12452
12804
|
|
|
12453
|
-
// src/commands/review/printReviewerFailures.ts
|
|
12454
|
-
function printReviewerFailures(results) {
|
|
12455
|
-
for (const r of results) {
|
|
12456
|
-
if (r.exitCode === 0) continue;
|
|
12457
|
-
console.error(`[${r.name}] exited with code ${r.exitCode}`);
|
|
12458
|
-
if (r.stderr) console.error(r.stderr.trim());
|
|
12459
|
-
}
|
|
12460
|
-
}
|
|
12461
|
-
|
|
12462
12805
|
// src/commands/review/runClaudeReviewer.ts
|
|
12463
12806
|
import { writeFileSync as writeFileSync27 } from "fs";
|
|
12464
12807
|
|
|
12465
12808
|
// src/commands/review/finaliseReviewerSpinner.ts
|
|
12466
|
-
|
|
12809
|
+
var SUMMARY_MAX_LEN = 80;
|
|
12810
|
+
function summariseStderr(stderr) {
|
|
12811
|
+
const firstLine = stderr.split(/\r?\n/).find((l) => l.trim().length > 0);
|
|
12812
|
+
if (!firstLine) return "";
|
|
12813
|
+
const trimmed = firstLine.trim();
|
|
12814
|
+
return trimmed.length > SUMMARY_MAX_LEN ? `${trimmed.slice(0, SUMMARY_MAX_LEN - 1)}\u2026` : trimmed;
|
|
12815
|
+
}
|
|
12816
|
+
function finaliseReviewerSpinner(spinner, name, exitCode, elapsedMs, stderr = "") {
|
|
12467
12817
|
const elapsed = Math.round(elapsedMs / 1e3);
|
|
12468
12818
|
if (exitCode === 0) {
|
|
12469
12819
|
spinner.succeed(`${name} \u2014 done in ${elapsed}s`);
|
|
12470
12820
|
return;
|
|
12471
12821
|
}
|
|
12472
|
-
|
|
12822
|
+
const summary = summariseStderr(stderr);
|
|
12823
|
+
const suffix = summary ? `: ${summary}` : "";
|
|
12824
|
+
spinner.fail(`${name} \u2014 failed in ${elapsed}s (exit ${exitCode})${suffix}`);
|
|
12473
12825
|
}
|
|
12474
12826
|
|
|
12475
12827
|
// src/commands/review/finaliseReviewerRun.ts
|
|
@@ -12479,13 +12831,17 @@ function finaliseReviewerRun(spec, spinner, result) {
|
|
|
12479
12831
|
spinner,
|
|
12480
12832
|
spec.name,
|
|
12481
12833
|
result.exitCode,
|
|
12482
|
-
result.elapsedMs
|
|
12834
|
+
result.elapsedMs,
|
|
12835
|
+
result.stderr
|
|
12483
12836
|
);
|
|
12484
12837
|
return {
|
|
12485
12838
|
name: spec.name,
|
|
12839
|
+
command: spec.command,
|
|
12486
12840
|
outputPath: spec.outputPath,
|
|
12487
12841
|
exitCode: result.exitCode,
|
|
12488
|
-
stderr: result.stderr
|
|
12842
|
+
stderr: result.stderr,
|
|
12843
|
+
stdout: result.stdout,
|
|
12844
|
+
elapsedMs: result.elapsedMs
|
|
12489
12845
|
};
|
|
12490
12846
|
}
|
|
12491
12847
|
|
|
@@ -12610,14 +12966,35 @@ function attachStderrCollector(child) {
|
|
|
12610
12966
|
return state;
|
|
12611
12967
|
}
|
|
12612
12968
|
|
|
12969
|
+
// src/commands/review/attachStdoutTail.ts
|
|
12970
|
+
function attachStdoutTail(child, maxBytes = 8192) {
|
|
12971
|
+
const state = { value: "" };
|
|
12972
|
+
child.stdout?.on("data", (chunk) => {
|
|
12973
|
+
state.value += chunk.toString("utf-8");
|
|
12974
|
+
if (state.value.length > maxBytes) {
|
|
12975
|
+
state.value = state.value.slice(state.value.length - maxBytes);
|
|
12976
|
+
}
|
|
12977
|
+
});
|
|
12978
|
+
return state;
|
|
12979
|
+
}
|
|
12980
|
+
|
|
12613
12981
|
// src/commands/review/logChildClose.ts
|
|
12614
|
-
function logChildClose(
|
|
12615
|
-
|
|
12616
|
-
|
|
12982
|
+
function logChildClose(args) {
|
|
12983
|
+
const elapsedSeconds = Math.round(args.elapsedMs / 1e3);
|
|
12984
|
+
if (args.exitCode === 0) {
|
|
12985
|
+
console.log(`[${args.name}] done in ${elapsedSeconds}s`);
|
|
12617
12986
|
return;
|
|
12618
12987
|
}
|
|
12619
|
-
|
|
12620
|
-
|
|
12988
|
+
const diagnostic = formatReviewerFailure({
|
|
12989
|
+
name: args.name,
|
|
12990
|
+
command: args.command,
|
|
12991
|
+
exitCode: args.exitCode,
|
|
12992
|
+
stderr: args.stderr,
|
|
12993
|
+
stdout: args.stdout,
|
|
12994
|
+
elapsedMs: args.elapsedMs
|
|
12995
|
+
});
|
|
12996
|
+
console.error(`[${args.name}] ${diagnostic.headerLine}`);
|
|
12997
|
+
for (const line of diagnostic.detailLines) console.error(line);
|
|
12621
12998
|
}
|
|
12622
12999
|
|
|
12623
13000
|
// src/commands/review/handleChildClose.ts
|
|
@@ -12625,8 +13002,14 @@ function handleChildClose(args) {
|
|
|
12625
13002
|
const elapsedMs = Date.now() - args.startedAt;
|
|
12626
13003
|
const exitCode = args.code ?? 0;
|
|
12627
13004
|
if (!args.quiet) {
|
|
12628
|
-
|
|
12629
|
-
|
|
13005
|
+
logChildClose({
|
|
13006
|
+
name: args.name,
|
|
13007
|
+
command: args.command,
|
|
13008
|
+
exitCode,
|
|
13009
|
+
elapsedMs,
|
|
13010
|
+
stderr: args.stderr,
|
|
13011
|
+
stdout: args.stdout
|
|
13012
|
+
});
|
|
12630
13013
|
}
|
|
12631
13014
|
return { exitCode, elapsedMs };
|
|
12632
13015
|
}
|
|
@@ -12644,6 +13027,7 @@ ${message}` : message;
|
|
|
12644
13027
|
return {
|
|
12645
13028
|
exitCode: 127,
|
|
12646
13029
|
stderr,
|
|
13030
|
+
stdout: ctx.stdout,
|
|
12647
13031
|
elapsedMs: Date.now() - ctx.startedAt
|
|
12648
13032
|
};
|
|
12649
13033
|
}
|
|
@@ -12656,6 +13040,7 @@ function onErrorResult(ctx, err) {
|
|
|
12656
13040
|
command: ctx.command,
|
|
12657
13041
|
name: ctx.name,
|
|
12658
13042
|
stderr: ctx.stderr.value,
|
|
13043
|
+
stdout: ctx.stdout.value,
|
|
12659
13044
|
startedAt: ctx.startedAt,
|
|
12660
13045
|
quiet: ctx.quiet
|
|
12661
13046
|
},
|
|
@@ -12668,10 +13053,12 @@ function onCloseResult(ctx, code) {
|
|
|
12668
13053
|
code,
|
|
12669
13054
|
startedAt: ctx.startedAt,
|
|
12670
13055
|
name: ctx.name,
|
|
13056
|
+
command: ctx.command,
|
|
12671
13057
|
stderr: ctx.stderr.value,
|
|
13058
|
+
stdout: ctx.stdout.value,
|
|
12672
13059
|
quiet: ctx.quiet
|
|
12673
13060
|
});
|
|
12674
|
-
return { ...closed, stderr: ctx.stderr.value };
|
|
13061
|
+
return { ...closed, stderr: ctx.stderr.value, stdout: ctx.stdout.value };
|
|
12675
13062
|
}
|
|
12676
13063
|
function waitForChildExit(ctx) {
|
|
12677
13064
|
return new Promise((resolve15) => {
|
|
@@ -12698,21 +13085,24 @@ function writeStdinSafely(child, payload) {
|
|
|
12698
13085
|
}
|
|
12699
13086
|
function startChild(spec) {
|
|
12700
13087
|
const child = spawn6(spec.command, spec.args, {
|
|
12701
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
13088
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
13089
|
+
shell: process.platform === "win32"
|
|
12702
13090
|
});
|
|
12703
13091
|
const flushPending = attachLineParser(child, spec.onLine);
|
|
12704
13092
|
const stderr = attachStderrCollector(child);
|
|
13093
|
+
const stdout = attachStdoutTail(child);
|
|
12705
13094
|
writeStdinSafely(child, spec.stdin);
|
|
12706
|
-
return { child, flushPending, stderr };
|
|
13095
|
+
return { child, flushPending, stderr, stdout };
|
|
12707
13096
|
}
|
|
12708
13097
|
function runStreamingChild(spec) {
|
|
12709
13098
|
const startedAt = Date.now();
|
|
12710
13099
|
if (!spec.quiet) console.log(`[${spec.name}] starting`);
|
|
12711
|
-
const { child, flushPending, stderr } = startChild(spec);
|
|
13100
|
+
const { child, flushPending, stderr, stdout } = startChild(spec);
|
|
12712
13101
|
return waitForChildExit({
|
|
12713
13102
|
child,
|
|
12714
13103
|
flushPending,
|
|
12715
13104
|
stderr,
|
|
13105
|
+
stdout,
|
|
12716
13106
|
name: spec.name,
|
|
12717
13107
|
command: spec.command,
|
|
12718
13108
|
startedAt,
|
|
@@ -12724,9 +13114,10 @@ function runStreamingChild(spec) {
|
|
|
12724
13114
|
async function runClaudeReviewer(spec) {
|
|
12725
13115
|
let finalText = "";
|
|
12726
13116
|
const { spinner } = spec;
|
|
13117
|
+
const command = "claude";
|
|
12727
13118
|
const result = await runStreamingChild({
|
|
12728
13119
|
name: spec.name,
|
|
12729
|
-
command
|
|
13120
|
+
command,
|
|
12730
13121
|
args: [
|
|
12731
13122
|
"-p",
|
|
12732
13123
|
"--add-dir",
|
|
@@ -12749,7 +13140,7 @@ async function runClaudeReviewer(spec) {
|
|
|
12749
13140
|
});
|
|
12750
13141
|
if (result.exitCode === 0 && finalText)
|
|
12751
13142
|
writeFileSync27(spec.outputPath, finalText);
|
|
12752
|
-
return finaliseReviewerRun(spec, spinner, result);
|
|
13143
|
+
return finaliseReviewerRun({ ...spec, command }, spinner, result);
|
|
12753
13144
|
}
|
|
12754
13145
|
|
|
12755
13146
|
// src/commands/review/resolveClaude.ts
|
|
@@ -12766,7 +13157,7 @@ function resolveClaude(args) {
|
|
|
12766
13157
|
}
|
|
12767
13158
|
|
|
12768
13159
|
// src/commands/review/runCodexReviewer.ts
|
|
12769
|
-
import { existsSync as
|
|
13160
|
+
import { existsSync as existsSync36, unlinkSync as unlinkSync11 } from "fs";
|
|
12770
13161
|
|
|
12771
13162
|
// src/commands/review/parseCodexEvent.ts
|
|
12772
13163
|
function isItemStarted(value) {
|
|
@@ -12807,9 +13198,10 @@ function codexArgs(reviewDir, outputPath) {
|
|
|
12807
13198
|
}
|
|
12808
13199
|
async function runCodexReviewer(spec) {
|
|
12809
13200
|
const { spinner } = spec;
|
|
13201
|
+
const command = "codex";
|
|
12810
13202
|
const result = await runStreamingChild({
|
|
12811
13203
|
name: spec.name,
|
|
12812
|
-
command
|
|
13204
|
+
command,
|
|
12813
13205
|
args: codexArgs(spec.reviewDir, spec.outputPath),
|
|
12814
13206
|
stdin: spec.stdin,
|
|
12815
13207
|
quiet: Boolean(spinner),
|
|
@@ -12819,10 +13211,10 @@ async function runCodexReviewer(spec) {
|
|
|
12819
13211
|
reportReviewerToolUse(spec.name, event, spinner);
|
|
12820
13212
|
}
|
|
12821
13213
|
});
|
|
12822
|
-
if (result.exitCode !== 0 &&
|
|
13214
|
+
if (result.exitCode !== 0 && existsSync36(spec.outputPath)) {
|
|
12823
13215
|
unlinkSync11(spec.outputPath);
|
|
12824
13216
|
}
|
|
12825
|
-
return finaliseReviewerRun(spec, spinner, result);
|
|
13217
|
+
return finaliseReviewerRun({ ...spec, command }, spinner, result);
|
|
12826
13218
|
}
|
|
12827
13219
|
|
|
12828
13220
|
// src/commands/review/resolveCodex.ts
|
|
@@ -12858,13 +13250,12 @@ async function runReviewers(reviewDir, claudePath, codexPath, stdinPrompt, optio
|
|
|
12858
13250
|
multi: options2.multi
|
|
12859
13251
|
});
|
|
12860
13252
|
const results = await Promise.all([claudePromise, codexPromise]);
|
|
12861
|
-
if (options2.multi) printReviewerFailures(results);
|
|
12862
13253
|
const anyFresh = options2.cachedClaude === null || options2.codexPlan.kind !== "cached";
|
|
12863
13254
|
return { results, anyFresh };
|
|
12864
13255
|
}
|
|
12865
13256
|
|
|
12866
13257
|
// src/commands/review/synthesise.ts
|
|
12867
|
-
import { readFileSync as
|
|
13258
|
+
import { readFileSync as readFileSync32 } from "fs";
|
|
12868
13259
|
|
|
12869
13260
|
// src/commands/review/buildSynthesisStdin.ts
|
|
12870
13261
|
var SYNTHESIS_PROMPT = `You are consolidating two independent code reviews of the same change. The original review request is in request.md. The two reviews are in claude.md and codex.md in the current working directory.
|
|
@@ -12920,7 +13311,7 @@ Files:
|
|
|
12920
13311
|
|
|
12921
13312
|
// src/commands/review/synthesise.ts
|
|
12922
13313
|
function printSummary(synthesisPath) {
|
|
12923
|
-
const markdown =
|
|
13314
|
+
const markdown = readFileSync32(synthesisPath, "utf-8");
|
|
12924
13315
|
console.log("");
|
|
12925
13316
|
console.log(buildReviewSummary(markdown));
|
|
12926
13317
|
console.log("");
|
|
@@ -12946,12 +13337,14 @@ async function synthesise(paths, options2) {
|
|
|
12946
13337
|
outputPath: paths.synthesisPath,
|
|
12947
13338
|
spinner
|
|
12948
13339
|
});
|
|
12949
|
-
if (multi) printReviewerFailures([result]);
|
|
12950
13340
|
if (result.exitCode === 0) printSummary(paths.synthesisPath);
|
|
12951
13341
|
return result;
|
|
12952
13342
|
}
|
|
12953
13343
|
|
|
12954
13344
|
// src/commands/review/runAndSynthesise.ts
|
|
13345
|
+
function failed(results) {
|
|
13346
|
+
return results.filter((r) => r.exitCode !== 0);
|
|
13347
|
+
}
|
|
12955
13348
|
async function runAndSynthesise(args) {
|
|
12956
13349
|
const { paths, multi } = args;
|
|
12957
13350
|
const { results, anyFresh } = await runReviewers(
|
|
@@ -12961,17 +13354,17 @@ async function runAndSynthesise(args) {
|
|
|
12961
13354
|
buildReviewerStdin(paths.requestPath),
|
|
12962
13355
|
{ multi, codexPlan: args.codexPlan, cachedClaude: args.cachedClaude }
|
|
12963
13356
|
);
|
|
13357
|
+
const failures = failed(results);
|
|
12964
13358
|
if (results.every((r) => r.exitCode !== 0)) {
|
|
12965
|
-
console.error(
|
|
12966
|
-
|
|
12967
|
-
);
|
|
12968
|
-
return false;
|
|
13359
|
+
console.error("Both reviewers failed; skipping synthesis.");
|
|
13360
|
+
return { ok: false, failures };
|
|
12969
13361
|
}
|
|
12970
|
-
if (anyFresh &&
|
|
13362
|
+
if (anyFresh && existsSync37(paths.synthesisPath)) {
|
|
12971
13363
|
unlinkSync12(paths.synthesisPath);
|
|
12972
13364
|
}
|
|
12973
13365
|
const synthesisResult = await synthesise(paths, { multi });
|
|
12974
|
-
|
|
13366
|
+
if (synthesisResult.exitCode !== 0) failures.push(synthesisResult);
|
|
13367
|
+
return { ok: synthesisResult.exitCode === 0, failures };
|
|
12975
13368
|
}
|
|
12976
13369
|
|
|
12977
13370
|
// src/commands/review/useSpinnerUi.ts
|
|
@@ -12994,19 +13387,25 @@ function finishUi(ui, ok) {
|
|
|
12994
13387
|
if (ok) ui.elapsed.succeed(label2);
|
|
12995
13388
|
else ui.elapsed.fail(label2);
|
|
12996
13389
|
}
|
|
13390
|
+
function reportFailures(failures, usingSpinner) {
|
|
13391
|
+
if (failures.length === 0) return;
|
|
13392
|
+
if (!usingSpinner) return;
|
|
13393
|
+
printReviewerFailures(failures);
|
|
13394
|
+
}
|
|
12997
13395
|
async function runReviewPipeline(paths, options2) {
|
|
12998
13396
|
const cachedClaude = cachedReviewerResult("claude", paths.claudePath);
|
|
12999
13397
|
const codexPlan = await planCodexReviewer(paths.codexPath);
|
|
13000
13398
|
const ui = createUi(useSpinnerUi(options2.verbose));
|
|
13001
13399
|
try {
|
|
13002
|
-
const
|
|
13400
|
+
const outcome = await runAndSynthesise({
|
|
13003
13401
|
paths,
|
|
13004
13402
|
cachedClaude,
|
|
13005
13403
|
codexPlan,
|
|
13006
13404
|
multi: ui.multi
|
|
13007
13405
|
});
|
|
13008
|
-
finishUi(ui, ok);
|
|
13009
|
-
|
|
13406
|
+
finishUi(ui, outcome.ok);
|
|
13407
|
+
reportFailures(outcome.failures, ui.multi !== void 0);
|
|
13408
|
+
return outcome.ok;
|
|
13010
13409
|
} catch (err) {
|
|
13011
13410
|
ui.multi?.failRemaining();
|
|
13012
13411
|
throw err;
|
|
@@ -13777,8 +14176,8 @@ function registerSql(program2) {
|
|
|
13777
14176
|
}
|
|
13778
14177
|
|
|
13779
14178
|
// src/commands/transcript/shared.ts
|
|
13780
|
-
import { existsSync as
|
|
13781
|
-
import { basename as basename8, join as
|
|
14179
|
+
import { existsSync as existsSync38, readdirSync as readdirSync7, statSync as statSync6 } from "fs";
|
|
14180
|
+
import { basename as basename8, join as join40, relative as relative2 } from "path";
|
|
13782
14181
|
import * as readline2 from "readline";
|
|
13783
14182
|
var DATE_PREFIX_REGEX = /^\d{4}-\d{2}-\d{2}/;
|
|
13784
14183
|
function getDatePrefix(daysOffset = 0) {
|
|
@@ -13793,11 +14192,11 @@ function isValidDatePrefix(filename) {
|
|
|
13793
14192
|
return DATE_PREFIX_REGEX.test(filename);
|
|
13794
14193
|
}
|
|
13795
14194
|
function collectFiles(dir, extension) {
|
|
13796
|
-
if (!
|
|
14195
|
+
if (!existsSync38(dir)) return [];
|
|
13797
14196
|
const results = [];
|
|
13798
|
-
for (const entry of
|
|
13799
|
-
const fullPath =
|
|
13800
|
-
if (
|
|
14197
|
+
for (const entry of readdirSync7(dir)) {
|
|
14198
|
+
const fullPath = join40(dir, entry);
|
|
14199
|
+
if (statSync6(fullPath).isDirectory()) {
|
|
13801
14200
|
results.push(...collectFiles(fullPath, extension));
|
|
13802
14201
|
} else if (entry.endsWith(extension)) {
|
|
13803
14202
|
results.push(fullPath);
|
|
@@ -13890,14 +14289,14 @@ async function configure() {
|
|
|
13890
14289
|
}
|
|
13891
14290
|
|
|
13892
14291
|
// src/commands/transcript/format/index.ts
|
|
13893
|
-
import { existsSync as
|
|
14292
|
+
import { existsSync as existsSync40 } from "fs";
|
|
13894
14293
|
|
|
13895
14294
|
// src/commands/transcript/format/fixInvalidDatePrefixes/index.ts
|
|
13896
|
-
import { dirname as dirname20, join as
|
|
14295
|
+
import { dirname as dirname20, join as join42 } from "path";
|
|
13897
14296
|
|
|
13898
14297
|
// src/commands/transcript/format/fixInvalidDatePrefixes/promptForDateFix.ts
|
|
13899
|
-
import { renameSync as
|
|
13900
|
-
import { join as
|
|
14298
|
+
import { renameSync as renameSync3 } from "fs";
|
|
14299
|
+
import { join as join41 } from "path";
|
|
13901
14300
|
async function resolveDate(rl, choice) {
|
|
13902
14301
|
if (choice === "1") return getDatePrefix(0);
|
|
13903
14302
|
if (choice === "2") return getDatePrefix(-1);
|
|
@@ -13912,7 +14311,7 @@ async function resolveDate(rl, choice) {
|
|
|
13912
14311
|
}
|
|
13913
14312
|
function renameWithPrefix(vttDir, vttFile, prefix2) {
|
|
13914
14313
|
const newFilename = `${prefix2}.${vttFile}`;
|
|
13915
|
-
|
|
14314
|
+
renameSync3(join41(vttDir, vttFile), join41(vttDir, newFilename));
|
|
13916
14315
|
console.log(`Renamed to: ${newFilename}`);
|
|
13917
14316
|
return newFilename;
|
|
13918
14317
|
}
|
|
@@ -13946,12 +14345,12 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
13946
14345
|
const vttFileDir = dirname20(vttFile.absolutePath);
|
|
13947
14346
|
const newFilename = await promptForDateFix(vttFile.filename, vttFileDir);
|
|
13948
14347
|
if (newFilename) {
|
|
13949
|
-
const newRelativePath =
|
|
14348
|
+
const newRelativePath = join42(
|
|
13950
14349
|
dirname20(vttFile.relativePath),
|
|
13951
14350
|
newFilename
|
|
13952
14351
|
);
|
|
13953
14352
|
vttFiles[i] = {
|
|
13954
|
-
absolutePath:
|
|
14353
|
+
absolutePath: join42(vttFileDir, newFilename),
|
|
13955
14354
|
relativePath: newRelativePath,
|
|
13956
14355
|
filename: newFilename
|
|
13957
14356
|
};
|
|
@@ -13964,8 +14363,8 @@ async function fixInvalidDatePrefixes(vttFiles) {
|
|
|
13964
14363
|
}
|
|
13965
14364
|
|
|
13966
14365
|
// src/commands/transcript/format/processVttFile/index.ts
|
|
13967
|
-
import { existsSync as
|
|
13968
|
-
import { basename as basename9, dirname as dirname21, join as
|
|
14366
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync12, readFileSync as readFileSync33, writeFileSync as writeFileSync28 } from "fs";
|
|
14367
|
+
import { basename as basename9, dirname as dirname21, join as join43 } from "path";
|
|
13969
14368
|
|
|
13970
14369
|
// src/commands/transcript/cleanText.ts
|
|
13971
14370
|
function cleanText(text) {
|
|
@@ -14175,22 +14574,22 @@ function toMdFilename(vttFilename) {
|
|
|
14175
14574
|
return `${basename9(vttFilename, ".vtt").replace(/\s*Transcription\s*/g, " ").trim()}.md`;
|
|
14176
14575
|
}
|
|
14177
14576
|
function resolveOutputDir(relativeDir, transcriptsDir) {
|
|
14178
|
-
return relativeDir === "." ? transcriptsDir :
|
|
14577
|
+
return relativeDir === "." ? transcriptsDir : join43(transcriptsDir, relativeDir);
|
|
14179
14578
|
}
|
|
14180
14579
|
function buildOutputPaths(vttFile, transcriptsDir) {
|
|
14181
14580
|
const mdFile = toMdFilename(vttFile.filename);
|
|
14182
14581
|
const relativeDir = dirname21(vttFile.relativePath);
|
|
14183
14582
|
const outputDir = resolveOutputDir(relativeDir, transcriptsDir);
|
|
14184
|
-
const outputPath =
|
|
14583
|
+
const outputPath = join43(outputDir, mdFile);
|
|
14185
14584
|
return { outputDir, outputPath, mdFile, relativeDir };
|
|
14186
14585
|
}
|
|
14187
14586
|
function logSkipped(relativeDir, mdFile) {
|
|
14188
|
-
console.log(`Skipping (already exists): ${
|
|
14587
|
+
console.log(`Skipping (already exists): ${join43(relativeDir, mdFile)}`);
|
|
14189
14588
|
return "skipped";
|
|
14190
14589
|
}
|
|
14191
14590
|
function ensureDirectory(dir, label2) {
|
|
14192
|
-
if (!
|
|
14193
|
-
|
|
14591
|
+
if (!existsSync39(dir)) {
|
|
14592
|
+
mkdirSync12(dir, { recursive: true });
|
|
14194
14593
|
console.log(`Created ${label2}: ${dir}`);
|
|
14195
14594
|
}
|
|
14196
14595
|
}
|
|
@@ -14212,7 +14611,7 @@ function logReduction(cueCount, messageCount) {
|
|
|
14212
14611
|
}
|
|
14213
14612
|
function readAndParseCues(inputPath) {
|
|
14214
14613
|
console.log(`Reading: ${inputPath}`);
|
|
14215
|
-
return processCues(
|
|
14614
|
+
return processCues(readFileSync33(inputPath, "utf-8"));
|
|
14216
14615
|
}
|
|
14217
14616
|
function writeFormatted(outputPath, content) {
|
|
14218
14617
|
writeFileSync28(outputPath, content, "utf-8");
|
|
@@ -14225,7 +14624,7 @@ function convertVttToMarkdown(inputPath, outputPath) {
|
|
|
14225
14624
|
logReduction(cues.length, chatMessages.length);
|
|
14226
14625
|
}
|
|
14227
14626
|
function tryProcessVtt(vttFile, paths) {
|
|
14228
|
-
if (
|
|
14627
|
+
if (existsSync39(paths.outputPath))
|
|
14229
14628
|
return logSkipped(paths.relativeDir, paths.mdFile);
|
|
14230
14629
|
convertVttToMarkdown(vttFile.absolutePath, paths.outputPath);
|
|
14231
14630
|
return "processed";
|
|
@@ -14251,7 +14650,7 @@ function processAllFiles(vttFiles, transcriptsDir) {
|
|
|
14251
14650
|
logSummary(counts);
|
|
14252
14651
|
}
|
|
14253
14652
|
function requireVttDir(vttDir) {
|
|
14254
|
-
if (!
|
|
14653
|
+
if (!existsSync40(vttDir)) {
|
|
14255
14654
|
console.error(`VTT directory not found: ${vttDir}`);
|
|
14256
14655
|
process.exit(1);
|
|
14257
14656
|
}
|
|
@@ -14283,18 +14682,18 @@ async function format() {
|
|
|
14283
14682
|
}
|
|
14284
14683
|
|
|
14285
14684
|
// src/commands/transcript/summarise/index.ts
|
|
14286
|
-
import { existsSync as
|
|
14287
|
-
import { basename as basename10, dirname as dirname23, join as
|
|
14685
|
+
import { existsSync as existsSync42 } from "fs";
|
|
14686
|
+
import { basename as basename10, dirname as dirname23, join as join45, relative as relative3 } from "path";
|
|
14288
14687
|
|
|
14289
14688
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
14290
14689
|
import {
|
|
14291
|
-
existsSync as
|
|
14292
|
-
mkdirSync as
|
|
14293
|
-
readFileSync as
|
|
14294
|
-
renameSync as
|
|
14690
|
+
existsSync as existsSync41,
|
|
14691
|
+
mkdirSync as mkdirSync13,
|
|
14692
|
+
readFileSync as readFileSync34,
|
|
14693
|
+
renameSync as renameSync4,
|
|
14295
14694
|
rmSync
|
|
14296
14695
|
} from "fs";
|
|
14297
|
-
import { dirname as dirname22, join as
|
|
14696
|
+
import { dirname as dirname22, join as join44 } from "path";
|
|
14298
14697
|
|
|
14299
14698
|
// src/commands/transcript/summarise/processStagedFile/validateStagedContent.ts
|
|
14300
14699
|
import chalk142 from "chalk";
|
|
@@ -14323,9 +14722,9 @@ function validateStagedContent(filename, content) {
|
|
|
14323
14722
|
}
|
|
14324
14723
|
|
|
14325
14724
|
// src/commands/transcript/summarise/processStagedFile/index.ts
|
|
14326
|
-
var STAGING_DIR =
|
|
14725
|
+
var STAGING_DIR = join44(process.cwd(), ".assist", "transcript");
|
|
14327
14726
|
function processStagedFile() {
|
|
14328
|
-
if (!
|
|
14727
|
+
if (!existsSync41(STAGING_DIR)) {
|
|
14329
14728
|
return false;
|
|
14330
14729
|
}
|
|
14331
14730
|
const stagedFiles = findMdFilesRecursive(STAGING_DIR);
|
|
@@ -14334,7 +14733,7 @@ function processStagedFile() {
|
|
|
14334
14733
|
}
|
|
14335
14734
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
14336
14735
|
const stagedFile = stagedFiles[0];
|
|
14337
|
-
const content =
|
|
14736
|
+
const content = readFileSync34(stagedFile.absolutePath, "utf-8");
|
|
14338
14737
|
validateStagedContent(stagedFile.filename, content);
|
|
14339
14738
|
const stagedBaseName = getTranscriptBaseName(stagedFile.filename);
|
|
14340
14739
|
const transcriptFiles = findMdFilesRecursive(transcriptsDir);
|
|
@@ -14347,12 +14746,12 @@ function processStagedFile() {
|
|
|
14347
14746
|
);
|
|
14348
14747
|
process.exit(1);
|
|
14349
14748
|
}
|
|
14350
|
-
const destPath =
|
|
14749
|
+
const destPath = join44(summaryDir, matchingTranscript.relativePath);
|
|
14351
14750
|
const destDir = dirname22(destPath);
|
|
14352
|
-
if (!
|
|
14353
|
-
|
|
14751
|
+
if (!existsSync41(destDir)) {
|
|
14752
|
+
mkdirSync13(destDir, { recursive: true });
|
|
14354
14753
|
}
|
|
14355
|
-
|
|
14754
|
+
renameSync4(stagedFile.absolutePath, destPath);
|
|
14356
14755
|
const remaining = findMdFilesRecursive(STAGING_DIR);
|
|
14357
14756
|
if (remaining.length === 0) {
|
|
14358
14757
|
rmSync(STAGING_DIR, { recursive: true });
|
|
@@ -14363,7 +14762,7 @@ function processStagedFile() {
|
|
|
14363
14762
|
// src/commands/transcript/summarise/index.ts
|
|
14364
14763
|
function buildRelativeKey(relativePath, baseName) {
|
|
14365
14764
|
const relDir = dirname23(relativePath);
|
|
14366
|
-
return relDir === "." ? baseName :
|
|
14765
|
+
return relDir === "." ? baseName : join45(relDir, baseName);
|
|
14367
14766
|
}
|
|
14368
14767
|
function buildSummaryIndex(summaryDir) {
|
|
14369
14768
|
const summaryFiles = findMdFilesRecursive(summaryDir);
|
|
@@ -14373,10 +14772,10 @@ function buildSummaryIndex(summaryDir) {
|
|
|
14373
14772
|
)
|
|
14374
14773
|
);
|
|
14375
14774
|
}
|
|
14376
|
-
function
|
|
14775
|
+
function summarise3() {
|
|
14377
14776
|
processStagedFile();
|
|
14378
14777
|
const { transcriptsDir, summaryDir } = getTranscriptConfig();
|
|
14379
|
-
if (!
|
|
14778
|
+
if (!existsSync42(transcriptsDir)) {
|
|
14380
14779
|
console.log("No transcripts directory found.");
|
|
14381
14780
|
return;
|
|
14382
14781
|
}
|
|
@@ -14397,8 +14796,8 @@ function summarise2() {
|
|
|
14397
14796
|
}
|
|
14398
14797
|
const next3 = missing[0];
|
|
14399
14798
|
const outputFilename = `${getTranscriptBaseName(next3.filename)}.md`;
|
|
14400
|
-
const outputPath =
|
|
14401
|
-
const summaryFileDir =
|
|
14799
|
+
const outputPath = join45(STAGING_DIR, outputFilename);
|
|
14800
|
+
const summaryFileDir = join45(summaryDir, dirname23(next3.relativePath));
|
|
14402
14801
|
const relativeTranscriptPath = encodeURI(
|
|
14403
14802
|
relative3(summaryFileDir, next3.absolutePath).replace(/\\/g, "/")
|
|
14404
14803
|
);
|
|
@@ -14419,7 +14818,7 @@ function registerTranscript(program2) {
|
|
|
14419
14818
|
const transcriptCommand = program2.command("transcript").description("Meeting transcript utilities");
|
|
14420
14819
|
transcriptCommand.command("configure").description("Configure transcript directories").action(configure);
|
|
14421
14820
|
transcriptCommand.command("format").description("Convert VTT files to formatted markdown transcripts").action(format);
|
|
14422
|
-
transcriptCommand.command("summarise").description("List transcripts that do not have summaries").action(
|
|
14821
|
+
transcriptCommand.command("summarise").description("List transcripts that do not have summaries").action(summarise3);
|
|
14423
14822
|
}
|
|
14424
14823
|
|
|
14425
14824
|
// src/commands/registerVerify.ts
|
|
@@ -14447,50 +14846,50 @@ function registerVerify(program2) {
|
|
|
14447
14846
|
|
|
14448
14847
|
// src/commands/voice/devices.ts
|
|
14449
14848
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
14450
|
-
import { join as
|
|
14849
|
+
import { join as join47 } from "path";
|
|
14451
14850
|
|
|
14452
14851
|
// src/commands/voice/shared.ts
|
|
14453
|
-
import { homedir as
|
|
14454
|
-
import { dirname as dirname24, join as
|
|
14852
|
+
import { homedir as homedir10 } from "os";
|
|
14853
|
+
import { dirname as dirname24, join as join46 } from "path";
|
|
14455
14854
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
14456
14855
|
var __dirname6 = dirname24(fileURLToPath6(import.meta.url));
|
|
14457
|
-
var VOICE_DIR =
|
|
14856
|
+
var VOICE_DIR = join46(homedir10(), ".assist", "voice");
|
|
14458
14857
|
var voicePaths = {
|
|
14459
14858
|
dir: VOICE_DIR,
|
|
14460
|
-
pid:
|
|
14461
|
-
log:
|
|
14462
|
-
venv:
|
|
14463
|
-
lock:
|
|
14859
|
+
pid: join46(VOICE_DIR, "voice.pid"),
|
|
14860
|
+
log: join46(VOICE_DIR, "voice.log"),
|
|
14861
|
+
venv: join46(VOICE_DIR, ".venv"),
|
|
14862
|
+
lock: join46(VOICE_DIR, "voice.lock")
|
|
14464
14863
|
};
|
|
14465
14864
|
function getPythonDir() {
|
|
14466
|
-
return
|
|
14865
|
+
return join46(__dirname6, "commands", "voice", "python");
|
|
14467
14866
|
}
|
|
14468
14867
|
function getVenvPython() {
|
|
14469
|
-
return process.platform === "win32" ?
|
|
14868
|
+
return process.platform === "win32" ? join46(voicePaths.venv, "Scripts", "python.exe") : join46(voicePaths.venv, "bin", "python");
|
|
14470
14869
|
}
|
|
14471
14870
|
function getLockDir() {
|
|
14472
14871
|
const config = loadConfig();
|
|
14473
14872
|
return config.voice?.lockDir ?? VOICE_DIR;
|
|
14474
14873
|
}
|
|
14475
14874
|
function getLockFile() {
|
|
14476
|
-
return
|
|
14875
|
+
return join46(getLockDir(), "voice.lock");
|
|
14477
14876
|
}
|
|
14478
14877
|
|
|
14479
14878
|
// src/commands/voice/devices.ts
|
|
14480
14879
|
function devices() {
|
|
14481
|
-
const script =
|
|
14880
|
+
const script = join47(getPythonDir(), "list_devices.py");
|
|
14482
14881
|
spawnSync4(getVenvPython(), [script], { stdio: "inherit" });
|
|
14483
14882
|
}
|
|
14484
14883
|
|
|
14485
14884
|
// src/commands/voice/logs.ts
|
|
14486
|
-
import { existsSync as
|
|
14885
|
+
import { existsSync as existsSync43, readFileSync as readFileSync35 } from "fs";
|
|
14487
14886
|
function logs(options2) {
|
|
14488
|
-
if (!
|
|
14887
|
+
if (!existsSync43(voicePaths.log)) {
|
|
14489
14888
|
console.log("No voice log file found");
|
|
14490
14889
|
return;
|
|
14491
14890
|
}
|
|
14492
14891
|
const count = Number.parseInt(options2.lines ?? "150", 10);
|
|
14493
|
-
const content =
|
|
14892
|
+
const content = readFileSync35(voicePaths.log, "utf-8").trim();
|
|
14494
14893
|
if (!content) {
|
|
14495
14894
|
console.log("Voice log is empty");
|
|
14496
14895
|
return;
|
|
@@ -14512,13 +14911,13 @@ function logs(options2) {
|
|
|
14512
14911
|
|
|
14513
14912
|
// src/commands/voice/setup.ts
|
|
14514
14913
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
14515
|
-
import { mkdirSync as
|
|
14516
|
-
import { join as
|
|
14914
|
+
import { mkdirSync as mkdirSync15 } from "fs";
|
|
14915
|
+
import { join as join49 } from "path";
|
|
14517
14916
|
|
|
14518
14917
|
// src/commands/voice/checkLockFile.ts
|
|
14519
14918
|
import { execSync as execSync41 } from "child_process";
|
|
14520
|
-
import { existsSync as
|
|
14521
|
-
import { join as
|
|
14919
|
+
import { existsSync as existsSync44, mkdirSync as mkdirSync14, readFileSync as readFileSync36, writeFileSync as writeFileSync29 } from "fs";
|
|
14920
|
+
import { join as join48 } from "path";
|
|
14522
14921
|
function isProcessAlive2(pid) {
|
|
14523
14922
|
try {
|
|
14524
14923
|
process.kill(pid, 0);
|
|
@@ -14529,9 +14928,9 @@ function isProcessAlive2(pid) {
|
|
|
14529
14928
|
}
|
|
14530
14929
|
function checkLockFile() {
|
|
14531
14930
|
const lockFile = getLockFile();
|
|
14532
|
-
if (!
|
|
14931
|
+
if (!existsSync44(lockFile)) return;
|
|
14533
14932
|
try {
|
|
14534
|
-
const lock = JSON.parse(
|
|
14933
|
+
const lock = JSON.parse(readFileSync36(lockFile, "utf-8"));
|
|
14535
14934
|
if (lock.pid && isProcessAlive2(lock.pid)) {
|
|
14536
14935
|
console.error(
|
|
14537
14936
|
`Voice daemon already running (PID ${lock.pid}, env: ${lock.env}). Stop it first with: assist voice stop`
|
|
@@ -14542,7 +14941,7 @@ function checkLockFile() {
|
|
|
14542
14941
|
}
|
|
14543
14942
|
}
|
|
14544
14943
|
function bootstrapVenv() {
|
|
14545
|
-
if (
|
|
14944
|
+
if (existsSync44(getVenvPython())) return;
|
|
14546
14945
|
console.log("Setting up Python environment...");
|
|
14547
14946
|
const pythonDir = getPythonDir();
|
|
14548
14947
|
execSync41(
|
|
@@ -14555,7 +14954,7 @@ function bootstrapVenv() {
|
|
|
14555
14954
|
}
|
|
14556
14955
|
function writeLockFile(pid) {
|
|
14557
14956
|
const lockFile = getLockFile();
|
|
14558
|
-
|
|
14957
|
+
mkdirSync14(join48(lockFile, ".."), { recursive: true });
|
|
14559
14958
|
writeFileSync29(
|
|
14560
14959
|
lockFile,
|
|
14561
14960
|
JSON.stringify({
|
|
@@ -14568,10 +14967,10 @@ function writeLockFile(pid) {
|
|
|
14568
14967
|
|
|
14569
14968
|
// src/commands/voice/setup.ts
|
|
14570
14969
|
function setup() {
|
|
14571
|
-
|
|
14970
|
+
mkdirSync15(voicePaths.dir, { recursive: true });
|
|
14572
14971
|
bootstrapVenv();
|
|
14573
14972
|
console.log("\nDownloading models...\n");
|
|
14574
|
-
const script =
|
|
14973
|
+
const script = join49(getPythonDir(), "setup_models.py");
|
|
14575
14974
|
const result = spawnSync5(getVenvPython(), [script], {
|
|
14576
14975
|
stdio: "inherit",
|
|
14577
14976
|
env: { ...process.env, VOICE_LOG_FILE: voicePaths.log }
|
|
@@ -14584,8 +14983,8 @@ function setup() {
|
|
|
14584
14983
|
|
|
14585
14984
|
// src/commands/voice/start.ts
|
|
14586
14985
|
import { spawn as spawn7 } from "child_process";
|
|
14587
|
-
import { mkdirSync as
|
|
14588
|
-
import { join as
|
|
14986
|
+
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync30 } from "fs";
|
|
14987
|
+
import { join as join50 } from "path";
|
|
14589
14988
|
|
|
14590
14989
|
// src/commands/voice/buildDaemonEnv.ts
|
|
14591
14990
|
function buildDaemonEnv(options2) {
|
|
@@ -14618,12 +15017,12 @@ function spawnBackground(python, script, env) {
|
|
|
14618
15017
|
console.log(`Voice daemon started (PID ${pid})`);
|
|
14619
15018
|
}
|
|
14620
15019
|
function start2(options2) {
|
|
14621
|
-
|
|
15020
|
+
mkdirSync16(voicePaths.dir, { recursive: true });
|
|
14622
15021
|
checkLockFile();
|
|
14623
15022
|
bootstrapVenv();
|
|
14624
15023
|
const debug = options2.debug || options2.foreground || process.platform === "win32";
|
|
14625
15024
|
const env = buildDaemonEnv({ debug });
|
|
14626
|
-
const script =
|
|
15025
|
+
const script = join50(getPythonDir(), "voice_daemon.py");
|
|
14627
15026
|
const python = getVenvPython();
|
|
14628
15027
|
if (options2.foreground) {
|
|
14629
15028
|
spawnForeground(python, script, env);
|
|
@@ -14633,7 +15032,7 @@ function start2(options2) {
|
|
|
14633
15032
|
}
|
|
14634
15033
|
|
|
14635
15034
|
// src/commands/voice/status.ts
|
|
14636
|
-
import { existsSync as
|
|
15035
|
+
import { existsSync as existsSync45, readFileSync as readFileSync37 } from "fs";
|
|
14637
15036
|
function isProcessAlive3(pid) {
|
|
14638
15037
|
try {
|
|
14639
15038
|
process.kill(pid, 0);
|
|
@@ -14643,16 +15042,16 @@ function isProcessAlive3(pid) {
|
|
|
14643
15042
|
}
|
|
14644
15043
|
}
|
|
14645
15044
|
function readRecentLogs(count) {
|
|
14646
|
-
if (!
|
|
14647
|
-
const lines =
|
|
15045
|
+
if (!existsSync45(voicePaths.log)) return [];
|
|
15046
|
+
const lines = readFileSync37(voicePaths.log, "utf-8").trim().split("\n");
|
|
14648
15047
|
return lines.slice(-count);
|
|
14649
15048
|
}
|
|
14650
15049
|
function status() {
|
|
14651
|
-
if (!
|
|
15050
|
+
if (!existsSync45(voicePaths.pid)) {
|
|
14652
15051
|
console.log("Voice daemon: not running (no PID file)");
|
|
14653
15052
|
return;
|
|
14654
15053
|
}
|
|
14655
|
-
const pid = Number.parseInt(
|
|
15054
|
+
const pid = Number.parseInt(readFileSync37(voicePaths.pid, "utf-8").trim(), 10);
|
|
14656
15055
|
const alive = isProcessAlive3(pid);
|
|
14657
15056
|
console.log(`Voice daemon: ${alive ? "running" : "dead"} (PID ${pid})`);
|
|
14658
15057
|
const recent = readRecentLogs(5);
|
|
@@ -14671,13 +15070,13 @@ function status() {
|
|
|
14671
15070
|
}
|
|
14672
15071
|
|
|
14673
15072
|
// src/commands/voice/stop.ts
|
|
14674
|
-
import { existsSync as
|
|
15073
|
+
import { existsSync as existsSync46, readFileSync as readFileSync38, unlinkSync as unlinkSync13 } from "fs";
|
|
14675
15074
|
function stop2() {
|
|
14676
|
-
if (!
|
|
15075
|
+
if (!existsSync46(voicePaths.pid)) {
|
|
14677
15076
|
console.log("Voice daemon is not running (no PID file)");
|
|
14678
15077
|
return;
|
|
14679
15078
|
}
|
|
14680
|
-
const pid = Number.parseInt(
|
|
15079
|
+
const pid = Number.parseInt(readFileSync38(voicePaths.pid, "utf-8").trim(), 10);
|
|
14681
15080
|
try {
|
|
14682
15081
|
process.kill(pid, "SIGTERM");
|
|
14683
15082
|
console.log(`Sent SIGTERM to voice daemon (PID ${pid})`);
|
|
@@ -14690,7 +15089,7 @@ function stop2() {
|
|
|
14690
15089
|
}
|
|
14691
15090
|
try {
|
|
14692
15091
|
const lockFile = getLockFile();
|
|
14693
|
-
if (
|
|
15092
|
+
if (existsSync46(lockFile)) unlinkSync13(lockFile);
|
|
14694
15093
|
} catch {
|
|
14695
15094
|
}
|
|
14696
15095
|
console.log("Voice daemon stopped");
|
|
@@ -14911,20 +15310,20 @@ async function auth() {
|
|
|
14911
15310
|
}
|
|
14912
15311
|
|
|
14913
15312
|
// src/commands/roam/postRoamActivity.ts
|
|
14914
|
-
import { execFileSync as
|
|
14915
|
-
import { readdirSync as
|
|
14916
|
-
import { join as
|
|
15313
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
15314
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync39, statSync as statSync7 } from "fs";
|
|
15315
|
+
import { join as join51 } from "path";
|
|
14917
15316
|
function findPortFile(roamDir) {
|
|
14918
15317
|
let entries;
|
|
14919
15318
|
try {
|
|
14920
|
-
entries =
|
|
15319
|
+
entries = readdirSync8(roamDir);
|
|
14921
15320
|
} catch {
|
|
14922
15321
|
return void 0;
|
|
14923
15322
|
}
|
|
14924
15323
|
const candidates = entries.filter((name) => /^roam-local-api(-[^.]+)?\.port$/.test(name)).map((name) => {
|
|
14925
|
-
const path52 =
|
|
15324
|
+
const path52 = join51(roamDir, name);
|
|
14926
15325
|
try {
|
|
14927
|
-
return { path: path52, mtimeMs:
|
|
15326
|
+
return { path: path52, mtimeMs: statSync7(path52).mtimeMs };
|
|
14928
15327
|
} catch {
|
|
14929
15328
|
return void 0;
|
|
14930
15329
|
}
|
|
@@ -14934,17 +15333,17 @@ function findPortFile(roamDir) {
|
|
|
14934
15333
|
function postRoamActivity(app, event) {
|
|
14935
15334
|
const appData = process.env.APPDATA;
|
|
14936
15335
|
if (!appData) return;
|
|
14937
|
-
const portFile = findPortFile(
|
|
15336
|
+
const portFile = findPortFile(join51(appData, "Roam"));
|
|
14938
15337
|
if (!portFile) return;
|
|
14939
15338
|
let port;
|
|
14940
15339
|
try {
|
|
14941
|
-
port =
|
|
15340
|
+
port = readFileSync39(portFile, "utf-8").trim();
|
|
14942
15341
|
} catch {
|
|
14943
15342
|
return;
|
|
14944
15343
|
}
|
|
14945
15344
|
const url = `http://127.0.0.1:${port}/api/v1/activity/${app}/${event}?pid=${app === "codex" ? 99998 : 99999}`;
|
|
14946
15345
|
try {
|
|
14947
|
-
|
|
15346
|
+
execFileSync5("curl", ["-sf", "--max-time", "0.2", "-X", "POST", url], {
|
|
14948
15347
|
stdio: "ignore"
|
|
14949
15348
|
});
|
|
14950
15349
|
} catch {
|
|
@@ -15071,16 +15470,16 @@ function runPreCommands(pre, cwd) {
|
|
|
15071
15470
|
}
|
|
15072
15471
|
|
|
15073
15472
|
// src/commands/run/spawnRunCommand.ts
|
|
15074
|
-
import { execFileSync as
|
|
15075
|
-
import { existsSync as
|
|
15076
|
-
import { dirname as dirname25, join as
|
|
15473
|
+
import { execFileSync as execFileSync6, spawn as spawn8 } from "child_process";
|
|
15474
|
+
import { existsSync as existsSync47 } from "fs";
|
|
15475
|
+
import { dirname as dirname25, join as join52, resolve as resolve11 } from "path";
|
|
15077
15476
|
function resolveCommand2(command) {
|
|
15078
15477
|
if (process.platform !== "win32" || command !== "bash") return command;
|
|
15079
15478
|
try {
|
|
15080
|
-
const gitPath =
|
|
15479
|
+
const gitPath = execFileSync6("where", ["git"], { encoding: "utf8" }).trim().split("\r\n")[0];
|
|
15081
15480
|
const gitRoot = resolve11(dirname25(gitPath), "..");
|
|
15082
|
-
const gitBash =
|
|
15083
|
-
if (
|
|
15481
|
+
const gitBash = join52(gitRoot, "bin", "bash.exe");
|
|
15482
|
+
if (existsSync47(gitBash)) return gitBash;
|
|
15084
15483
|
} catch {
|
|
15085
15484
|
}
|
|
15086
15485
|
return command;
|
|
@@ -15147,8 +15546,8 @@ function run3(name, args) {
|
|
|
15147
15546
|
}
|
|
15148
15547
|
|
|
15149
15548
|
// src/commands/run/add.ts
|
|
15150
|
-
import { mkdirSync as
|
|
15151
|
-
import { join as
|
|
15549
|
+
import { mkdirSync as mkdirSync17, writeFileSync as writeFileSync31 } from "fs";
|
|
15550
|
+
import { join as join53 } from "path";
|
|
15152
15551
|
|
|
15153
15552
|
// src/commands/run/extractOption.ts
|
|
15154
15553
|
function extractOption(args, flag) {
|
|
@@ -15209,15 +15608,15 @@ function saveNewRunConfig(name, command, args, cwd) {
|
|
|
15209
15608
|
saveConfig(config);
|
|
15210
15609
|
}
|
|
15211
15610
|
function createCommandFile(name) {
|
|
15212
|
-
const dir =
|
|
15213
|
-
|
|
15611
|
+
const dir = join53(".claude", "commands");
|
|
15612
|
+
mkdirSync17(dir, { recursive: true });
|
|
15214
15613
|
const content = `---
|
|
15215
15614
|
description: Run ${name}
|
|
15216
15615
|
---
|
|
15217
15616
|
|
|
15218
15617
|
Run \`assist run ${name} $ARGUMENTS 2>&1\`.
|
|
15219
15618
|
`;
|
|
15220
|
-
const filePath =
|
|
15619
|
+
const filePath = join53(dir, `${name}.md`);
|
|
15221
15620
|
writeFileSync31(filePath, content);
|
|
15222
15621
|
console.log(`Created command file: ${filePath}`);
|
|
15223
15622
|
}
|
|
@@ -15273,8 +15672,8 @@ function link2() {
|
|
|
15273
15672
|
}
|
|
15274
15673
|
|
|
15275
15674
|
// src/commands/run/remove.ts
|
|
15276
|
-
import { existsSync as
|
|
15277
|
-
import { join as
|
|
15675
|
+
import { existsSync as existsSync48, unlinkSync as unlinkSync14 } from "fs";
|
|
15676
|
+
import { join as join54 } from "path";
|
|
15278
15677
|
function findRemoveIndex() {
|
|
15279
15678
|
const idx = process.argv.indexOf("remove");
|
|
15280
15679
|
if (idx === -1 || idx + 1 >= process.argv.length) return -1;
|
|
@@ -15289,8 +15688,8 @@ function parseRemoveName() {
|
|
|
15289
15688
|
return process.argv[idx + 1];
|
|
15290
15689
|
}
|
|
15291
15690
|
function deleteCommandFile(name) {
|
|
15292
|
-
const filePath =
|
|
15293
|
-
if (
|
|
15691
|
+
const filePath = join54(".claude", "commands", `${name}.md`);
|
|
15692
|
+
if (existsSync48(filePath)) {
|
|
15294
15693
|
unlinkSync14(filePath);
|
|
15295
15694
|
console.log(`Deleted command file: ${filePath}`);
|
|
15296
15695
|
}
|
|
@@ -15326,9 +15725,9 @@ function registerRun(program2) {
|
|
|
15326
15725
|
|
|
15327
15726
|
// src/commands/screenshot/index.ts
|
|
15328
15727
|
import { execSync as execSync44 } from "child_process";
|
|
15329
|
-
import { existsSync as
|
|
15728
|
+
import { existsSync as existsSync49, mkdirSync as mkdirSync18, unlinkSync as unlinkSync15, writeFileSync as writeFileSync32 } from "fs";
|
|
15330
15729
|
import { tmpdir as tmpdir7 } from "os";
|
|
15331
|
-
import { join as
|
|
15730
|
+
import { join as join55, resolve as resolve13 } from "path";
|
|
15332
15731
|
import chalk144 from "chalk";
|
|
15333
15732
|
|
|
15334
15733
|
// src/commands/screenshot/captureWindowPs1.ts
|
|
@@ -15458,14 +15857,14 @@ Write-Output $OutputPath
|
|
|
15458
15857
|
|
|
15459
15858
|
// src/commands/screenshot/index.ts
|
|
15460
15859
|
function buildOutputPath(outputDir, processName) {
|
|
15461
|
-
if (!
|
|
15462
|
-
|
|
15860
|
+
if (!existsSync49(outputDir)) {
|
|
15861
|
+
mkdirSync18(outputDir, { recursive: true });
|
|
15463
15862
|
}
|
|
15464
15863
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
15465
15864
|
return resolve13(outputDir, `${processName}-${timestamp}.png`);
|
|
15466
15865
|
}
|
|
15467
15866
|
function runPowerShellScript(processName, outputPath) {
|
|
15468
|
-
const scriptPath =
|
|
15867
|
+
const scriptPath = join55(tmpdir7(), `assist-screenshot-${Date.now()}.ps1`);
|
|
15469
15868
|
writeFileSync32(scriptPath, captureWindowPs1, "utf-8");
|
|
15470
15869
|
try {
|
|
15471
15870
|
execSync44(
|
|
@@ -15492,58 +15891,45 @@ function screenshot(processName) {
|
|
|
15492
15891
|
}
|
|
15493
15892
|
|
|
15494
15893
|
// src/commands/sessions/summarise/index.ts
|
|
15495
|
-
import * as
|
|
15894
|
+
import * as fs28 from "fs";
|
|
15496
15895
|
import chalk145 from "chalk";
|
|
15497
15896
|
|
|
15498
15897
|
// src/commands/sessions/summarise/shared.ts
|
|
15499
|
-
import * as
|
|
15898
|
+
import * as fs26 from "fs";
|
|
15500
15899
|
function writeSummary(jsonlPath, summary) {
|
|
15501
|
-
|
|
15900
|
+
fs26.writeFileSync(summaryPathFor(jsonlPath), `${summary.trim()}
|
|
15502
15901
|
`, "utf8");
|
|
15503
15902
|
}
|
|
15504
15903
|
function hasSummary(jsonlPath) {
|
|
15505
|
-
return
|
|
15904
|
+
return fs26.existsSync(summaryPathFor(jsonlPath));
|
|
15506
15905
|
}
|
|
15507
15906
|
function summaryPathFor(jsonlPath) {
|
|
15508
15907
|
return jsonlPath.replace(/\.jsonl$/, ".summary");
|
|
15509
15908
|
}
|
|
15510
15909
|
|
|
15511
15910
|
// src/commands/sessions/summarise/summariseSession.ts
|
|
15512
|
-
import { execFileSync as
|
|
15911
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
15513
15912
|
|
|
15514
15913
|
// src/commands/sessions/summarise/iterateUserMessages.ts
|
|
15515
|
-
import * as
|
|
15914
|
+
import * as fs27 from "fs";
|
|
15516
15915
|
function* iterateUserMessages(filePath, maxBytes = 65536) {
|
|
15517
15916
|
let content;
|
|
15518
15917
|
try {
|
|
15519
|
-
const fd =
|
|
15918
|
+
const fd = fs27.openSync(filePath, "r");
|
|
15520
15919
|
try {
|
|
15521
15920
|
const buf = Buffer.alloc(maxBytes);
|
|
15522
|
-
const bytesRead =
|
|
15921
|
+
const bytesRead = fs27.readSync(fd, buf, 0, buf.length, 0);
|
|
15523
15922
|
content = buf.toString("utf8", 0, bytesRead);
|
|
15524
15923
|
} finally {
|
|
15525
|
-
|
|
15924
|
+
fs27.closeSync(fd);
|
|
15526
15925
|
}
|
|
15527
15926
|
} catch {
|
|
15528
15927
|
return;
|
|
15529
15928
|
}
|
|
15530
15929
|
for (const line of content.split("\n")) {
|
|
15531
15930
|
if (!line) continue;
|
|
15532
|
-
|
|
15533
|
-
|
|
15534
|
-
entry = JSON.parse(line);
|
|
15535
|
-
} catch {
|
|
15536
|
-
continue;
|
|
15537
|
-
}
|
|
15538
|
-
if (entry.type !== "user") continue;
|
|
15539
|
-
const msg = entry.message;
|
|
15540
|
-
const c = msg?.content;
|
|
15541
|
-
if (typeof c === "string") {
|
|
15542
|
-
yield c;
|
|
15543
|
-
} else if (Array.isArray(c)) {
|
|
15544
|
-
const text = c.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n");
|
|
15545
|
-
if (text) yield text;
|
|
15546
|
-
}
|
|
15931
|
+
const entry = parseUserLine(line);
|
|
15932
|
+
if (entry) yield entry.text;
|
|
15547
15933
|
}
|
|
15548
15934
|
}
|
|
15549
15935
|
|
|
@@ -15599,7 +15985,7 @@ function summariseSession(jsonlPath) {
|
|
|
15599
15985
|
}
|
|
15600
15986
|
const prompt = buildPrompt2(firstMessage, backlogIds);
|
|
15601
15987
|
try {
|
|
15602
|
-
const output =
|
|
15988
|
+
const output = execFileSync7("claude", ["-p", "--model", "haiku", prompt], {
|
|
15603
15989
|
encoding: "utf8",
|
|
15604
15990
|
timeout: 3e4,
|
|
15605
15991
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -15630,7 +16016,7 @@ ${firstMessage}`);
|
|
|
15630
16016
|
}
|
|
15631
16017
|
|
|
15632
16018
|
// src/commands/sessions/summarise/index.ts
|
|
15633
|
-
async function
|
|
16019
|
+
async function summarise4(options2) {
|
|
15634
16020
|
const files = await discoverSessionJsonlPaths();
|
|
15635
16021
|
if (files.length === 0) {
|
|
15636
16022
|
console.log(chalk145.yellow("No sessions found."));
|
|
@@ -15646,16 +16032,16 @@ async function summarise3(options2) {
|
|
|
15646
16032
|
`Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
|
|
15647
16033
|
)
|
|
15648
16034
|
);
|
|
15649
|
-
const { succeeded, failed } = processSessions(toProcess);
|
|
16035
|
+
const { succeeded, failed: failed2 } = processSessions(toProcess);
|
|
15650
16036
|
console.log(
|
|
15651
|
-
chalk145.green(`Done: ${succeeded} summarised`) + (
|
|
16037
|
+
chalk145.green(`Done: ${succeeded} summarised`) + (failed2 > 0 ? chalk145.yellow(`, ${failed2} skipped`) : "")
|
|
15652
16038
|
);
|
|
15653
16039
|
}
|
|
15654
16040
|
function selectCandidates(files, options2) {
|
|
15655
16041
|
const candidates = options2.force ? files : files.filter((f) => !hasSummary(f));
|
|
15656
16042
|
candidates.sort((a, b) => {
|
|
15657
16043
|
try {
|
|
15658
|
-
return
|
|
16044
|
+
return fs28.statSync(b).mtimeMs - fs28.statSync(a).mtimeMs;
|
|
15659
16045
|
} catch {
|
|
15660
16046
|
return 0;
|
|
15661
16047
|
}
|
|
@@ -15665,7 +16051,7 @@ function selectCandidates(files, options2) {
|
|
|
15665
16051
|
}
|
|
15666
16052
|
function processSessions(files) {
|
|
15667
16053
|
let succeeded = 0;
|
|
15668
|
-
let
|
|
16054
|
+
let failed2 = 0;
|
|
15669
16055
|
for (let i = 0; i < files.length; i++) {
|
|
15670
16056
|
const file = files[i];
|
|
15671
16057
|
process.stdout.write(chalk145.dim(` [${i + 1}/${files.length}] `));
|
|
@@ -15676,19 +16062,19 @@ function processSessions(files) {
|
|
|
15676
16062
|
process.stdout.write(`${chalk145.green("\u2713")} ${summary}
|
|
15677
16063
|
`);
|
|
15678
16064
|
} else {
|
|
15679
|
-
|
|
16065
|
+
failed2++;
|
|
15680
16066
|
process.stdout.write(` ${chalk145.yellow("skip")}
|
|
15681
16067
|
`);
|
|
15682
16068
|
}
|
|
15683
16069
|
}
|
|
15684
|
-
return { succeeded, failed };
|
|
16070
|
+
return { succeeded, failed: failed2 };
|
|
15685
16071
|
}
|
|
15686
16072
|
|
|
15687
16073
|
// src/commands/sessions/registerSessions.ts
|
|
15688
16074
|
function registerSessions(program2) {
|
|
15689
16075
|
const cmd = program2.command("sessions").description("Web dashboard for Claude Code sessions").action(() => web({ port: "3100" }));
|
|
15690
16076
|
cmd.command("web").description("Start the sessions web dashboard").option("-p, --port <number>", "Port to listen on", "3100").action(web);
|
|
15691
|
-
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(
|
|
16077
|
+
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(summarise4);
|
|
15692
16078
|
}
|
|
15693
16079
|
|
|
15694
16080
|
// src/commands/statusLine.ts
|
|
@@ -15769,21 +16155,21 @@ async function statusLine() {
|
|
|
15769
16155
|
}
|
|
15770
16156
|
|
|
15771
16157
|
// src/commands/sync.ts
|
|
15772
|
-
import * as
|
|
16158
|
+
import * as fs31 from "fs";
|
|
15773
16159
|
import * as os2 from "os";
|
|
15774
16160
|
import * as path50 from "path";
|
|
15775
16161
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
15776
16162
|
|
|
15777
16163
|
// src/commands/sync/syncClaudeMd.ts
|
|
15778
|
-
import * as
|
|
16164
|
+
import * as fs29 from "fs";
|
|
15779
16165
|
import * as path48 from "path";
|
|
15780
16166
|
import chalk148 from "chalk";
|
|
15781
16167
|
async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
15782
16168
|
const source = path48.join(claudeDir, "CLAUDE.md");
|
|
15783
16169
|
const target = path48.join(targetBase, "CLAUDE.md");
|
|
15784
|
-
const sourceContent =
|
|
15785
|
-
if (
|
|
15786
|
-
const targetContent =
|
|
16170
|
+
const sourceContent = fs29.readFileSync(source, "utf-8");
|
|
16171
|
+
if (fs29.existsSync(target)) {
|
|
16172
|
+
const targetContent = fs29.readFileSync(target, "utf-8");
|
|
15787
16173
|
if (sourceContent !== targetContent) {
|
|
15788
16174
|
console.log(
|
|
15789
16175
|
chalk148.yellow("\n\u26A0\uFE0F Warning: CLAUDE.md differs from existing file")
|
|
@@ -15800,21 +16186,21 @@ async function syncClaudeMd(claudeDir, targetBase, options2) {
|
|
|
15800
16186
|
}
|
|
15801
16187
|
}
|
|
15802
16188
|
}
|
|
15803
|
-
|
|
16189
|
+
fs29.copyFileSync(source, target);
|
|
15804
16190
|
console.log("Copied CLAUDE.md to ~/.claude/CLAUDE.md");
|
|
15805
16191
|
}
|
|
15806
16192
|
|
|
15807
16193
|
// src/commands/sync/syncSettings.ts
|
|
15808
|
-
import * as
|
|
16194
|
+
import * as fs30 from "fs";
|
|
15809
16195
|
import * as path49 from "path";
|
|
15810
16196
|
import chalk149 from "chalk";
|
|
15811
16197
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
15812
16198
|
const source = path49.join(claudeDir, "settings.json");
|
|
15813
16199
|
const target = path49.join(targetBase, "settings.json");
|
|
15814
|
-
const sourceContent =
|
|
16200
|
+
const sourceContent = fs30.readFileSync(source, "utf-8");
|
|
15815
16201
|
const mergedContent = JSON.stringify(JSON.parse(sourceContent), null, " ");
|
|
15816
|
-
if (
|
|
15817
|
-
const targetContent =
|
|
16202
|
+
if (fs30.existsSync(target)) {
|
|
16203
|
+
const targetContent = fs30.readFileSync(target, "utf-8");
|
|
15818
16204
|
const normalizedTarget = JSON.stringify(
|
|
15819
16205
|
JSON.parse(targetContent),
|
|
15820
16206
|
null,
|
|
@@ -15840,7 +16226,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
15840
16226
|
}
|
|
15841
16227
|
}
|
|
15842
16228
|
}
|
|
15843
|
-
|
|
16229
|
+
fs30.writeFileSync(target, mergedContent);
|
|
15844
16230
|
console.log("Copied settings.json to ~/.claude/settings.json");
|
|
15845
16231
|
}
|
|
15846
16232
|
|
|
@@ -15859,10 +16245,10 @@ async function sync(options2) {
|
|
|
15859
16245
|
function syncCommands(claudeDir, targetBase) {
|
|
15860
16246
|
const sourceDir = path50.join(claudeDir, "commands");
|
|
15861
16247
|
const targetDir = path50.join(targetBase, "commands");
|
|
15862
|
-
|
|
15863
|
-
const files =
|
|
16248
|
+
fs31.mkdirSync(targetDir, { recursive: true });
|
|
16249
|
+
const files = fs31.readdirSync(sourceDir);
|
|
15864
16250
|
for (const file of files) {
|
|
15865
|
-
|
|
16251
|
+
fs31.copyFileSync(path50.join(sourceDir, file), path50.join(targetDir, file));
|
|
15866
16252
|
console.log(`Copied ${file} to ${targetDir}`);
|
|
15867
16253
|
}
|
|
15868
16254
|
console.log(`Synced ${files.length} command(s) to ~/.claude/commands`);
|
|
@@ -15930,6 +16316,7 @@ program.command("coverage").description("Print global statement coverage percent
|
|
|
15930
16316
|
program.command("screenshot").description("Capture a screenshot of a running application window").argument("<process>", "Name of the running process (e.g. notepad, code)").action(screenshot);
|
|
15931
16317
|
registerActivity(program);
|
|
15932
16318
|
registerCliHook(program);
|
|
16319
|
+
registerHandover(program);
|
|
15933
16320
|
registerJira(program);
|
|
15934
16321
|
registerMermaid(program);
|
|
15935
16322
|
registerPrs(program);
|