@staff0rd/assist 0.660.2 → 0.661.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/allowed.cli-reads +1 -0
- package/dist/allowed.cli-reads +1 -0
- package/dist/commands/sessions/web/bundle.js +1 -1
- package/dist/index.js +802 -307
- 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.661.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -717,7 +717,12 @@ var assistConfigShape = {
|
|
|
717
717
|
readingWordsPerMinute: z3.number().int().positive().optional()
|
|
718
718
|
}).optional(),
|
|
719
719
|
review: z3.strictObject({
|
|
720
|
-
codexModel: z3.string().optional()
|
|
720
|
+
codexModel: z3.string().optional(),
|
|
721
|
+
highLevel: z3.strictObject({
|
|
722
|
+
criticalPaths: z3.array(z3.string()).default([]),
|
|
723
|
+
uiPaths: z3.array(z3.string()).default([]),
|
|
724
|
+
descriptionWordCap: z3.number().int().positive().default(300)
|
|
725
|
+
}).optional()
|
|
721
726
|
}).optional(),
|
|
722
727
|
readTime: z3.strictObject({
|
|
723
728
|
wordsPerMinute: z3.number().int().positive().default(200)
|
|
@@ -1941,10 +1946,10 @@ import fs11 from "fs";
|
|
|
1941
1946
|
|
|
1942
1947
|
// src/commands/lint/shared.ts
|
|
1943
1948
|
import chalk24 from "chalk";
|
|
1944
|
-
function reportViolations2(violations,
|
|
1949
|
+
function reportViolations2(violations, checkName2, errorMessage, successMessage) {
|
|
1945
1950
|
if (violations.length > 0) {
|
|
1946
1951
|
console.error(chalk24.red(`
|
|
1947
|
-
${
|
|
1952
|
+
${checkName2} failed:
|
|
1948
1953
|
`));
|
|
1949
1954
|
console.error(chalk24.red(` ${errorMessage}
|
|
1950
1955
|
`));
|
|
@@ -3363,6 +3368,21 @@ var reviewConfigHelp = [
|
|
|
3363
3368
|
key: "review.codexModel",
|
|
3364
3369
|
setter: "assist config set review.codexModel gpt-5-codex",
|
|
3365
3370
|
note: "optional; runs the codex reviewer on this model via the LiteLLM proxy (needs litellm.baseUrl and litellm.apiKey)"
|
|
3371
|
+
},
|
|
3372
|
+
{
|
|
3373
|
+
key: "review.highLevel.criticalPaths",
|
|
3374
|
+
setter: 'assist config set review.highLevel.criticalPaths "**/*.graphql,en-AU/translation.json"',
|
|
3375
|
+
note: "comma-separated globs whose full diffs the high-level review shows; unset shows none"
|
|
3376
|
+
},
|
|
3377
|
+
{
|
|
3378
|
+
key: "review.highLevel.uiPaths",
|
|
3379
|
+
setter: 'assist config set review.highLevel.uiPaths "src/ui/**"',
|
|
3380
|
+
note: "comma-separated globs that make a change a UI change, so --high-level requires a screenshot or video; unset requires none"
|
|
3381
|
+
},
|
|
3382
|
+
{
|
|
3383
|
+
key: "review.highLevel.descriptionWordCap",
|
|
3384
|
+
setter: "assist config set review.highLevel.descriptionWordCap 300",
|
|
3385
|
+
note: "word cap --high-level holds the PR description to (default 300)"
|
|
3366
3386
|
}
|
|
3367
3387
|
];
|
|
3368
3388
|
|
|
@@ -5010,12 +5030,12 @@ function addViteBaseConfig() {
|
|
|
5010
5030
|
console.log("vite.config.ts already has base config");
|
|
5011
5031
|
return;
|
|
5012
5032
|
}
|
|
5013
|
-
const
|
|
5033
|
+
const updated2 = content.replace(
|
|
5014
5034
|
/defineConfig\(\{/,
|
|
5015
5035
|
'defineConfig({\n base: "./",'
|
|
5016
5036
|
);
|
|
5017
|
-
if (
|
|
5018
|
-
writeFileSync13(viteConfigPath,
|
|
5037
|
+
if (updated2 !== content) {
|
|
5038
|
+
writeFileSync13(viteConfigPath, updated2);
|
|
5019
5039
|
console.log('Added base: "./" to vite.config.ts');
|
|
5020
5040
|
}
|
|
5021
5041
|
}
|
|
@@ -10716,16 +10736,16 @@ async function patchItemStatus(req, res, id) {
|
|
|
10716
10736
|
const result = await findItemOr404(res, id);
|
|
10717
10737
|
if (!result) return;
|
|
10718
10738
|
await updateStatus(result.orm, id, status3);
|
|
10719
|
-
const
|
|
10720
|
-
respondJson(res, 200,
|
|
10739
|
+
const updated2 = { ...result.item, status: status3 };
|
|
10740
|
+
respondJson(res, 200, updated2);
|
|
10721
10741
|
}
|
|
10722
10742
|
async function patchItemStar(req, res, id) {
|
|
10723
10743
|
const { starred } = await parseStarBody(req);
|
|
10724
10744
|
const result = await findItemOr404(res, id);
|
|
10725
10745
|
if (!result) return;
|
|
10726
10746
|
await updateStarred(result.orm, id, starred);
|
|
10727
|
-
const
|
|
10728
|
-
respondJson(res, 200,
|
|
10747
|
+
const updated2 = { ...result.item, starred };
|
|
10748
|
+
respondJson(res, 200, updated2);
|
|
10729
10749
|
}
|
|
10730
10750
|
|
|
10731
10751
|
// src/commands/backlog/web/deleteItemComment.ts
|
|
@@ -13045,15 +13065,15 @@ function setNestedValue(obj, path91, value) {
|
|
|
13045
13065
|
}
|
|
13046
13066
|
|
|
13047
13067
|
// src/commands/config/validateConfig.ts
|
|
13048
|
-
function validateConfig(
|
|
13049
|
-
const result = schema2.safeParse(stripLegacyConfigKeys(
|
|
13068
|
+
function validateConfig(updated2, key, schema2 = assistConfigSchema) {
|
|
13069
|
+
const result = schema2.safeParse(stripLegacyConfigKeys(updated2));
|
|
13050
13070
|
if (result.success) return { ok: true };
|
|
13051
13071
|
const errors = result.error.issues.flatMap(
|
|
13052
13072
|
(issue) => formatIssue2(issue, key)
|
|
13053
13073
|
);
|
|
13054
13074
|
return {
|
|
13055
13075
|
ok: false,
|
|
13056
|
-
errors: scrubConfigSecrets(errors,
|
|
13076
|
+
errors: scrubConfigSecrets(errors, updated2, describeConfigNode(schema2))
|
|
13057
13077
|
};
|
|
13058
13078
|
}
|
|
13059
13079
|
function formatIssue2(issue, key) {
|
|
@@ -13076,14 +13096,14 @@ function applyConfigSet(key, coerced, global, cwd = process.cwd(), globalConfigP
|
|
|
13076
13096
|
};
|
|
13077
13097
|
}
|
|
13078
13098
|
const raw = global ? loadGlobalConfigRaw(globalConfigPath) : loadProjectConfig(cwd);
|
|
13079
|
-
const
|
|
13080
|
-
const validation = validateConfig(
|
|
13099
|
+
const updated2 = setNestedValue(raw, key, coerced);
|
|
13100
|
+
const validation = validateConfig(updated2, key);
|
|
13081
13101
|
if (!validation.ok) return validation;
|
|
13082
13102
|
if (global) {
|
|
13083
|
-
saveGlobalConfig(
|
|
13103
|
+
saveGlobalConfig(updated2, globalConfigPath);
|
|
13084
13104
|
return { ok: true, target: "global" };
|
|
13085
13105
|
}
|
|
13086
|
-
saveConfig(
|
|
13106
|
+
saveConfig(updated2, cwd);
|
|
13087
13107
|
return { ok: true, target: "project" };
|
|
13088
13108
|
}
|
|
13089
13109
|
|
|
@@ -16467,20 +16487,20 @@ function hasListMutations(options2) {
|
|
|
16467
16487
|
options2.add && options2.add.length > 0 || options2.edit || options2.remove
|
|
16468
16488
|
);
|
|
16469
16489
|
}
|
|
16470
|
-
function applyListMutations(current, options2,
|
|
16490
|
+
function applyListMutations(current, options2, flags2) {
|
|
16471
16491
|
let items2 = [...current];
|
|
16472
16492
|
if (options2.edit) {
|
|
16473
16493
|
const [rawIndex, ...textParts] = options2.edit;
|
|
16474
16494
|
if (rawIndex === void 0 || textParts.length === 0) {
|
|
16475
16495
|
return {
|
|
16476
16496
|
ok: false,
|
|
16477
|
-
error: `${
|
|
16497
|
+
error: `${flags2.edit} requires an index and replacement text.`
|
|
16478
16498
|
};
|
|
16479
16499
|
}
|
|
16480
16500
|
const parsed = parseListIndex(
|
|
16481
16501
|
rawIndex,
|
|
16482
16502
|
items2.length,
|
|
16483
|
-
`${
|
|
16503
|
+
`${flags2.edit} index`
|
|
16484
16504
|
);
|
|
16485
16505
|
if (!parsed.ok) return parsed;
|
|
16486
16506
|
items2[parsed.index - 1] = textParts.join(" ");
|
|
@@ -16489,7 +16509,7 @@ function applyListMutations(current, options2, flags) {
|
|
|
16489
16509
|
const parsed = parseListIndex(
|
|
16490
16510
|
options2.remove,
|
|
16491
16511
|
items2.length,
|
|
16492
|
-
`${
|
|
16512
|
+
`${flags2.remove} index`
|
|
16493
16513
|
);
|
|
16494
16514
|
if (!parsed.ok) return parsed;
|
|
16495
16515
|
items2 = items2.filter((_, i) => i !== parsed.index - 1);
|
|
@@ -16501,15 +16521,15 @@ function applyListMutations(current, options2, flags) {
|
|
|
16501
16521
|
}
|
|
16502
16522
|
|
|
16503
16523
|
// src/commands/backlog/update/resolveListUpdate.ts
|
|
16504
|
-
function resolveListUpdate(whole, current, mutations, wholeFlag,
|
|
16524
|
+
function resolveListUpdate(whole, current, mutations, wholeFlag, flags2) {
|
|
16505
16525
|
if (!hasListMutations(mutations)) return { ok: true, items: whole };
|
|
16506
16526
|
if (whole) {
|
|
16507
16527
|
return {
|
|
16508
16528
|
ok: false,
|
|
16509
|
-
error: `Cannot combine ${wholeFlag} with ${
|
|
16529
|
+
error: `Cannot combine ${wholeFlag} with ${flags2.add}/${flags2.edit}/${flags2.remove}.`
|
|
16510
16530
|
};
|
|
16511
16531
|
}
|
|
16512
|
-
return applyListMutations(current, mutations,
|
|
16532
|
+
return applyListMutations(current, mutations, flags2);
|
|
16513
16533
|
}
|
|
16514
16534
|
|
|
16515
16535
|
// src/commands/backlog/resolvePhaseFields.ts
|
|
@@ -18428,8 +18448,8 @@ function loadDenyConfig(global) {
|
|
|
18428
18448
|
const deny = config.deny ?? [];
|
|
18429
18449
|
return {
|
|
18430
18450
|
deny,
|
|
18431
|
-
saveDeny: (
|
|
18432
|
-
config.deny =
|
|
18451
|
+
saveDeny: (updated2) => {
|
|
18452
|
+
config.deny = updated2;
|
|
18433
18453
|
save(config);
|
|
18434
18454
|
}
|
|
18435
18455
|
};
|
|
@@ -27471,8 +27491,218 @@ async function reply(commentId, body) {
|
|
|
27471
27491
|
}
|
|
27472
27492
|
}
|
|
27473
27493
|
|
|
27474
|
-
// src/commands/prs/
|
|
27494
|
+
// src/commands/prs/status/describeFetchError.ts
|
|
27495
|
+
function firstLine2(text18) {
|
|
27496
|
+
const line = text18.split("\n").map((candidate) => candidate.trim()).find((candidate) => candidate.length > 0);
|
|
27497
|
+
return line ?? null;
|
|
27498
|
+
}
|
|
27499
|
+
function stderrText(error) {
|
|
27500
|
+
const stderr = error?.stderr;
|
|
27501
|
+
if (typeof stderr === "string") return firstLine2(stderr);
|
|
27502
|
+
if (stderr instanceof Uint8Array) {
|
|
27503
|
+
return firstLine2(Buffer.from(stderr).toString("utf8"));
|
|
27504
|
+
}
|
|
27505
|
+
return null;
|
|
27506
|
+
}
|
|
27507
|
+
function describeFetchError(error) {
|
|
27508
|
+
if (isGhNotInstalled(error)) {
|
|
27509
|
+
return "GitHub CLI (gh) is not installed \u2014 see https://cli.github.com/";
|
|
27510
|
+
}
|
|
27511
|
+
const stderr = stderrText(error);
|
|
27512
|
+
if (stderr) return stderr;
|
|
27513
|
+
if (error instanceof Error) return firstLine2(error.message) ?? error.message;
|
|
27514
|
+
return String(error);
|
|
27515
|
+
}
|
|
27516
|
+
|
|
27517
|
+
// src/commands/prs/status/fetchRepoPullRequests.ts
|
|
27475
27518
|
import { execSync as execSync51 } from "child_process";
|
|
27519
|
+
var FIELDS = "number,title,url,author,isDraft,createdAt,updatedAt,reviewDecision,latestReviews,statusCheckRollup,mergeable";
|
|
27520
|
+
function fetchRepoPullRequests(org, repo) {
|
|
27521
|
+
const output = execSync51(
|
|
27522
|
+
`gh pr list --state open --json ${FIELDS} --limit 100 -R ${org}/${repo}`,
|
|
27523
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
|
|
27524
|
+
);
|
|
27525
|
+
const parsed = JSON.parse(output);
|
|
27526
|
+
if (!Array.isArray(parsed)) {
|
|
27527
|
+
throw new Error("unexpected response from gh pr list");
|
|
27528
|
+
}
|
|
27529
|
+
return parsed;
|
|
27530
|
+
}
|
|
27531
|
+
|
|
27532
|
+
// src/commands/prs/status/parseRepoArgument.ts
|
|
27533
|
+
var REPO_ARGUMENT = /^([A-Za-z0-9._-]+)\/([A-Za-z0-9._-]+)$/;
|
|
27534
|
+
function parseRepoArgument(value) {
|
|
27535
|
+
const match = REPO_ARGUMENT.exec(value.trim());
|
|
27536
|
+
if (!match) return null;
|
|
27537
|
+
return { org: match[1], repo: match[2] };
|
|
27538
|
+
}
|
|
27539
|
+
|
|
27540
|
+
// src/commands/prs/status/describeAge.ts
|
|
27541
|
+
function describeAge(timestamp6, now = Date.now()) {
|
|
27542
|
+
const parsed = new Date(timestamp6).getTime();
|
|
27543
|
+
if (Number.isNaN(parsed)) return { hours: null, label: "unknown" };
|
|
27544
|
+
const hours = Math.max(0, Math.floor((now - parsed) / 36e5));
|
|
27545
|
+
if (hours < 1) return { hours, label: "<1h" };
|
|
27546
|
+
if (hours < 24) return { hours, label: `${hours}h` };
|
|
27547
|
+
return { hours, label: `${Math.floor(hours / 24)}d` };
|
|
27548
|
+
}
|
|
27549
|
+
|
|
27550
|
+
// src/commands/prs/status/summariseChecks.ts
|
|
27551
|
+
var PASSING_CONCLUSIONS = /* @__PURE__ */ new Set(["SUCCESS", "NEUTRAL", "SKIPPED"]);
|
|
27552
|
+
var PENDING_STATES = /* @__PURE__ */ new Set(["PENDING", "EXPECTED"]);
|
|
27553
|
+
function checkName(check2) {
|
|
27554
|
+
return check2.name?.trim() || check2.context?.trim() || "unnamed check";
|
|
27555
|
+
}
|
|
27556
|
+
function isPending(check2) {
|
|
27557
|
+
if (check2.status) return check2.status !== "COMPLETED";
|
|
27558
|
+
return PENDING_STATES.has(check2.state ?? "");
|
|
27559
|
+
}
|
|
27560
|
+
function isFailing(check2) {
|
|
27561
|
+
if (check2.status) return !PASSING_CONCLUSIONS.has(check2.conclusion ?? "");
|
|
27562
|
+
return !PASSING_CONCLUSIONS.has(check2.state ?? "");
|
|
27563
|
+
}
|
|
27564
|
+
function summariseChecks(rollup) {
|
|
27565
|
+
const summary = { failing: [], pending: [] };
|
|
27566
|
+
for (const check2 of rollup ?? []) {
|
|
27567
|
+
if (isPending(check2)) summary.pending.push(checkName(check2));
|
|
27568
|
+
else if (isFailing(check2)) summary.failing.push(checkName(check2));
|
|
27569
|
+
}
|
|
27570
|
+
return summary;
|
|
27571
|
+
}
|
|
27572
|
+
|
|
27573
|
+
// src/commands/prs/status/toPrStatus.ts
|
|
27574
|
+
function toReviews(pr) {
|
|
27575
|
+
return (pr.latestReviews ?? []).map((review2) => ({
|
|
27576
|
+
reviewer: review2.author?.login ?? "unknown",
|
|
27577
|
+
state: review2.state ?? "UNKNOWN"
|
|
27578
|
+
}));
|
|
27579
|
+
}
|
|
27580
|
+
function toPrStatus(pr, now = Date.now()) {
|
|
27581
|
+
const age = describeAge(pr.updatedAt, now);
|
|
27582
|
+
return {
|
|
27583
|
+
number: pr.number,
|
|
27584
|
+
title: pr.title,
|
|
27585
|
+
url: pr.url,
|
|
27586
|
+
author: pr.author?.login ?? "unknown",
|
|
27587
|
+
isBot: pr.author?.is_bot === true,
|
|
27588
|
+
isDraft: pr.isDraft === true,
|
|
27589
|
+
createdAt: pr.createdAt,
|
|
27590
|
+
updatedAt: pr.updatedAt,
|
|
27591
|
+
age: age.label,
|
|
27592
|
+
ageHours: age.hours,
|
|
27593
|
+
reviewDecision: pr.reviewDecision || null,
|
|
27594
|
+
reviews: toReviews(pr),
|
|
27595
|
+
checks: summariseChecks(pr.statusCheckRollup),
|
|
27596
|
+
mergeable: pr.mergeable || "UNKNOWN"
|
|
27597
|
+
};
|
|
27598
|
+
}
|
|
27599
|
+
|
|
27600
|
+
// src/commands/prs/status/buildPrsStatusReport.ts
|
|
27601
|
+
function buildPrsStatusReport(repoArguments, now = Date.now()) {
|
|
27602
|
+
const report2 = { repos: [], errors: [] };
|
|
27603
|
+
for (const argument of repoArguments) {
|
|
27604
|
+
const parsed = parseRepoArgument(argument);
|
|
27605
|
+
if (!parsed) {
|
|
27606
|
+
report2.errors.push({
|
|
27607
|
+
repo: argument,
|
|
27608
|
+
error: "not an owner/repo argument"
|
|
27609
|
+
});
|
|
27610
|
+
continue;
|
|
27611
|
+
}
|
|
27612
|
+
const repo = `${parsed.org}/${parsed.repo}`;
|
|
27613
|
+
try {
|
|
27614
|
+
const pullRequests = fetchRepoPullRequests(parsed.org, parsed.repo).map(
|
|
27615
|
+
(pr) => toPrStatus(pr, now)
|
|
27616
|
+
);
|
|
27617
|
+
report2.repos.push({ repo, pullRequests });
|
|
27618
|
+
} catch (error) {
|
|
27619
|
+
report2.errors.push({ repo, error: describeFetchError(error) });
|
|
27620
|
+
}
|
|
27621
|
+
}
|
|
27622
|
+
return report2;
|
|
27623
|
+
}
|
|
27624
|
+
|
|
27625
|
+
// src/commands/prs/status/printPrsStatus.ts
|
|
27626
|
+
import chalk187 from "chalk";
|
|
27627
|
+
|
|
27628
|
+
// src/commands/prs/status/printPrStatus.ts
|
|
27629
|
+
import chalk186 from "chalk";
|
|
27630
|
+
function flags(pr) {
|
|
27631
|
+
const markers = [];
|
|
27632
|
+
if (pr.isDraft) markers.push("draft");
|
|
27633
|
+
if (pr.isBot) markers.push("bot");
|
|
27634
|
+
return markers.length ? ` ${chalk186.yellow(`[${markers.join(", ")}]`)}` : "";
|
|
27635
|
+
}
|
|
27636
|
+
function updated(pr) {
|
|
27637
|
+
return pr.ageHours === null ? "updated unknown" : `updated ${pr.age} ago`;
|
|
27638
|
+
}
|
|
27639
|
+
function reviewLine(pr) {
|
|
27640
|
+
if (!pr.reviewDecision && pr.reviews.length === 0) return null;
|
|
27641
|
+
const reviewers = pr.reviews.map((review2) => `${review2.reviewer}: ${review2.state}`).join(", ");
|
|
27642
|
+
const decision = pr.reviewDecision ?? "no decision";
|
|
27643
|
+
return reviewers ? `review: ${decision} | ${reviewers}` : `review: ${decision}`;
|
|
27644
|
+
}
|
|
27645
|
+
function checkLines(pr) {
|
|
27646
|
+
const lines2 = [];
|
|
27647
|
+
if (pr.checks.failing.length) {
|
|
27648
|
+
lines2.push(chalk186.red(`failing: ${pr.checks.failing.join(", ")}`));
|
|
27649
|
+
}
|
|
27650
|
+
if (pr.checks.pending.length) {
|
|
27651
|
+
lines2.push(chalk186.yellow(`pending: ${pr.checks.pending.join(", ")}`));
|
|
27652
|
+
}
|
|
27653
|
+
return lines2;
|
|
27654
|
+
}
|
|
27655
|
+
function mergeableLine(pr) {
|
|
27656
|
+
if (pr.mergeable === "CONFLICTING") return chalk186.red("conflicting");
|
|
27657
|
+
if (pr.mergeable === "MERGEABLE") return null;
|
|
27658
|
+
return chalk186.dim(`mergeable: ${pr.mergeable.toLowerCase()}`);
|
|
27659
|
+
}
|
|
27660
|
+
function printPrStatus(pr) {
|
|
27661
|
+
console.log(
|
|
27662
|
+
` ${chalk186.cyan(`#${pr.number}`)} ${pr.title} ${chalk186.dim(`(${pr.author}, ${updated(pr)})`)}${flags(pr)}`
|
|
27663
|
+
);
|
|
27664
|
+
const details = [
|
|
27665
|
+
reviewLine(pr),
|
|
27666
|
+
...checkLines(pr),
|
|
27667
|
+
mergeableLine(pr),
|
|
27668
|
+
chalk186.dim(pr.url)
|
|
27669
|
+
];
|
|
27670
|
+
for (const detail of details) {
|
|
27671
|
+
if (detail) console.log(` ${detail}`);
|
|
27672
|
+
}
|
|
27673
|
+
}
|
|
27674
|
+
|
|
27675
|
+
// src/commands/prs/status/printPrsStatus.ts
|
|
27676
|
+
function printPrsStatus(report2) {
|
|
27677
|
+
for (const repo of report2.repos) {
|
|
27678
|
+
const count8 = repo.pullRequests.length;
|
|
27679
|
+
console.log(`${chalk187.bold(repo.repo)} ${chalk187.dim(`(${count8} open)`)}`);
|
|
27680
|
+
for (const pr of repo.pullRequests) {
|
|
27681
|
+
printPrStatus(pr);
|
|
27682
|
+
}
|
|
27683
|
+
if (count8 === 0) console.log(chalk187.dim(" no open pull requests"));
|
|
27684
|
+
console.log();
|
|
27685
|
+
}
|
|
27686
|
+
if (report2.errors.length === 0) return;
|
|
27687
|
+
console.log(chalk187.bold("Errors"));
|
|
27688
|
+
for (const failure of report2.errors) {
|
|
27689
|
+
console.log(` ${chalk187.red(failure.repo)}: ${failure.error}`);
|
|
27690
|
+
}
|
|
27691
|
+
console.log();
|
|
27692
|
+
}
|
|
27693
|
+
|
|
27694
|
+
// src/commands/prs/status/index.ts
|
|
27695
|
+
function prsStatus(repos2, options2) {
|
|
27696
|
+
const report2 = buildPrsStatusReport(repos2);
|
|
27697
|
+
if (options2.json) {
|
|
27698
|
+
console.log(JSON.stringify(report2, null, 2));
|
|
27699
|
+
return;
|
|
27700
|
+
}
|
|
27701
|
+
printPrsStatus(report2);
|
|
27702
|
+
}
|
|
27703
|
+
|
|
27704
|
+
// src/commands/prs/wontfix.ts
|
|
27705
|
+
import { execSync as execSync52 } from "child_process";
|
|
27476
27706
|
function validateReason(reason4) {
|
|
27477
27707
|
const lowerReason = reason4.toLowerCase();
|
|
27478
27708
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -27489,7 +27719,7 @@ function validateShaReferences(reason4) {
|
|
|
27489
27719
|
const invalidShas = [];
|
|
27490
27720
|
for (const sha of shas) {
|
|
27491
27721
|
try {
|
|
27492
|
-
|
|
27722
|
+
execSync52(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
27493
27723
|
} catch {
|
|
27494
27724
|
invalidShas.push(sha);
|
|
27495
27725
|
}
|
|
@@ -27782,6 +28012,13 @@ function registerPrsRaise(prsCommand) {
|
|
|
27782
28012
|
configHelp(raiseCommand, prsRaiseConfigHelp);
|
|
27783
28013
|
}
|
|
27784
28014
|
|
|
28015
|
+
// src/commands/registerPrsStatus.ts
|
|
28016
|
+
function registerPrsStatus(prsCommand) {
|
|
28017
|
+
prsCommand.command("status <repo...>").description(
|
|
28018
|
+
"Report every open pull request in each owner/repo, grouped by repo"
|
|
28019
|
+
).option("--json", "Output as JSON").action(prsStatus);
|
|
28020
|
+
}
|
|
28021
|
+
|
|
27785
28022
|
// src/commands/readTime/readTime.ts
|
|
27786
28023
|
import { readFileSync as readFileSync46 } from "fs";
|
|
27787
28024
|
|
|
@@ -27871,7 +28108,7 @@ function resolveReadTimeTarget(target) {
|
|
|
27871
28108
|
}
|
|
27872
28109
|
|
|
27873
28110
|
// src/commands/prs/fetchPrBody.ts
|
|
27874
|
-
import { execSync as
|
|
28111
|
+
import { execSync as execSync53 } from "child_process";
|
|
27875
28112
|
function exitGhNotInstalled() {
|
|
27876
28113
|
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
27877
28114
|
console.error("Install it from https://cli.github.com/");
|
|
@@ -27892,7 +28129,7 @@ function currentRepo() {
|
|
|
27892
28129
|
function fetchPrBody(number, repo) {
|
|
27893
28130
|
const { org, repo: name } = repo ?? currentRepo();
|
|
27894
28131
|
try {
|
|
27895
|
-
const raw =
|
|
28132
|
+
const raw = execSync53(`gh pr view ${number} --json body -R ${org}/${name}`, {
|
|
27896
28133
|
encoding: "utf8",
|
|
27897
28134
|
stdio: ["pipe", "pipe", "pipe"]
|
|
27898
28135
|
});
|
|
@@ -27971,6 +28208,7 @@ function registerReadTime(parent) {
|
|
|
27971
28208
|
function registerPrs(program2) {
|
|
27972
28209
|
const prsCommand = program2.command("prs").description("Pull request utilities").option("--open", "List only open pull requests").option("--closed", "List only closed pull requests").action(prs);
|
|
27973
28210
|
registerPrsRaise(prsCommand);
|
|
28211
|
+
registerPrsStatus(prsCommand);
|
|
27974
28212
|
registerPrsEdit(prsCommand);
|
|
27975
28213
|
registerPrsComments(prsCommand);
|
|
27976
28214
|
registerReadTime(prsCommand);
|
|
@@ -27978,10 +28216,10 @@ function registerPrs(program2) {
|
|
|
27978
28216
|
}
|
|
27979
28217
|
|
|
27980
28218
|
// src/commands/ravendb/ravendbAuth.ts
|
|
27981
|
-
import
|
|
28219
|
+
import chalk193 from "chalk";
|
|
27982
28220
|
|
|
27983
28221
|
// src/shared/createConnectionAuth.ts
|
|
27984
|
-
import
|
|
28222
|
+
import chalk188 from "chalk";
|
|
27985
28223
|
function listConnections(connections, format2) {
|
|
27986
28224
|
if (connections.length === 0) {
|
|
27987
28225
|
console.log("No connections configured.");
|
|
@@ -27994,7 +28232,7 @@ function listConnections(connections, format2) {
|
|
|
27994
28232
|
function removeConnection(connections, name, save) {
|
|
27995
28233
|
const filtered = connections.filter((c) => c.name !== name);
|
|
27996
28234
|
if (filtered.length === connections.length) {
|
|
27997
|
-
console.error(
|
|
28235
|
+
console.error(chalk188.red(`Connection "${name}" not found.`));
|
|
27998
28236
|
process.exit(1);
|
|
27999
28237
|
}
|
|
28000
28238
|
save(filtered);
|
|
@@ -28040,17 +28278,17 @@ function saveConnections(connections) {
|
|
|
28040
28278
|
}
|
|
28041
28279
|
|
|
28042
28280
|
// src/commands/ravendb/promptConnection.ts
|
|
28043
|
-
import
|
|
28281
|
+
import chalk191 from "chalk";
|
|
28044
28282
|
|
|
28045
28283
|
// src/commands/ravendb/selectOpSecret.ts
|
|
28046
|
-
import
|
|
28284
|
+
import chalk190 from "chalk";
|
|
28047
28285
|
import Enquirer2 from "enquirer";
|
|
28048
28286
|
|
|
28049
28287
|
// src/commands/ravendb/searchItems.ts
|
|
28050
|
-
import { execSync as
|
|
28051
|
-
import
|
|
28288
|
+
import { execSync as execSync54 } from "child_process";
|
|
28289
|
+
import chalk189 from "chalk";
|
|
28052
28290
|
function opExec(args) {
|
|
28053
|
-
return
|
|
28291
|
+
return execSync54(`op ${args}`, {
|
|
28054
28292
|
encoding: "utf8",
|
|
28055
28293
|
stdio: ["pipe", "pipe", "pipe"]
|
|
28056
28294
|
}).trim();
|
|
@@ -28061,7 +28299,7 @@ function searchItems(search2) {
|
|
|
28061
28299
|
items2 = JSON.parse(opExec("item list --format=json"));
|
|
28062
28300
|
} catch {
|
|
28063
28301
|
console.error(
|
|
28064
|
-
|
|
28302
|
+
chalk189.red(
|
|
28065
28303
|
"Failed to search 1Password. Ensure the CLI is installed and you are signed in."
|
|
28066
28304
|
)
|
|
28067
28305
|
);
|
|
@@ -28075,7 +28313,7 @@ function getItemFields(itemId2) {
|
|
|
28075
28313
|
const item = JSON.parse(opExec(`item get "${itemId2}" --format=json`));
|
|
28076
28314
|
return item.fields.filter((f) => f.reference && f.label);
|
|
28077
28315
|
} catch {
|
|
28078
|
-
console.error(
|
|
28316
|
+
console.error(chalk189.red("Failed to get item details from 1Password."));
|
|
28079
28317
|
process.exit(1);
|
|
28080
28318
|
}
|
|
28081
28319
|
}
|
|
@@ -28094,7 +28332,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
28094
28332
|
}).run();
|
|
28095
28333
|
const items2 = searchItems(search2);
|
|
28096
28334
|
if (items2.length === 0) {
|
|
28097
|
-
console.error(
|
|
28335
|
+
console.error(chalk190.red(`No items found matching "${search2}".`));
|
|
28098
28336
|
process.exit(1);
|
|
28099
28337
|
}
|
|
28100
28338
|
const itemId2 = await selectOne(
|
|
@@ -28103,7 +28341,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
28103
28341
|
);
|
|
28104
28342
|
const fields = getItemFields(itemId2);
|
|
28105
28343
|
if (fields.length === 0) {
|
|
28106
|
-
console.error(
|
|
28344
|
+
console.error(chalk190.red("No fields with references found on this item."));
|
|
28107
28345
|
process.exit(1);
|
|
28108
28346
|
}
|
|
28109
28347
|
const ref = await selectOne(
|
|
@@ -28117,7 +28355,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
28117
28355
|
async function promptConnection(existingNames) {
|
|
28118
28356
|
const name = await promptInput("name", "Connection name:");
|
|
28119
28357
|
if (existingNames.includes(name)) {
|
|
28120
|
-
console.error(
|
|
28358
|
+
console.error(chalk191.red(`Connection "${name}" already exists.`));
|
|
28121
28359
|
process.exit(1);
|
|
28122
28360
|
}
|
|
28123
28361
|
const url = await promptInput(
|
|
@@ -28126,22 +28364,22 @@ async function promptConnection(existingNames) {
|
|
|
28126
28364
|
);
|
|
28127
28365
|
const database = await promptInput("database", "Database name:");
|
|
28128
28366
|
if (!name || !url || !database) {
|
|
28129
|
-
console.error(
|
|
28367
|
+
console.error(chalk191.red("All fields are required."));
|
|
28130
28368
|
process.exit(1);
|
|
28131
28369
|
}
|
|
28132
28370
|
const apiKeyRef = await selectOpSecret();
|
|
28133
|
-
console.log(
|
|
28371
|
+
console.log(chalk191.dim(`Using: ${apiKeyRef}`));
|
|
28134
28372
|
return { name, url, database, apiKeyRef };
|
|
28135
28373
|
}
|
|
28136
28374
|
|
|
28137
28375
|
// src/commands/ravendb/ravendbSetConnection.ts
|
|
28138
|
-
import
|
|
28376
|
+
import chalk192 from "chalk";
|
|
28139
28377
|
function ravendbSetConnection(name) {
|
|
28140
28378
|
const raw = loadGlobalConfigRaw();
|
|
28141
28379
|
const ravendb = raw.ravendb ?? {};
|
|
28142
28380
|
const connections = ravendb.connections ?? [];
|
|
28143
28381
|
if (!connections.some((c) => c.name === name)) {
|
|
28144
|
-
console.error(
|
|
28382
|
+
console.error(chalk192.red(`Connection "${name}" not found.`));
|
|
28145
28383
|
console.error(
|
|
28146
28384
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
28147
28385
|
);
|
|
@@ -28157,16 +28395,16 @@ function ravendbSetConnection(name) {
|
|
|
28157
28395
|
var ravendbAuth = createConnectionAuth({
|
|
28158
28396
|
load: loadConnections,
|
|
28159
28397
|
save: saveConnections,
|
|
28160
|
-
format: (c) => `${
|
|
28398
|
+
format: (c) => `${chalk193.bold(c.name)} ${c.url} db=${c.database} key=${c.apiKeyRef}`,
|
|
28161
28399
|
promptNew: promptConnection,
|
|
28162
28400
|
onFirst: (c) => ravendbSetConnection(c.name)
|
|
28163
28401
|
});
|
|
28164
28402
|
|
|
28165
28403
|
// src/commands/ravendb/ravendbCollections.ts
|
|
28166
|
-
import
|
|
28404
|
+
import chalk197 from "chalk";
|
|
28167
28405
|
|
|
28168
28406
|
// src/commands/ravendb/ravenFetch.ts
|
|
28169
|
-
import
|
|
28407
|
+
import chalk195 from "chalk";
|
|
28170
28408
|
|
|
28171
28409
|
// src/commands/ravendb/getAccessToken.ts
|
|
28172
28410
|
var OAUTH_URL = "https://amazon-useast-1-oauth.ravenhq.com/ApiKeys/OAuth/AccessToken";
|
|
@@ -28202,21 +28440,21 @@ ${errorText}`
|
|
|
28202
28440
|
}
|
|
28203
28441
|
|
|
28204
28442
|
// src/commands/ravendb/resolveOpSecret.ts
|
|
28205
|
-
import { execSync as
|
|
28206
|
-
import
|
|
28443
|
+
import { execSync as execSync55 } from "child_process";
|
|
28444
|
+
import chalk194 from "chalk";
|
|
28207
28445
|
function resolveOpSecret(reference) {
|
|
28208
28446
|
if (!reference.startsWith("op://")) {
|
|
28209
|
-
console.error(
|
|
28447
|
+
console.error(chalk194.red(`Invalid secret reference: must start with op://`));
|
|
28210
28448
|
process.exit(1);
|
|
28211
28449
|
}
|
|
28212
28450
|
try {
|
|
28213
|
-
return
|
|
28451
|
+
return execSync55(`op read "${reference}"`, {
|
|
28214
28452
|
encoding: "utf8",
|
|
28215
28453
|
stdio: ["pipe", "pipe", "pipe"]
|
|
28216
28454
|
}).trim();
|
|
28217
28455
|
} catch {
|
|
28218
28456
|
console.error(
|
|
28219
|
-
|
|
28457
|
+
chalk194.red(
|
|
28220
28458
|
"Failed to resolve secret reference. Ensure 1Password CLI is installed and you are signed in."
|
|
28221
28459
|
)
|
|
28222
28460
|
);
|
|
@@ -28243,7 +28481,7 @@ async function ravenFetch(connection, path91) {
|
|
|
28243
28481
|
if (!response.ok) {
|
|
28244
28482
|
const body = await response.text();
|
|
28245
28483
|
console.error(
|
|
28246
|
-
|
|
28484
|
+
chalk195.red(`RavenDB error: ${response.status} ${response.statusText}`)
|
|
28247
28485
|
);
|
|
28248
28486
|
console.error(body.substring(0, 500));
|
|
28249
28487
|
process.exit(1);
|
|
@@ -28252,7 +28490,7 @@ async function ravenFetch(connection, path91) {
|
|
|
28252
28490
|
}
|
|
28253
28491
|
|
|
28254
28492
|
// src/commands/ravendb/resolveConnection.ts
|
|
28255
|
-
import
|
|
28493
|
+
import chalk196 from "chalk";
|
|
28256
28494
|
function loadRavendb() {
|
|
28257
28495
|
const raw = loadGlobalConfigRaw();
|
|
28258
28496
|
const ravendb = raw.ravendb;
|
|
@@ -28266,7 +28504,7 @@ function resolveConnection(name) {
|
|
|
28266
28504
|
const connectionName = name ?? defaultConnection;
|
|
28267
28505
|
if (!connectionName) {
|
|
28268
28506
|
console.error(
|
|
28269
|
-
|
|
28507
|
+
chalk196.red(
|
|
28270
28508
|
"No connection specified and no default set. Use assist ravendb set-connection <name> or pass a connection name."
|
|
28271
28509
|
)
|
|
28272
28510
|
);
|
|
@@ -28274,7 +28512,7 @@ function resolveConnection(name) {
|
|
|
28274
28512
|
}
|
|
28275
28513
|
const connection = connections.find((c) => c.name === connectionName);
|
|
28276
28514
|
if (!connection) {
|
|
28277
|
-
console.error(
|
|
28515
|
+
console.error(chalk196.red(`Connection "${connectionName}" not found.`));
|
|
28278
28516
|
console.error(
|
|
28279
28517
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
28280
28518
|
);
|
|
@@ -28305,15 +28543,15 @@ async function ravendbCollections(connectionName) {
|
|
|
28305
28543
|
return;
|
|
28306
28544
|
}
|
|
28307
28545
|
for (const c of collections) {
|
|
28308
|
-
console.log(`${
|
|
28546
|
+
console.log(`${chalk197.bold(c.Name)} ${c.CountOfDocuments} docs`);
|
|
28309
28547
|
}
|
|
28310
28548
|
}
|
|
28311
28549
|
|
|
28312
28550
|
// src/commands/ravendb/ravendbQuery.ts
|
|
28313
|
-
import
|
|
28551
|
+
import chalk199 from "chalk";
|
|
28314
28552
|
|
|
28315
28553
|
// src/commands/ravendb/fetchAllPages.ts
|
|
28316
|
-
import
|
|
28554
|
+
import chalk198 from "chalk";
|
|
28317
28555
|
|
|
28318
28556
|
// src/commands/ravendb/buildQueryPath.ts
|
|
28319
28557
|
function buildQueryPath(opts) {
|
|
@@ -28351,7 +28589,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
28351
28589
|
allResults.push(...results);
|
|
28352
28590
|
start3 += results.length;
|
|
28353
28591
|
process.stderr.write(
|
|
28354
|
-
`\r${
|
|
28592
|
+
`\r${chalk198.dim(`Fetched ${allResults.length}/${totalResults}`)}`
|
|
28355
28593
|
);
|
|
28356
28594
|
if (start3 >= totalResults) break;
|
|
28357
28595
|
if (opts.limit !== void 0 && allResults.length >= opts.limit) break;
|
|
@@ -28366,7 +28604,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
28366
28604
|
async function ravendbQuery(connectionName, collection, options2) {
|
|
28367
28605
|
const resolved = resolveArgs(connectionName, collection);
|
|
28368
28606
|
if (!resolved.collection && !options2.query) {
|
|
28369
|
-
console.error(
|
|
28607
|
+
console.error(chalk199.red("Provide a collection name or --query filter."));
|
|
28370
28608
|
process.exit(1);
|
|
28371
28609
|
}
|
|
28372
28610
|
const { collection: col } = resolved;
|
|
@@ -28405,7 +28643,7 @@ import { spawn as spawn6 } from "child_process";
|
|
|
28405
28643
|
import * as path46 from "path";
|
|
28406
28644
|
|
|
28407
28645
|
// src/commands/refactor/logViolations.ts
|
|
28408
|
-
import
|
|
28646
|
+
import chalk200 from "chalk";
|
|
28409
28647
|
var DEFAULT_MAX_LINES2 = 100;
|
|
28410
28648
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES2) {
|
|
28411
28649
|
if (violations.length === 0) {
|
|
@@ -28414,43 +28652,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES2) {
|
|
|
28414
28652
|
}
|
|
28415
28653
|
return;
|
|
28416
28654
|
}
|
|
28417
|
-
console.error(
|
|
28655
|
+
console.error(chalk200.red(`
|
|
28418
28656
|
Refactor check failed:
|
|
28419
28657
|
`));
|
|
28420
|
-
console.error(
|
|
28658
|
+
console.error(chalk200.red(` The following files exceed ${maxLines} lines:
|
|
28421
28659
|
`));
|
|
28422
28660
|
for (const violation of violations) {
|
|
28423
|
-
console.error(
|
|
28661
|
+
console.error(chalk200.red(` ${violation.file} (${violation.lines} lines)`));
|
|
28424
28662
|
}
|
|
28425
28663
|
console.error(
|
|
28426
|
-
|
|
28664
|
+
chalk200.yellow(
|
|
28427
28665
|
`
|
|
28428
28666
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
28429
28667
|
way to refactor it, ignore it with:
|
|
28430
28668
|
`
|
|
28431
28669
|
)
|
|
28432
28670
|
);
|
|
28433
|
-
console.error(
|
|
28671
|
+
console.error(chalk200.gray(` assist refactor ignore <file>
|
|
28434
28672
|
`));
|
|
28435
28673
|
if (process.env.CLAUDECODE) {
|
|
28436
|
-
console.error(
|
|
28674
|
+
console.error(chalk200.cyan(`
|
|
28437
28675
|
## Extracting Code to New Files
|
|
28438
28676
|
`));
|
|
28439
28677
|
console.error(
|
|
28440
|
-
|
|
28678
|
+
chalk200.cyan(
|
|
28441
28679
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
28442
28680
|
`
|
|
28443
28681
|
)
|
|
28444
28682
|
);
|
|
28445
28683
|
console.error(
|
|
28446
|
-
|
|
28684
|
+
chalk200.cyan(
|
|
28447
28685
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
28448
28686
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
28449
28687
|
`
|
|
28450
28688
|
)
|
|
28451
28689
|
);
|
|
28452
28690
|
console.error(
|
|
28453
|
-
|
|
28691
|
+
chalk200.cyan(
|
|
28454
28692
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
28455
28693
|
domains, move it to a common/shared folder.
|
|
28456
28694
|
`
|
|
@@ -28460,7 +28698,7 @@ Refactor check failed:
|
|
|
28460
28698
|
}
|
|
28461
28699
|
|
|
28462
28700
|
// src/commands/refactor/check/getViolations/index.ts
|
|
28463
|
-
import { execSync as
|
|
28701
|
+
import { execSync as execSync56 } from "child_process";
|
|
28464
28702
|
import fs31 from "fs";
|
|
28465
28703
|
import { minimatch as minimatch6 } from "minimatch";
|
|
28466
28704
|
|
|
@@ -28510,7 +28748,7 @@ function getGitFiles(options2) {
|
|
|
28510
28748
|
}
|
|
28511
28749
|
const files = /* @__PURE__ */ new Set();
|
|
28512
28750
|
if (options2.staged || options2.modified) {
|
|
28513
|
-
const staged =
|
|
28751
|
+
const staged = execSync56("git diff --cached --name-only", {
|
|
28514
28752
|
encoding: "utf8"
|
|
28515
28753
|
});
|
|
28516
28754
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -28518,7 +28756,7 @@ function getGitFiles(options2) {
|
|
|
28518
28756
|
}
|
|
28519
28757
|
}
|
|
28520
28758
|
if (options2.unstaged || options2.modified) {
|
|
28521
|
-
const unstaged =
|
|
28759
|
+
const unstaged = execSync56("git diff --name-only", { encoding: "utf8" });
|
|
28522
28760
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
28523
28761
|
files.add(file);
|
|
28524
28762
|
}
|
|
@@ -28606,7 +28844,7 @@ async function check(pattern2, options2) {
|
|
|
28606
28844
|
|
|
28607
28845
|
// src/commands/refactor/extract/index.ts
|
|
28608
28846
|
import path54 from "path";
|
|
28609
|
-
import
|
|
28847
|
+
import chalk203 from "chalk";
|
|
28610
28848
|
|
|
28611
28849
|
// src/commands/refactor/extract/applyExtraction.ts
|
|
28612
28850
|
import { SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
@@ -29205,23 +29443,23 @@ function buildPlan2(functionName, sourceFile, sourcePath, destPath, project) {
|
|
|
29205
29443
|
|
|
29206
29444
|
// src/commands/refactor/extract/displayPlan.ts
|
|
29207
29445
|
import path50 from "path";
|
|
29208
|
-
import
|
|
29446
|
+
import chalk201 from "chalk";
|
|
29209
29447
|
function section(title) {
|
|
29210
29448
|
return `
|
|
29211
|
-
${
|
|
29449
|
+
${chalk201.cyan(title)}`;
|
|
29212
29450
|
}
|
|
29213
29451
|
function displayImporters(plan2, cwd) {
|
|
29214
29452
|
if (plan2.importersToUpdate.length === 0) return;
|
|
29215
29453
|
console.log(section("Update importers:"));
|
|
29216
29454
|
for (const imp of plan2.importersToUpdate) {
|
|
29217
29455
|
const rel = path50.relative(cwd, imp.file.getFilePath());
|
|
29218
|
-
console.log(` ${
|
|
29456
|
+
console.log(` ${chalk201.dim(rel)}: \u2192 import from "${imp.relPath}"`);
|
|
29219
29457
|
}
|
|
29220
29458
|
}
|
|
29221
29459
|
function displayPlan(functionName, relDest, plan2, cwd) {
|
|
29222
|
-
console.log(
|
|
29460
|
+
console.log(chalk201.bold(`Extract: ${functionName} \u2192 ${relDest}
|
|
29223
29461
|
`));
|
|
29224
|
-
console.log(` ${
|
|
29462
|
+
console.log(` ${chalk201.cyan("Functions to move:")}`);
|
|
29225
29463
|
for (const name of plan2.extractedNames) {
|
|
29226
29464
|
console.log(` ${name}`);
|
|
29227
29465
|
}
|
|
@@ -29255,7 +29493,7 @@ function displayPlan(functionName, relDest, plan2, cwd) {
|
|
|
29255
29493
|
|
|
29256
29494
|
// src/commands/refactor/extract/loadProjectFile.ts
|
|
29257
29495
|
import path53 from "path";
|
|
29258
|
-
import
|
|
29496
|
+
import chalk202 from "chalk";
|
|
29259
29497
|
import { Project as Project4 } from "ts-morph";
|
|
29260
29498
|
|
|
29261
29499
|
// src/commands/refactor/extract/findTsConfig.ts
|
|
@@ -29347,7 +29585,7 @@ function loadProjectFile(file) {
|
|
|
29347
29585
|
});
|
|
29348
29586
|
const sourceFile = project.getSourceFile(sourcePath);
|
|
29349
29587
|
if (!sourceFile) {
|
|
29350
|
-
console.log(
|
|
29588
|
+
console.log(chalk202.red(`File not found in project: ${file}`));
|
|
29351
29589
|
process.exit(1);
|
|
29352
29590
|
}
|
|
29353
29591
|
return { project, sourceFile };
|
|
@@ -29370,19 +29608,19 @@ async function extract(file, functionName, destination, options2 = {}) {
|
|
|
29370
29608
|
displayPlan(functionName, relDest, plan2, cwd);
|
|
29371
29609
|
if (options2.apply) {
|
|
29372
29610
|
await applyExtraction(functionName, sourceFile, destPath, plan2, project);
|
|
29373
|
-
console.log(
|
|
29611
|
+
console.log(chalk203.green("\nExtraction complete"));
|
|
29374
29612
|
} else {
|
|
29375
|
-
console.log(
|
|
29613
|
+
console.log(chalk203.dim("\nDry run. Use --apply to execute."));
|
|
29376
29614
|
}
|
|
29377
29615
|
}
|
|
29378
29616
|
|
|
29379
29617
|
// src/commands/refactor/ignore.ts
|
|
29380
29618
|
import fs34 from "fs";
|
|
29381
|
-
import
|
|
29619
|
+
import chalk204 from "chalk";
|
|
29382
29620
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
29383
29621
|
function ignore2(file) {
|
|
29384
29622
|
if (!fs34.existsSync(file)) {
|
|
29385
|
-
console.error(
|
|
29623
|
+
console.error(chalk204.red(`Error: File does not exist: ${file}`));
|
|
29386
29624
|
process.exit(1);
|
|
29387
29625
|
}
|
|
29388
29626
|
const content = fs34.readFileSync(file, "utf8");
|
|
@@ -29398,7 +29636,7 @@ function ignore2(file) {
|
|
|
29398
29636
|
fs34.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
29399
29637
|
}
|
|
29400
29638
|
console.log(
|
|
29401
|
-
|
|
29639
|
+
chalk204.green(
|
|
29402
29640
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
29403
29641
|
)
|
|
29404
29642
|
);
|
|
@@ -29407,12 +29645,12 @@ function ignore2(file) {
|
|
|
29407
29645
|
// src/commands/refactor/rename/index.ts
|
|
29408
29646
|
import fs37 from "fs";
|
|
29409
29647
|
import path59 from "path";
|
|
29410
|
-
import
|
|
29648
|
+
import chalk207 from "chalk";
|
|
29411
29649
|
|
|
29412
29650
|
// src/commands/refactor/rename/applyRename.ts
|
|
29413
29651
|
import fs36 from "fs";
|
|
29414
29652
|
import path56 from "path";
|
|
29415
|
-
import
|
|
29653
|
+
import chalk205 from "chalk";
|
|
29416
29654
|
|
|
29417
29655
|
// src/commands/refactor/restructure/computeRewrites/index.ts
|
|
29418
29656
|
import path55 from "path";
|
|
@@ -29517,13 +29755,13 @@ function applyRename(rewrites, sourcePath, destPath, cwd) {
|
|
|
29517
29755
|
const updatedContents = applyRewrites(rewrites);
|
|
29518
29756
|
for (const [file, content] of updatedContents) {
|
|
29519
29757
|
fs36.writeFileSync(file, content, "utf8");
|
|
29520
|
-
console.log(
|
|
29758
|
+
console.log(chalk205.cyan(` Updated imports in ${path56.relative(cwd, file)}`));
|
|
29521
29759
|
}
|
|
29522
29760
|
const destDir = path56.dirname(destPath);
|
|
29523
29761
|
if (!fs36.existsSync(destDir)) fs36.mkdirSync(destDir, { recursive: true });
|
|
29524
29762
|
fs36.renameSync(sourcePath, destPath);
|
|
29525
29763
|
console.log(
|
|
29526
|
-
|
|
29764
|
+
chalk205.white(
|
|
29527
29765
|
` Moved ${path56.relative(cwd, sourcePath)} \u2192 ${path56.relative(cwd, destPath)}`
|
|
29528
29766
|
)
|
|
29529
29767
|
);
|
|
@@ -29610,16 +29848,16 @@ function computeRenameRewrites(sourcePath, destPath) {
|
|
|
29610
29848
|
|
|
29611
29849
|
// src/commands/refactor/rename/printRenamePreview.ts
|
|
29612
29850
|
import path58 from "path";
|
|
29613
|
-
import
|
|
29851
|
+
import chalk206 from "chalk";
|
|
29614
29852
|
function printRenamePreview(rewrites, cwd) {
|
|
29615
29853
|
for (const rewrite of rewrites) {
|
|
29616
29854
|
console.log(
|
|
29617
|
-
|
|
29855
|
+
chalk206.dim(
|
|
29618
29856
|
` ${path58.relative(cwd, rewrite.file)}: ${rewrite.oldSpecifier} \u2192 ${rewrite.newSpecifier}`
|
|
29619
29857
|
)
|
|
29620
29858
|
);
|
|
29621
29859
|
}
|
|
29622
|
-
console.log(
|
|
29860
|
+
console.log(chalk206.dim("Dry run. Use --apply to execute."));
|
|
29623
29861
|
}
|
|
29624
29862
|
|
|
29625
29863
|
// src/commands/refactor/rename/index.ts
|
|
@@ -29630,20 +29868,20 @@ async function rename(source, destination, options2 = {}) {
|
|
|
29630
29868
|
const relSource = path59.relative(cwd, sourcePath);
|
|
29631
29869
|
const relDest = path59.relative(cwd, destPath);
|
|
29632
29870
|
if (!fs37.existsSync(sourcePath)) {
|
|
29633
|
-
console.log(
|
|
29871
|
+
console.log(chalk207.red(`File not found: ${source}`));
|
|
29634
29872
|
process.exit(1);
|
|
29635
29873
|
}
|
|
29636
29874
|
if (destPath !== sourcePath && fs37.existsSync(destPath)) {
|
|
29637
|
-
console.log(
|
|
29875
|
+
console.log(chalk207.red(`Destination already exists: ${destination}`));
|
|
29638
29876
|
process.exit(1);
|
|
29639
29877
|
}
|
|
29640
|
-
console.log(
|
|
29641
|
-
console.log(
|
|
29642
|
-
console.log(
|
|
29878
|
+
console.log(chalk207.bold(`Rename: ${relSource} \u2192 ${relDest}`));
|
|
29879
|
+
console.log(chalk207.dim("Loading project..."));
|
|
29880
|
+
console.log(chalk207.dim("Scanning imports across the project..."));
|
|
29643
29881
|
const rewrites = computeRenameRewrites(sourcePath, destPath);
|
|
29644
29882
|
const affectedFiles = new Set(rewrites.map((r) => r.file)).size;
|
|
29645
29883
|
console.log(
|
|
29646
|
-
|
|
29884
|
+
chalk207.dim(
|
|
29647
29885
|
`${rewrites.length} import path(s) to update across ${affectedFiles} file(s)`
|
|
29648
29886
|
)
|
|
29649
29887
|
);
|
|
@@ -29652,11 +29890,11 @@ async function rename(source, destination, options2 = {}) {
|
|
|
29652
29890
|
return;
|
|
29653
29891
|
}
|
|
29654
29892
|
applyRename(rewrites, sourcePath, destPath, cwd);
|
|
29655
|
-
console.log(
|
|
29893
|
+
console.log(chalk207.green("Done"));
|
|
29656
29894
|
}
|
|
29657
29895
|
|
|
29658
29896
|
// src/commands/refactor/renameSymbol/index.ts
|
|
29659
|
-
import
|
|
29897
|
+
import chalk208 from "chalk";
|
|
29660
29898
|
|
|
29661
29899
|
// src/commands/refactor/renameSymbol/findSymbol.ts
|
|
29662
29900
|
import { SyntaxKind as SyntaxKind15 } from "ts-morph";
|
|
@@ -29702,33 +29940,33 @@ async function renameSymbol(file, oldName, newName, options2 = {}) {
|
|
|
29702
29940
|
const { project, sourceFile } = loadProjectFile(file);
|
|
29703
29941
|
const symbol = findSymbol(sourceFile, oldName);
|
|
29704
29942
|
if (!symbol) {
|
|
29705
|
-
console.log(
|
|
29943
|
+
console.log(chalk208.red(`Symbol "${oldName}" not found in ${file}`));
|
|
29706
29944
|
process.exit(1);
|
|
29707
29945
|
}
|
|
29708
29946
|
const grouped = groupReferences(symbol, cwd);
|
|
29709
29947
|
const totalRefs = [...grouped.values()].reduce((s, l) => s + l.length, 0);
|
|
29710
29948
|
console.log(
|
|
29711
|
-
|
|
29949
|
+
chalk208.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
|
|
29712
29950
|
`)
|
|
29713
29951
|
);
|
|
29714
29952
|
for (const [refFile, lines2] of grouped) {
|
|
29715
29953
|
console.log(
|
|
29716
|
-
` ${
|
|
29954
|
+
` ${chalk208.dim(refFile)}: lines ${chalk208.cyan(lines2.join(", "))}`
|
|
29717
29955
|
);
|
|
29718
29956
|
}
|
|
29719
29957
|
if (options2.apply) {
|
|
29720
29958
|
symbol.rename(newName);
|
|
29721
29959
|
await project.save();
|
|
29722
|
-
console.log(
|
|
29960
|
+
console.log(chalk208.green(`
|
|
29723
29961
|
Renamed ${oldName} \u2192 ${newName}`));
|
|
29724
29962
|
} else {
|
|
29725
|
-
console.log(
|
|
29963
|
+
console.log(chalk208.dim("\nDry run. Use --apply to execute."));
|
|
29726
29964
|
}
|
|
29727
29965
|
}
|
|
29728
29966
|
|
|
29729
29967
|
// src/commands/refactor/restructure/index.ts
|
|
29730
29968
|
import path67 from "path";
|
|
29731
|
-
import
|
|
29969
|
+
import chalk211 from "chalk";
|
|
29732
29970
|
|
|
29733
29971
|
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
29734
29972
|
import path61 from "path";
|
|
@@ -29807,50 +30045,50 @@ function clusterFiles(graph) {
|
|
|
29807
30045
|
|
|
29808
30046
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
29809
30047
|
import path63 from "path";
|
|
29810
|
-
import
|
|
30048
|
+
import chalk209 from "chalk";
|
|
29811
30049
|
function relPath(filePath) {
|
|
29812
30050
|
return path63.relative(process.cwd(), filePath);
|
|
29813
30051
|
}
|
|
29814
30052
|
function displayMoves(plan2) {
|
|
29815
30053
|
if (plan2.moves.length === 0) return;
|
|
29816
|
-
console.log(
|
|
30054
|
+
console.log(chalk209.bold("\nFile moves:"));
|
|
29817
30055
|
for (const move2 of plan2.moves) {
|
|
29818
30056
|
console.log(
|
|
29819
|
-
` ${
|
|
30057
|
+
` ${chalk209.red(relPath(move2.from))} \u2192 ${chalk209.green(relPath(move2.to))}`
|
|
29820
30058
|
);
|
|
29821
|
-
console.log(
|
|
30059
|
+
console.log(chalk209.dim(` ${move2.reason}`));
|
|
29822
30060
|
}
|
|
29823
30061
|
}
|
|
29824
30062
|
function displayRewrites(rewrites) {
|
|
29825
30063
|
if (rewrites.length === 0) return;
|
|
29826
30064
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
29827
|
-
console.log(
|
|
30065
|
+
console.log(chalk209.bold(`
|
|
29828
30066
|
Import rewrites (${affectedFiles.size} files):`));
|
|
29829
30067
|
for (const file of affectedFiles) {
|
|
29830
|
-
console.log(` ${
|
|
30068
|
+
console.log(` ${chalk209.cyan(relPath(file))}:`);
|
|
29831
30069
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
29832
30070
|
(r) => r.file === file
|
|
29833
30071
|
)) {
|
|
29834
30072
|
console.log(
|
|
29835
|
-
` ${
|
|
30073
|
+
` ${chalk209.red(`"${oldSpecifier}"`)} \u2192 ${chalk209.green(`"${newSpecifier}"`)}`
|
|
29836
30074
|
);
|
|
29837
30075
|
}
|
|
29838
30076
|
}
|
|
29839
30077
|
}
|
|
29840
30078
|
function displayPlan2(plan2) {
|
|
29841
30079
|
if (plan2.warnings.length > 0) {
|
|
29842
|
-
console.log(
|
|
29843
|
-
for (const w of plan2.warnings) console.log(
|
|
30080
|
+
console.log(chalk209.yellow("\nWarnings:"));
|
|
30081
|
+
for (const w of plan2.warnings) console.log(chalk209.yellow(` ${w}`));
|
|
29844
30082
|
}
|
|
29845
30083
|
if (plan2.newDirectories.length > 0) {
|
|
29846
|
-
console.log(
|
|
30084
|
+
console.log(chalk209.bold("\nNew directories:"));
|
|
29847
30085
|
for (const dir of plan2.newDirectories)
|
|
29848
|
-
console.log(
|
|
30086
|
+
console.log(chalk209.green(` ${dir}/`));
|
|
29849
30087
|
}
|
|
29850
30088
|
displayMoves(plan2);
|
|
29851
30089
|
displayRewrites(plan2.rewrites);
|
|
29852
30090
|
console.log(
|
|
29853
|
-
|
|
30091
|
+
chalk209.dim(
|
|
29854
30092
|
`
|
|
29855
30093
|
Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports rewritten`
|
|
29856
30094
|
)
|
|
@@ -29860,18 +30098,18 @@ Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports r
|
|
|
29860
30098
|
// src/commands/refactor/restructure/executePlan.ts
|
|
29861
30099
|
import fs38 from "fs";
|
|
29862
30100
|
import path64 from "path";
|
|
29863
|
-
import
|
|
30101
|
+
import chalk210 from "chalk";
|
|
29864
30102
|
function executePlan(plan2) {
|
|
29865
30103
|
const updatedContents = applyRewrites(plan2.rewrites);
|
|
29866
30104
|
for (const [file, content] of updatedContents) {
|
|
29867
30105
|
fs38.writeFileSync(file, content, "utf8");
|
|
29868
30106
|
console.log(
|
|
29869
|
-
|
|
30107
|
+
chalk210.cyan(` Rewrote imports in ${path64.relative(process.cwd(), file)}`)
|
|
29870
30108
|
);
|
|
29871
30109
|
}
|
|
29872
30110
|
for (const dir of plan2.newDirectories) {
|
|
29873
30111
|
fs38.mkdirSync(dir, { recursive: true });
|
|
29874
|
-
console.log(
|
|
30112
|
+
console.log(chalk210.green(` Created ${path64.relative(process.cwd(), dir)}/`));
|
|
29875
30113
|
}
|
|
29876
30114
|
for (const move2 of plan2.moves) {
|
|
29877
30115
|
const targetDir = path64.dirname(move2.to);
|
|
@@ -29880,7 +30118,7 @@ function executePlan(plan2) {
|
|
|
29880
30118
|
}
|
|
29881
30119
|
fs38.renameSync(move2.from, move2.to);
|
|
29882
30120
|
console.log(
|
|
29883
|
-
|
|
30121
|
+
chalk210.white(
|
|
29884
30122
|
` Moved ${path64.relative(process.cwd(), move2.from)} \u2192 ${path64.relative(process.cwd(), move2.to)}`
|
|
29885
30123
|
)
|
|
29886
30124
|
);
|
|
@@ -29895,7 +30133,7 @@ function removeEmptyDirectories(dirs) {
|
|
|
29895
30133
|
if (entries.length === 0) {
|
|
29896
30134
|
fs38.rmdirSync(dir);
|
|
29897
30135
|
console.log(
|
|
29898
|
-
|
|
30136
|
+
chalk210.dim(
|
|
29899
30137
|
` Removed empty directory ${path64.relative(process.cwd(), dir)}`
|
|
29900
30138
|
)
|
|
29901
30139
|
);
|
|
@@ -30028,22 +30266,22 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
30028
30266
|
const targetPattern = pattern2 ?? "src";
|
|
30029
30267
|
const files = findSourceFiles2(targetPattern);
|
|
30030
30268
|
if (files.length === 0) {
|
|
30031
|
-
console.log(
|
|
30269
|
+
console.log(chalk211.yellow("No files found matching pattern"));
|
|
30032
30270
|
return;
|
|
30033
30271
|
}
|
|
30034
30272
|
const tsConfigPath = findTsConfig(path67.resolve(files[0]));
|
|
30035
30273
|
const plan2 = buildPlan3(files, tsConfigPath);
|
|
30036
30274
|
if (plan2.moves.length === 0) {
|
|
30037
|
-
console.log(
|
|
30275
|
+
console.log(chalk211.green("No restructuring needed"));
|
|
30038
30276
|
return;
|
|
30039
30277
|
}
|
|
30040
30278
|
displayPlan2(plan2);
|
|
30041
30279
|
if (options2.apply) {
|
|
30042
|
-
console.log(
|
|
30280
|
+
console.log(chalk211.bold("\nApplying changes..."));
|
|
30043
30281
|
executePlan(plan2);
|
|
30044
|
-
console.log(
|
|
30282
|
+
console.log(chalk211.green("\nRestructuring complete"));
|
|
30045
30283
|
} else {
|
|
30046
|
-
console.log(
|
|
30284
|
+
console.log(chalk211.dim("\nDry run. Use --apply to execute."));
|
|
30047
30285
|
}
|
|
30048
30286
|
}
|
|
30049
30287
|
|
|
@@ -30096,6 +30334,293 @@ async function checkoutOnlySession(number) {
|
|
|
30096
30334
|
await done2;
|
|
30097
30335
|
}
|
|
30098
30336
|
|
|
30337
|
+
// src/commands/review/highLevel/runHighLevelReview.ts
|
|
30338
|
+
import chalk213 from "chalk";
|
|
30339
|
+
|
|
30340
|
+
// src/commands/review/fetchPrDiffInfo.ts
|
|
30341
|
+
import { execSync as execSync57 } from "child_process";
|
|
30342
|
+
function getCurrentBranch3() {
|
|
30343
|
+
return execSync57("git rev-parse --abbrev-ref HEAD", {
|
|
30344
|
+
encoding: "utf8"
|
|
30345
|
+
}).trim();
|
|
30346
|
+
}
|
|
30347
|
+
function fetchPrDiffInfo() {
|
|
30348
|
+
const { org, repo } = getRepoInfo();
|
|
30349
|
+
const branch2 = getCurrentBranch3();
|
|
30350
|
+
const fields = "number,baseRefName,baseRefOid,headRefName,headRefOid";
|
|
30351
|
+
const raw = execSync57(
|
|
30352
|
+
`gh pr list --state open --head ${branch2} --json ${fields} -R ${org}/${repo}`,
|
|
30353
|
+
{
|
|
30354
|
+
encoding: "utf8",
|
|
30355
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
30356
|
+
}
|
|
30357
|
+
);
|
|
30358
|
+
const parsed = JSON.parse(raw);
|
|
30359
|
+
const pr = parsed[0];
|
|
30360
|
+
if (!pr) {
|
|
30361
|
+
console.error(
|
|
30362
|
+
`Error: No open pull request found for branch \`${branch2}\`. Open a PR for this branch before running \`assist review\`.`
|
|
30363
|
+
);
|
|
30364
|
+
process.exit(1);
|
|
30365
|
+
}
|
|
30366
|
+
return {
|
|
30367
|
+
prNumber: pr.number,
|
|
30368
|
+
baseRef: pr.baseRefName,
|
|
30369
|
+
baseSha: pr.baseRefOid,
|
|
30370
|
+
headRef: pr.headRefName,
|
|
30371
|
+
headSha: pr.headRefOid
|
|
30372
|
+
};
|
|
30373
|
+
}
|
|
30374
|
+
function fetchPrChangedFiles(prNumber) {
|
|
30375
|
+
const { org, repo } = getRepoInfo();
|
|
30376
|
+
const out = execSync57(
|
|
30377
|
+
`gh api repos/${org}/${repo}/pulls/${prNumber}/files --paginate --jq ".[].filename"`,
|
|
30378
|
+
{
|
|
30379
|
+
encoding: "utf8",
|
|
30380
|
+
maxBuffer: 64 * 1024 * 1024
|
|
30381
|
+
}
|
|
30382
|
+
);
|
|
30383
|
+
return out.trim().split("\n").filter(Boolean);
|
|
30384
|
+
}
|
|
30385
|
+
|
|
30386
|
+
// src/commands/review/highLevel/checkDescriptionLinksIssue.ts
|
|
30387
|
+
var ISSUE_URL_PATTERN = /https?:\/\/github\.com\/[\w.-]+\/[\w.-]+\/issues\/\d+/g;
|
|
30388
|
+
var CROSS_REPO_PATTERN = /\b[\w.-]+\/[\w.-]+#\d+/g;
|
|
30389
|
+
var SAME_REPO_PATTERN2 = /(?<![\w/#-])#\d+\b/g;
|
|
30390
|
+
function firstMatch(body, pattern2) {
|
|
30391
|
+
return body.match(pattern2)?.[0] ?? null;
|
|
30392
|
+
}
|
|
30393
|
+
function checkDescriptionLinksIssue(body) {
|
|
30394
|
+
const reference = firstMatch(body, ISSUE_URL_PATTERN) ?? firstMatch(body, CROSS_REPO_PATTERN) ?? firstMatch(body, SAME_REPO_PATTERN2);
|
|
30395
|
+
if (!reference)
|
|
30396
|
+
return {
|
|
30397
|
+
status: "fail",
|
|
30398
|
+
reason: "no GitHub issue reference (`#123`, `owner/repo#123`, or an issue URL) in the description"
|
|
30399
|
+
};
|
|
30400
|
+
return { status: "pass", reason: `links ${reference}` };
|
|
30401
|
+
}
|
|
30402
|
+
|
|
30403
|
+
// src/commands/review/highLevel/checkDescriptionSections.ts
|
|
30404
|
+
function sectionContent(body, heading2) {
|
|
30405
|
+
const section2 = parsePrBody(body).find(
|
|
30406
|
+
(candidate) => candidate.heading.trim().toLowerCase() === heading2
|
|
30407
|
+
);
|
|
30408
|
+
return section2 ? section2.content.trim() : null;
|
|
30409
|
+
}
|
|
30410
|
+
function describe5(heading2, content) {
|
|
30411
|
+
if (content === null) return `no \`## ${heading2}\` section`;
|
|
30412
|
+
if (content === "") return `\`## ${heading2}\` is empty`;
|
|
30413
|
+
return null;
|
|
30414
|
+
}
|
|
30415
|
+
function checkDescriptionSections(body) {
|
|
30416
|
+
const problems = [
|
|
30417
|
+
describe5("What", sectionContent(body, "what")),
|
|
30418
|
+
describe5("Why", sectionContent(body, "why"))
|
|
30419
|
+
].filter((problem) => problem !== null);
|
|
30420
|
+
if (problems.length > 0)
|
|
30421
|
+
return { status: "fail", reason: problems.join("; ") };
|
|
30422
|
+
const how = sectionContent(body, "how");
|
|
30423
|
+
const optional = how ? ", plus an optional `## How`" : "";
|
|
30424
|
+
return {
|
|
30425
|
+
status: "pass",
|
|
30426
|
+
reason: `\`## What\` and \`## Why\` are both present${optional}`
|
|
30427
|
+
};
|
|
30428
|
+
}
|
|
30429
|
+
|
|
30430
|
+
// src/commands/review/highLevel/checkDescriptionWordCap.ts
|
|
30431
|
+
function checkDescriptionWordCap(body, cap2) {
|
|
30432
|
+
const { prose, code } = countReadingWords(body);
|
|
30433
|
+
const words = prose + code;
|
|
30434
|
+
if (words > cap2)
|
|
30435
|
+
return {
|
|
30436
|
+
status: "fail",
|
|
30437
|
+
reason: `${words} words, ${words - cap2} over the ${cap2}-word cap`
|
|
30438
|
+
};
|
|
30439
|
+
return { status: "pass", reason: `${words} of ${cap2} words` };
|
|
30440
|
+
}
|
|
30441
|
+
|
|
30442
|
+
// src/commands/review/highLevel/checkUiEvidence.ts
|
|
30443
|
+
import { minimatch as minimatch7 } from "minimatch";
|
|
30444
|
+
var EVIDENCE_PATTERNS = [
|
|
30445
|
+
/!\[[^\]]*\]\([^)]+\)/g,
|
|
30446
|
+
/<(?:img|video)\b[^>]*>/gi,
|
|
30447
|
+
/https?:\/\/github\.com\/user-attachments\/assets\/\S+/g,
|
|
30448
|
+
/https?:\/\/\S+\.(?:png|jpe?g|gif|webp|avif|svg|mp4|mov|webm)\b/gi
|
|
30449
|
+
];
|
|
30450
|
+
function countEvidence(body) {
|
|
30451
|
+
return EVIDENCE_PATTERNS.reduce(
|
|
30452
|
+
(total, pattern2) => total + (body.match(pattern2)?.length ?? 0),
|
|
30453
|
+
0
|
|
30454
|
+
);
|
|
30455
|
+
}
|
|
30456
|
+
function plural(count8, noun) {
|
|
30457
|
+
return `${count8} ${noun}${count8 === 1 ? "" : "s"}`;
|
|
30458
|
+
}
|
|
30459
|
+
function checkUiEvidence(body, changedFiles2, uiPaths) {
|
|
30460
|
+
if (uiPaths.length === 0)
|
|
30461
|
+
return {
|
|
30462
|
+
status: "pass",
|
|
30463
|
+
reason: "review.highLevel.uiPaths is unset, so no UI evidence is required"
|
|
30464
|
+
};
|
|
30465
|
+
const touched = changedFiles2.filter(
|
|
30466
|
+
(file) => uiPaths.some((glob) => minimatch7(file, glob))
|
|
30467
|
+
);
|
|
30468
|
+
if (touched.length === 0)
|
|
30469
|
+
return {
|
|
30470
|
+
status: "pass",
|
|
30471
|
+
reason: "no changed file matches review.highLevel.uiPaths"
|
|
30472
|
+
};
|
|
30473
|
+
const evidence = countEvidence(body);
|
|
30474
|
+
if (evidence === 0)
|
|
30475
|
+
return {
|
|
30476
|
+
status: "fail",
|
|
30477
|
+
reason: `${plural(touched.length, "UI file")} changed (${touched[0]}) but no screenshot or video in the description`
|
|
30478
|
+
};
|
|
30479
|
+
const shown = evidence === 1 ? "1 screenshot or video" : `${evidence} screenshots or videos`;
|
|
30480
|
+
return {
|
|
30481
|
+
status: "pass",
|
|
30482
|
+
reason: `${shown} for ${plural(touched.length, "changed UI file")}`
|
|
30483
|
+
};
|
|
30484
|
+
}
|
|
30485
|
+
|
|
30486
|
+
// src/commands/review/highLevel/highLevelChecklist.ts
|
|
30487
|
+
var highLevelChecklist = [
|
|
30488
|
+
{
|
|
30489
|
+
id: "description-what-why",
|
|
30490
|
+
kind: "deterministic",
|
|
30491
|
+
title: "Description has a What and a Why",
|
|
30492
|
+
backing: "The PR body's `## What` and `## Why` sections"
|
|
30493
|
+
},
|
|
30494
|
+
{
|
|
30495
|
+
id: "description-word-cap",
|
|
30496
|
+
kind: "deterministic",
|
|
30497
|
+
title: "Description is under the word cap",
|
|
30498
|
+
backing: "The PR body's word count against review.highLevel.descriptionWordCap"
|
|
30499
|
+
},
|
|
30500
|
+
{
|
|
30501
|
+
id: "description-links-issue",
|
|
30502
|
+
kind: "deterministic",
|
|
30503
|
+
title: "Description links the GitHub issue the PR resolves",
|
|
30504
|
+
backing: "GitHub issue references in the PR body"
|
|
30505
|
+
},
|
|
30506
|
+
{
|
|
30507
|
+
id: "ui-evidence",
|
|
30508
|
+
kind: "deterministic",
|
|
30509
|
+
title: "UI changes are evidenced by a screenshot or video",
|
|
30510
|
+
backing: "Images and videos in the PR body, when a changed file matches review.highLevel.uiPaths"
|
|
30511
|
+
},
|
|
30512
|
+
{
|
|
30513
|
+
id: "structure-sensible",
|
|
30514
|
+
kind: "manual",
|
|
30515
|
+
title: "The structure of the change is sensible",
|
|
30516
|
+
backing: "The changed-file tree with add/delete/modify counts and per-file GitHub diff links"
|
|
30517
|
+
},
|
|
30518
|
+
{
|
|
30519
|
+
id: "critical-diffs-correct",
|
|
30520
|
+
kind: "manual",
|
|
30521
|
+
title: "The critical-file diffs are correct",
|
|
30522
|
+
backing: "Full diffs of files matching review.highLevel.criticalPaths"
|
|
30523
|
+
},
|
|
30524
|
+
{
|
|
30525
|
+
id: "backend-pr-linked",
|
|
30526
|
+
kind: "manual",
|
|
30527
|
+
title: "The backend PR is linked, if the change needs backend work",
|
|
30528
|
+
backing: "The PR body"
|
|
30529
|
+
}
|
|
30530
|
+
];
|
|
30531
|
+
|
|
30532
|
+
// src/commands/review/highLevel/evaluateHighLevelChecks.ts
|
|
30533
|
+
var evaluators = {
|
|
30534
|
+
"description-what-why": (subject) => checkDescriptionSections(subject.body),
|
|
30535
|
+
"description-word-cap": (subject) => checkDescriptionWordCap(subject.body, subject.config.descriptionWordCap),
|
|
30536
|
+
"description-links-issue": (subject) => checkDescriptionLinksIssue(subject.body),
|
|
30537
|
+
"ui-evidence": (subject) => checkUiEvidence(subject.body, subject.changedFiles, subject.config.uiPaths)
|
|
30538
|
+
};
|
|
30539
|
+
function evaluateHighLevelChecks(subject) {
|
|
30540
|
+
return highLevelChecklist.map((check2) => {
|
|
30541
|
+
if (check2.kind === "manual")
|
|
30542
|
+
return { ...check2, status: "manual", reason: check2.backing };
|
|
30543
|
+
return { ...check2, ...evaluators[check2.id](subject) };
|
|
30544
|
+
});
|
|
30545
|
+
}
|
|
30546
|
+
|
|
30547
|
+
// src/commands/review/highLevel/formatHighLevelChecklist.ts
|
|
30548
|
+
import chalk212 from "chalk";
|
|
30549
|
+
var MARKERS = {
|
|
30550
|
+
pass: () => chalk212.green("\u2714"),
|
|
30551
|
+
fail: () => chalk212.red("\u2718"),
|
|
30552
|
+
manual: () => chalk212.yellow("\u25A1")
|
|
30553
|
+
};
|
|
30554
|
+
function formatCheck(check2) {
|
|
30555
|
+
const title = check2.status === "fail" ? chalk212.red(check2.title) : chalk212.bold(check2.title);
|
|
30556
|
+
return ` ${MARKERS[check2.status]()} ${title}
|
|
30557
|
+
${chalk212.dim(check2.reason)}`;
|
|
30558
|
+
}
|
|
30559
|
+
function formatGroup(heading2, checks2) {
|
|
30560
|
+
if (checks2.length === 0) return [];
|
|
30561
|
+
return ["", chalk212.bold.underline(heading2), ...checks2.map(formatCheck)];
|
|
30562
|
+
}
|
|
30563
|
+
function formatHighLevelChecklist(checks2) {
|
|
30564
|
+
const deterministic = checks2.filter(
|
|
30565
|
+
(check2) => check2.kind === "deterministic"
|
|
30566
|
+
);
|
|
30567
|
+
const failed2 = deterministic.filter(
|
|
30568
|
+
(check2) => check2.status === "fail"
|
|
30569
|
+
).length;
|
|
30570
|
+
const summary = failed2 === 0 ? chalk212.green(`All ${deterministic.length} deterministic checks pass`) : chalk212.red(
|
|
30571
|
+
`${failed2} of ${deterministic.length} deterministic checks fail`
|
|
30572
|
+
);
|
|
30573
|
+
return [
|
|
30574
|
+
...formatGroup("Deterministic", deterministic),
|
|
30575
|
+
...formatGroup(
|
|
30576
|
+
"Manual",
|
|
30577
|
+
checks2.filter((check2) => check2.kind === "manual")
|
|
30578
|
+
),
|
|
30579
|
+
"",
|
|
30580
|
+
summary
|
|
30581
|
+
].join("\n");
|
|
30582
|
+
}
|
|
30583
|
+
|
|
30584
|
+
// src/commands/review/highLevel/resolveHighLevelConfig.ts
|
|
30585
|
+
var DEFAULT_DESCRIPTION_WORD_CAP = 300;
|
|
30586
|
+
function resolveHighLevelConfig(config) {
|
|
30587
|
+
const highLevel = config.review?.highLevel;
|
|
30588
|
+
return {
|
|
30589
|
+
criticalPaths: highLevel?.criticalPaths ?? [],
|
|
30590
|
+
uiPaths: highLevel?.uiPaths ?? [],
|
|
30591
|
+
descriptionWordCap: highLevel?.descriptionWordCap ?? DEFAULT_DESCRIPTION_WORD_CAP
|
|
30592
|
+
};
|
|
30593
|
+
}
|
|
30594
|
+
|
|
30595
|
+
// src/commands/review/highLevel/runHighLevelReview.ts
|
|
30596
|
+
function resolvePrNumber2(number) {
|
|
30597
|
+
if (number === void 0) return getCurrentPrNumber();
|
|
30598
|
+
const parsed = Number(number);
|
|
30599
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
30600
|
+
console.error(`Error: \`${number}\` is not a pull request number.`);
|
|
30601
|
+
process.exit(1);
|
|
30602
|
+
}
|
|
30603
|
+
return parsed;
|
|
30604
|
+
}
|
|
30605
|
+
function runHighLevelReview(number) {
|
|
30606
|
+
const prNumber = resolvePrNumber2(number);
|
|
30607
|
+
const { org, repo } = getRepoInfo();
|
|
30608
|
+
const config = resolveHighLevelConfig(loadConfig());
|
|
30609
|
+
const body = fetchPrBody(prNumber, { org, repo });
|
|
30610
|
+
const changedFiles2 = fetchPrChangedFiles(prNumber);
|
|
30611
|
+
const checks2 = evaluateHighLevelChecks({ body, changedFiles: changedFiles2, config });
|
|
30612
|
+
console.log(
|
|
30613
|
+
chalk213.bold(`High-level review of ${org}/${repo}#${prNumber}`),
|
|
30614
|
+
chalk213.dim(`\xB7 ${changedFiles2.length} changed files`)
|
|
30615
|
+
);
|
|
30616
|
+
console.log(formatHighLevelChecklist(checks2));
|
|
30617
|
+
console.log(
|
|
30618
|
+
chalk213.dim(
|
|
30619
|
+
"\nManual items are for the reviewer to judge; see docs/high-level-review.md."
|
|
30620
|
+
)
|
|
30621
|
+
);
|
|
30622
|
+
}
|
|
30623
|
+
|
|
30099
30624
|
// src/commands/review/annotateDiffWithLineNumbers.ts
|
|
30100
30625
|
var FILE_HEADER2 = /^\+\+\+ (?:b\/)?(.+)$/;
|
|
30101
30626
|
var HUNK_HEADER2 = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
|
|
@@ -30229,9 +30754,9 @@ function buildReviewPaths(repoRoot2, key) {
|
|
|
30229
30754
|
}
|
|
30230
30755
|
|
|
30231
30756
|
// src/commands/review/fetchExistingComments.ts
|
|
30232
|
-
import { execSync as
|
|
30757
|
+
import { execSync as execSync58 } from "child_process";
|
|
30233
30758
|
function fetchRawComments(org, repo, prNumber) {
|
|
30234
|
-
const out =
|
|
30759
|
+
const out = execSync58(
|
|
30235
30760
|
`gh api --paginate repos/${org}/${repo}/pulls/${prNumber}/comments`,
|
|
30236
30761
|
{ encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }
|
|
30237
30762
|
);
|
|
@@ -30262,14 +30787,14 @@ function fetchExistingComments() {
|
|
|
30262
30787
|
}
|
|
30263
30788
|
|
|
30264
30789
|
// src/commands/review/gatherContext.ts
|
|
30265
|
-
import { execSync as
|
|
30790
|
+
import { execSync as execSync60 } from "child_process";
|
|
30266
30791
|
|
|
30267
30792
|
// src/commands/review/fetchPrDiff.ts
|
|
30268
|
-
import { execSync as
|
|
30793
|
+
import { execSync as execSync59 } from "child_process";
|
|
30269
30794
|
function fetchPrDiff(prNumber, baseSha, headSha) {
|
|
30270
30795
|
const { org, repo } = getRepoInfo();
|
|
30271
30796
|
try {
|
|
30272
|
-
return
|
|
30797
|
+
return execSync59(`gh pr diff ${prNumber} -R ${org}/${repo}`, {
|
|
30273
30798
|
encoding: "utf8",
|
|
30274
30799
|
maxBuffer: 256 * 1024 * 1024,
|
|
30275
30800
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -30284,68 +30809,22 @@ function isDiffTooLarge(error) {
|
|
|
30284
30809
|
}
|
|
30285
30810
|
function fetchDiffViaGit(baseSha, headSha) {
|
|
30286
30811
|
try {
|
|
30287
|
-
|
|
30812
|
+
execSync59(`git fetch origin ${baseSha} ${headSha}`, { stdio: "ignore" });
|
|
30288
30813
|
} catch {
|
|
30289
30814
|
}
|
|
30290
|
-
return
|
|
30815
|
+
return execSync59(`git diff ${baseSha}...${headSha}`, {
|
|
30291
30816
|
encoding: "utf8",
|
|
30292
30817
|
maxBuffer: 256 * 1024 * 1024
|
|
30293
30818
|
});
|
|
30294
30819
|
}
|
|
30295
30820
|
|
|
30296
|
-
// src/commands/review/fetchPrDiffInfo.ts
|
|
30297
|
-
import { execSync as execSync58 } from "child_process";
|
|
30298
|
-
function getCurrentBranch3() {
|
|
30299
|
-
return execSync58("git rev-parse --abbrev-ref HEAD", {
|
|
30300
|
-
encoding: "utf8"
|
|
30301
|
-
}).trim();
|
|
30302
|
-
}
|
|
30303
|
-
function fetchPrDiffInfo() {
|
|
30304
|
-
const { org, repo } = getRepoInfo();
|
|
30305
|
-
const branch2 = getCurrentBranch3();
|
|
30306
|
-
const fields = "number,baseRefName,baseRefOid,headRefName,headRefOid";
|
|
30307
|
-
const raw = execSync58(
|
|
30308
|
-
`gh pr list --state open --head ${branch2} --json ${fields} -R ${org}/${repo}`,
|
|
30309
|
-
{
|
|
30310
|
-
encoding: "utf8",
|
|
30311
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
30312
|
-
}
|
|
30313
|
-
);
|
|
30314
|
-
const parsed = JSON.parse(raw);
|
|
30315
|
-
const pr = parsed[0];
|
|
30316
|
-
if (!pr) {
|
|
30317
|
-
console.error(
|
|
30318
|
-
`Error: No open pull request found for branch \`${branch2}\`. Open a PR for this branch before running \`assist review\`.`
|
|
30319
|
-
);
|
|
30320
|
-
process.exit(1);
|
|
30321
|
-
}
|
|
30322
|
-
return {
|
|
30323
|
-
prNumber: pr.number,
|
|
30324
|
-
baseRef: pr.baseRefName,
|
|
30325
|
-
baseSha: pr.baseRefOid,
|
|
30326
|
-
headRef: pr.headRefName,
|
|
30327
|
-
headSha: pr.headRefOid
|
|
30328
|
-
};
|
|
30329
|
-
}
|
|
30330
|
-
function fetchPrChangedFiles(prNumber) {
|
|
30331
|
-
const { org, repo } = getRepoInfo();
|
|
30332
|
-
const out = execSync58(
|
|
30333
|
-
`gh api repos/${org}/${repo}/pulls/${prNumber}/files --paginate --jq ".[].filename"`,
|
|
30334
|
-
{
|
|
30335
|
-
encoding: "utf8",
|
|
30336
|
-
maxBuffer: 64 * 1024 * 1024
|
|
30337
|
-
}
|
|
30338
|
-
);
|
|
30339
|
-
return out.trim().split("\n").filter(Boolean);
|
|
30340
|
-
}
|
|
30341
|
-
|
|
30342
30821
|
// src/commands/review/gatherContext.ts
|
|
30343
30822
|
function gatherContext() {
|
|
30344
|
-
const branch2 =
|
|
30823
|
+
const branch2 = execSync60("git rev-parse --abbrev-ref HEAD", {
|
|
30345
30824
|
encoding: "utf8"
|
|
30346
30825
|
}).trim();
|
|
30347
|
-
const sha =
|
|
30348
|
-
const shortSha =
|
|
30826
|
+
const sha = execSync60("git rev-parse HEAD", { encoding: "utf8" }).trim();
|
|
30827
|
+
const shortSha = execSync60("git rev-parse --short=7 HEAD", {
|
|
30349
30828
|
encoding: "utf8"
|
|
30350
30829
|
}).trim();
|
|
30351
30830
|
const prInfo = fetchPrDiffInfo();
|
|
@@ -30704,18 +31183,18 @@ function partitionFindingsByDiff(findings, index3) {
|
|
|
30704
31183
|
}
|
|
30705
31184
|
|
|
30706
31185
|
// src/commands/review/warnOutOfDiff.ts
|
|
30707
|
-
import
|
|
31186
|
+
import chalk214 from "chalk";
|
|
30708
31187
|
function warnOutOfDiff(outOfDiff) {
|
|
30709
31188
|
if (outOfDiff.length === 0) return;
|
|
30710
31189
|
console.warn(
|
|
30711
|
-
|
|
31190
|
+
chalk214.yellow(
|
|
30712
31191
|
`Moved ${outOfDiff.length} finding(s) whose lines fall outside the PR diff into the review body (GitHub cannot anchor a comment on these):`
|
|
30713
31192
|
)
|
|
30714
31193
|
);
|
|
30715
31194
|
for (const finding of outOfDiff) {
|
|
30716
31195
|
const range = finding.startLine !== void 0 ? `${finding.startLine}-${finding.line}` : `${finding.line}`;
|
|
30717
31196
|
console.warn(
|
|
30718
|
-
` ${
|
|
31197
|
+
` ${chalk214.yellow("\xB7")} ${finding.title} ${chalk214.dim(
|
|
30719
31198
|
`(${finding.file}:${range})`
|
|
30720
31199
|
)}`
|
|
30721
31200
|
);
|
|
@@ -30739,18 +31218,18 @@ function selectInDiffFindings(lineBound, prDiff) {
|
|
|
30739
31218
|
}
|
|
30740
31219
|
|
|
30741
31220
|
// src/commands/review/warnUnlocated.ts
|
|
30742
|
-
import
|
|
31221
|
+
import chalk215 from "chalk";
|
|
30743
31222
|
function warnUnlocated(unlocated) {
|
|
30744
31223
|
if (unlocated.length === 0) return;
|
|
30745
31224
|
console.warn(
|
|
30746
|
-
|
|
31225
|
+
chalk215.yellow(
|
|
30747
31226
|
`Moved ${unlocated.length} finding(s) without a parseable file:line into the review body:`
|
|
30748
31227
|
)
|
|
30749
31228
|
);
|
|
30750
31229
|
for (const finding of unlocated) {
|
|
30751
|
-
const where = finding.location ||
|
|
31230
|
+
const where = finding.location || chalk215.dim("missing");
|
|
30752
31231
|
console.warn(
|
|
30753
|
-
` ${
|
|
31232
|
+
` ${chalk215.yellow("\xB7")} ${finding.title} ${chalk215.dim(`(${where})`)}`
|
|
30754
31233
|
);
|
|
30755
31234
|
}
|
|
30756
31235
|
}
|
|
@@ -31340,9 +31819,9 @@ import { writeFileSync as writeFileSync39 } from "fs";
|
|
|
31340
31819
|
// src/commands/review/finaliseReviewerSpinner.ts
|
|
31341
31820
|
var SUMMARY_MAX_LEN = 80;
|
|
31342
31821
|
function summariseStderr(stderr) {
|
|
31343
|
-
const
|
|
31344
|
-
if (!
|
|
31345
|
-
const trimmed =
|
|
31822
|
+
const firstLine3 = stderr.split(/\r?\n/).find((l) => l.trim().length > 0);
|
|
31823
|
+
if (!firstLine3) return "";
|
|
31824
|
+
const trimmed = firstLine3.trim();
|
|
31346
31825
|
return trimmed.length > SUMMARY_MAX_LEN ? `${trimmed.slice(0, SUMMARY_MAX_LEN - 1)}\u2026` : trimmed;
|
|
31347
31826
|
}
|
|
31348
31827
|
function finaliseReviewerSpinner(spinner, outcome) {
|
|
@@ -32050,14 +32529,8 @@ async function reviewPr(repoRoot2, options2) {
|
|
|
32050
32529
|
console.log(`Done. Review folder: ${paths.reviewDir}`);
|
|
32051
32530
|
}
|
|
32052
32531
|
|
|
32053
|
-
// src/commands/review/
|
|
32054
|
-
function
|
|
32055
|
-
const repoRoot2 = findRepoRoot(process.cwd());
|
|
32056
|
-
if (repoRoot2) return repoRoot2;
|
|
32057
|
-
console.error("Error: not inside a git repository.");
|
|
32058
|
-
process.exit(1);
|
|
32059
|
-
}
|
|
32060
|
-
function validateOptions(options2) {
|
|
32532
|
+
// src/commands/review/validateReviewOptions.ts
|
|
32533
|
+
function validateReviewOptions(options2) {
|
|
32061
32534
|
if (options2.apply && options2.refine) {
|
|
32062
32535
|
console.error("Error: --apply cannot be combined with --refine.");
|
|
32063
32536
|
process.exit(1);
|
|
@@ -32069,6 +32542,7 @@ function validateOptions(options2) {
|
|
|
32069
32542
|
process.exit(1);
|
|
32070
32543
|
}
|
|
32071
32544
|
validateCheckoutOnly(options2);
|
|
32545
|
+
validateHighLevel(options2);
|
|
32072
32546
|
}
|
|
32073
32547
|
function validateCheckoutOnly(options2) {
|
|
32074
32548
|
if (!options2.checkoutOnly) return;
|
|
@@ -32083,13 +32557,31 @@ function validateCheckoutOnly(options2) {
|
|
|
32083
32557
|
process.exit(1);
|
|
32084
32558
|
}
|
|
32085
32559
|
}
|
|
32560
|
+
function validateHighLevel(options2) {
|
|
32561
|
+
if (!options2.highLevel) return;
|
|
32562
|
+
if (options2.refine || options2.apply || options2.backlog || options2.submit || options2.checkoutOnly) {
|
|
32563
|
+
console.error(
|
|
32564
|
+
"Error: --high-level cannot be combined with --refine, --apply, --backlog, --submit or --checkout-only."
|
|
32565
|
+
);
|
|
32566
|
+
process.exit(1);
|
|
32567
|
+
}
|
|
32568
|
+
}
|
|
32569
|
+
|
|
32570
|
+
// src/commands/review/review.ts
|
|
32571
|
+
function resolveRepoRoot() {
|
|
32572
|
+
const repoRoot2 = findRepoRoot(process.cwd());
|
|
32573
|
+
if (repoRoot2) return repoRoot2;
|
|
32574
|
+
console.error("Error: not inside a git repository.");
|
|
32575
|
+
process.exit(1);
|
|
32576
|
+
}
|
|
32086
32577
|
async function review(options2 = {}) {
|
|
32087
|
-
|
|
32578
|
+
validateReviewOptions(options2);
|
|
32088
32579
|
startReviewLog();
|
|
32089
32580
|
const invokedIn = resolveRepoRoot();
|
|
32090
32581
|
if (options2.checkoutOnly && options2.number)
|
|
32091
32582
|
return checkoutOnlySession(options2.number);
|
|
32092
32583
|
emitActivity({ kind: "command", name: "review" });
|
|
32584
|
+
if (options2.highLevel) return runHighLevelReview(options2.number);
|
|
32093
32585
|
if (!options2.number) return reviewPr(invokedIn, options2);
|
|
32094
32586
|
await checkoutPr(options2.number);
|
|
32095
32587
|
return reviewPr(resolveRepoRoot(), options2);
|
|
@@ -32123,6 +32615,9 @@ function registerReview(program2) {
|
|
|
32123
32615
|
).option(
|
|
32124
32616
|
"--checkout-only",
|
|
32125
32617
|
"Check the PR out and start an idle interactive Claude session in the checkout tree instead of reviewing; requires a PR number and cannot be combined with --refine, --apply, --backlog or --submit"
|
|
32618
|
+
).option(
|
|
32619
|
+
"--high-level",
|
|
32620
|
+
"Skip the LLM review; evaluate the high-level review checklist against the PR description and changed files and print each item as pass, fail or manual. Nothing is posted to GitHub; cannot be combined with --refine, --apply, --backlog, --submit or --checkout-only"
|
|
32126
32621
|
).option(
|
|
32127
32622
|
"--address-comments",
|
|
32128
32623
|
"After posting and submitting the review, start an Address Comments session (assist review-pr-comments <n>) for the PR; no-op when nothing was posted or the review was not submitted, and only inside an assist session"
|
|
@@ -32145,7 +32640,7 @@ function registerReview(program2) {
|
|
|
32145
32640
|
// src/commands/rules/addRule.ts
|
|
32146
32641
|
import { existsSync as existsSync68, readFileSync as readFileSync52, writeFileSync as writeFileSync41 } from "fs";
|
|
32147
32642
|
import path72 from "path";
|
|
32148
|
-
import
|
|
32643
|
+
import chalk216 from "chalk";
|
|
32149
32644
|
|
|
32150
32645
|
// src/commands/rules/insertRuleBullet.ts
|
|
32151
32646
|
function ruleBullet({ code, title, text: text18 }) {
|
|
@@ -32281,7 +32776,7 @@ function read2(file) {
|
|
|
32281
32776
|
function addRule(text18, options2) {
|
|
32282
32777
|
const rule = text18.trim();
|
|
32283
32778
|
if (rule === "") {
|
|
32284
|
-
console.error(
|
|
32779
|
+
console.error(chalk216.red("Rule text is required"));
|
|
32285
32780
|
process.exitCode = 1;
|
|
32286
32781
|
return;
|
|
32287
32782
|
}
|
|
@@ -32299,13 +32794,13 @@ function addRule(text18, options2) {
|
|
|
32299
32794
|
);
|
|
32300
32795
|
updateScopedRulesIndex(root);
|
|
32301
32796
|
console.log(
|
|
32302
|
-
`Added ${
|
|
32797
|
+
`Added ${chalk216.cyan(code)} to ${path72.relative(process.cwd(), target) || target}`
|
|
32303
32798
|
);
|
|
32304
32799
|
}
|
|
32305
32800
|
|
|
32306
32801
|
// src/commands/rules/indexRules.ts
|
|
32307
32802
|
import path73 from "path";
|
|
32308
|
-
import
|
|
32803
|
+
import chalk217 from "chalk";
|
|
32309
32804
|
function indexRules() {
|
|
32310
32805
|
const startDir = scopeDirectory(process.cwd());
|
|
32311
32806
|
const root = findRepoRoot(startDir) ?? startDir;
|
|
@@ -32313,24 +32808,24 @@ function indexRules() {
|
|
|
32313
32808
|
const rootFile = path73.relative(process.cwd(), path73.join(root, "CLAUDE.md"));
|
|
32314
32809
|
if (directories.length === 0) {
|
|
32315
32810
|
console.log(
|
|
32316
|
-
|
|
32811
|
+
chalk217.gray(`No directories carry their own \`## Rules\` under ${root}`)
|
|
32317
32812
|
);
|
|
32318
32813
|
return;
|
|
32319
32814
|
}
|
|
32320
32815
|
console.log(`Recorded in ${rootFile || "CLAUDE.md"}`);
|
|
32321
32816
|
for (const directory of directories)
|
|
32322
|
-
console.log(` ${
|
|
32817
|
+
console.log(` ${chalk217.cyan(directory)}`);
|
|
32323
32818
|
}
|
|
32324
32819
|
|
|
32325
32820
|
// src/commands/rules/listRules.ts
|
|
32326
32821
|
import path74 from "path";
|
|
32327
|
-
import
|
|
32822
|
+
import chalk218 from "chalk";
|
|
32328
32823
|
function listRules(target, options2 = {}) {
|
|
32329
32824
|
const resolved = path74.resolve(target ?? process.cwd());
|
|
32330
32825
|
const rules3 = readScopedRules(resolved);
|
|
32331
32826
|
if (rules3.length === 0) {
|
|
32332
32827
|
const label2 = path74.relative(process.cwd(), resolved) || ".";
|
|
32333
|
-
console.log(
|
|
32828
|
+
console.log(chalk218.gray(`No rules in scope for ${label2}`));
|
|
32334
32829
|
return;
|
|
32335
32830
|
}
|
|
32336
32831
|
const base = findRepoRoot(scopeDirectory(resolved));
|
|
@@ -32340,14 +32835,14 @@ function listRules(target, options2 = {}) {
|
|
|
32340
32835
|
if (rule.source !== shown) {
|
|
32341
32836
|
shown = rule.source;
|
|
32342
32837
|
console.log(
|
|
32343
|
-
|
|
32838
|
+
chalk218.dim(base ? path74.relative(base, rule.source) : rule.source)
|
|
32344
32839
|
);
|
|
32345
32840
|
}
|
|
32346
32841
|
console.log(
|
|
32347
|
-
` ${
|
|
32842
|
+
` ${chalk218.cyan(rule.code.padEnd(width))} ${rule.title ?? rule.text}`
|
|
32348
32843
|
);
|
|
32349
32844
|
if (options2.full && rule.title)
|
|
32350
|
-
console.log(` ${" ".repeat(width)} ${
|
|
32845
|
+
console.log(` ${" ".repeat(width)} ${chalk218.dim(rule.text)}`);
|
|
32351
32846
|
}
|
|
32352
32847
|
}
|
|
32353
32848
|
|
|
@@ -32376,7 +32871,7 @@ function registerRules(program2) {
|
|
|
32376
32871
|
}
|
|
32377
32872
|
|
|
32378
32873
|
// src/commands/seq/seqAuth.ts
|
|
32379
|
-
import
|
|
32874
|
+
import chalk220 from "chalk";
|
|
32380
32875
|
|
|
32381
32876
|
// src/commands/seq/loadConnections.ts
|
|
32382
32877
|
function loadConnections2() {
|
|
@@ -32405,10 +32900,10 @@ function setDefaultConnection(name) {
|
|
|
32405
32900
|
}
|
|
32406
32901
|
|
|
32407
32902
|
// src/shared/assertUniqueName.ts
|
|
32408
|
-
import
|
|
32903
|
+
import chalk219 from "chalk";
|
|
32409
32904
|
function assertUniqueName(existingNames, name) {
|
|
32410
32905
|
if (existingNames.includes(name)) {
|
|
32411
|
-
console.error(
|
|
32906
|
+
console.error(chalk219.red(`Connection "${name}" already exists.`));
|
|
32412
32907
|
process.exit(1);
|
|
32413
32908
|
}
|
|
32414
32909
|
}
|
|
@@ -32426,16 +32921,16 @@ async function promptConnection2(existingNames) {
|
|
|
32426
32921
|
var seqAuth = createConnectionAuth({
|
|
32427
32922
|
load: loadConnections2,
|
|
32428
32923
|
save: saveConnections2,
|
|
32429
|
-
format: (c) => `${
|
|
32924
|
+
format: (c) => `${chalk220.bold(c.name)} ${c.url}`,
|
|
32430
32925
|
promptNew: promptConnection2,
|
|
32431
32926
|
onFirst: (c) => setDefaultConnection(c.name)
|
|
32432
32927
|
});
|
|
32433
32928
|
|
|
32434
32929
|
// src/commands/seq/seqQuery.ts
|
|
32435
|
-
import
|
|
32930
|
+
import chalk224 from "chalk";
|
|
32436
32931
|
|
|
32437
32932
|
// src/commands/seq/fetchSeq.ts
|
|
32438
|
-
import
|
|
32933
|
+
import chalk221 from "chalk";
|
|
32439
32934
|
async function fetchSeq(conn, path91, params) {
|
|
32440
32935
|
const url = `${conn.url}${path91}?${params}`;
|
|
32441
32936
|
const response = await fetch(url, {
|
|
@@ -32446,7 +32941,7 @@ async function fetchSeq(conn, path91, params) {
|
|
|
32446
32941
|
});
|
|
32447
32942
|
if (!response.ok) {
|
|
32448
32943
|
const body = await response.text();
|
|
32449
|
-
console.error(
|
|
32944
|
+
console.error(chalk221.red(`Seq returned ${response.status}: ${body}`));
|
|
32450
32945
|
process.exit(1);
|
|
32451
32946
|
}
|
|
32452
32947
|
return response;
|
|
@@ -32505,23 +33000,23 @@ async function fetchSeqEvents(conn, params) {
|
|
|
32505
33000
|
}
|
|
32506
33001
|
|
|
32507
33002
|
// src/commands/seq/formatEvent.ts
|
|
32508
|
-
import
|
|
33003
|
+
import chalk222 from "chalk";
|
|
32509
33004
|
function levelColor(level) {
|
|
32510
33005
|
switch (level) {
|
|
32511
33006
|
case "Fatal":
|
|
32512
|
-
return
|
|
33007
|
+
return chalk222.bgRed.white;
|
|
32513
33008
|
case "Error":
|
|
32514
|
-
return
|
|
33009
|
+
return chalk222.red;
|
|
32515
33010
|
case "Warning":
|
|
32516
|
-
return
|
|
33011
|
+
return chalk222.yellow;
|
|
32517
33012
|
case "Information":
|
|
32518
|
-
return
|
|
33013
|
+
return chalk222.cyan;
|
|
32519
33014
|
case "Debug":
|
|
32520
|
-
return
|
|
33015
|
+
return chalk222.gray;
|
|
32521
33016
|
case "Verbose":
|
|
32522
|
-
return
|
|
33017
|
+
return chalk222.dim;
|
|
32523
33018
|
default:
|
|
32524
|
-
return
|
|
33019
|
+
return chalk222.white;
|
|
32525
33020
|
}
|
|
32526
33021
|
}
|
|
32527
33022
|
function levelAbbrev(level) {
|
|
@@ -32562,12 +33057,12 @@ function formatTimestamp(iso) {
|
|
|
32562
33057
|
function formatEvent(event) {
|
|
32563
33058
|
const color = levelColor(event.Level);
|
|
32564
33059
|
const abbrev = levelAbbrev(event.Level);
|
|
32565
|
-
const ts8 =
|
|
33060
|
+
const ts8 = chalk222.dim(formatTimestamp(event.Timestamp));
|
|
32566
33061
|
const msg = renderMessage(event);
|
|
32567
33062
|
const lines2 = [`${ts8} ${color(`[${abbrev}]`)} ${msg}`];
|
|
32568
33063
|
if (event.Exception) {
|
|
32569
33064
|
for (const line of event.Exception.split("\n")) {
|
|
32570
|
-
lines2.push(
|
|
33065
|
+
lines2.push(chalk222.red(` ${line}`));
|
|
32571
33066
|
}
|
|
32572
33067
|
}
|
|
32573
33068
|
return lines2.join("\n");
|
|
@@ -32600,11 +33095,11 @@ function rejectTimestampFilter(filter) {
|
|
|
32600
33095
|
}
|
|
32601
33096
|
|
|
32602
33097
|
// src/shared/resolveNamedConnection.ts
|
|
32603
|
-
import
|
|
33098
|
+
import chalk223 from "chalk";
|
|
32604
33099
|
function resolveNamedConnection(connections, requested, defaultName, kind, authCommand) {
|
|
32605
33100
|
if (connections.length === 0) {
|
|
32606
33101
|
console.error(
|
|
32607
|
-
|
|
33102
|
+
chalk223.red(
|
|
32608
33103
|
`No ${kind} connections configured. Run '${authCommand}' first.`
|
|
32609
33104
|
)
|
|
32610
33105
|
);
|
|
@@ -32613,7 +33108,7 @@ function resolveNamedConnection(connections, requested, defaultName, kind, authC
|
|
|
32613
33108
|
const target = requested ?? defaultName ?? connections[0].name;
|
|
32614
33109
|
const connection = connections.find((c) => c.name === target);
|
|
32615
33110
|
if (!connection) {
|
|
32616
|
-
console.error(
|
|
33111
|
+
console.error(chalk223.red(`${kind} connection "${target}" not found.`));
|
|
32617
33112
|
process.exit(1);
|
|
32618
33113
|
}
|
|
32619
33114
|
return connection;
|
|
@@ -32642,7 +33137,7 @@ async function seqQuery(filter, options2) {
|
|
|
32642
33137
|
new URLSearchParams({ filter, count: String(count8) })
|
|
32643
33138
|
);
|
|
32644
33139
|
if (events.length === 0) {
|
|
32645
|
-
console.log(
|
|
33140
|
+
console.log(chalk224.yellow("No events found."));
|
|
32646
33141
|
return;
|
|
32647
33142
|
}
|
|
32648
33143
|
if (options2.json) {
|
|
@@ -32653,11 +33148,11 @@ async function seqQuery(filter, options2) {
|
|
|
32653
33148
|
for (const event of chronological) {
|
|
32654
33149
|
console.log(formatEvent(event));
|
|
32655
33150
|
}
|
|
32656
|
-
console.log(
|
|
33151
|
+
console.log(chalk224.dim(`
|
|
32657
33152
|
${events.length} events`));
|
|
32658
33153
|
if (events.length >= count8) {
|
|
32659
33154
|
console.log(
|
|
32660
|
-
|
|
33155
|
+
chalk224.yellow(
|
|
32661
33156
|
`Results limited to ${count8}. Use --count to retrieve more.`
|
|
32662
33157
|
)
|
|
32663
33158
|
);
|
|
@@ -32665,10 +33160,10 @@ ${events.length} events`));
|
|
|
32665
33160
|
}
|
|
32666
33161
|
|
|
32667
33162
|
// src/shared/setNamedDefaultConnection.ts
|
|
32668
|
-
import
|
|
33163
|
+
import chalk225 from "chalk";
|
|
32669
33164
|
function setNamedDefaultConnection(connections, name, setDefault, kind) {
|
|
32670
33165
|
if (!connections.find((c) => c.name === name)) {
|
|
32671
|
-
console.error(
|
|
33166
|
+
console.error(chalk225.red(`Connection "${name}" not found.`));
|
|
32672
33167
|
process.exit(1);
|
|
32673
33168
|
}
|
|
32674
33169
|
setDefault(name);
|
|
@@ -32817,7 +33312,7 @@ function registerSlack(program2) {
|
|
|
32817
33312
|
}
|
|
32818
33313
|
|
|
32819
33314
|
// src/commands/sql/sqlAuth.ts
|
|
32820
|
-
import
|
|
33315
|
+
import chalk227 from "chalk";
|
|
32821
33316
|
|
|
32822
33317
|
// src/commands/sql/loadConnections.ts
|
|
32823
33318
|
function loadConnections3() {
|
|
@@ -32846,7 +33341,7 @@ function setDefaultConnection2(name) {
|
|
|
32846
33341
|
}
|
|
32847
33342
|
|
|
32848
33343
|
// src/commands/sql/promptConnection.ts
|
|
32849
|
-
import
|
|
33344
|
+
import chalk226 from "chalk";
|
|
32850
33345
|
async function promptConnection3(existingNames) {
|
|
32851
33346
|
const name = await promptInput("name", "Connection name:", "default");
|
|
32852
33347
|
assertUniqueName(existingNames, name);
|
|
@@ -32854,7 +33349,7 @@ async function promptConnection3(existingNames) {
|
|
|
32854
33349
|
const portStr = await promptInput("port", "Port:", "1433");
|
|
32855
33350
|
const port = Number.parseInt(portStr, 10);
|
|
32856
33351
|
if (!Number.isFinite(port)) {
|
|
32857
|
-
console.error(
|
|
33352
|
+
console.error(chalk226.red(`Invalid port "${portStr}".`));
|
|
32858
33353
|
process.exit(1);
|
|
32859
33354
|
}
|
|
32860
33355
|
const user = await promptInput("user", "User:");
|
|
@@ -32867,13 +33362,13 @@ async function promptConnection3(existingNames) {
|
|
|
32867
33362
|
var sqlAuth = createConnectionAuth({
|
|
32868
33363
|
load: loadConnections3,
|
|
32869
33364
|
save: saveConnections3,
|
|
32870
|
-
format: (c) => `${
|
|
33365
|
+
format: (c) => `${chalk227.bold(c.name)} ${c.server}:${c.port}/${c.database} (${c.user})`,
|
|
32871
33366
|
promptNew: promptConnection3,
|
|
32872
33367
|
onFirst: (c) => setDefaultConnection2(c.name)
|
|
32873
33368
|
});
|
|
32874
33369
|
|
|
32875
33370
|
// src/commands/sql/printTable.ts
|
|
32876
|
-
import
|
|
33371
|
+
import chalk228 from "chalk";
|
|
32877
33372
|
function formatCell(value) {
|
|
32878
33373
|
if (value === null || value === void 0) return "";
|
|
32879
33374
|
if (value instanceof Date) return value.toISOString();
|
|
@@ -32882,7 +33377,7 @@ function formatCell(value) {
|
|
|
32882
33377
|
}
|
|
32883
33378
|
function printTable(rows) {
|
|
32884
33379
|
if (rows.length === 0) {
|
|
32885
|
-
console.log(
|
|
33380
|
+
console.log(chalk228.yellow("(no rows)"));
|
|
32886
33381
|
return;
|
|
32887
33382
|
}
|
|
32888
33383
|
const columns = Object.keys(rows[0]);
|
|
@@ -32890,13 +33385,13 @@ function printTable(rows) {
|
|
|
32890
33385
|
(col) => Math.max(col.length, ...rows.map((r) => formatCell(r[col]).length))
|
|
32891
33386
|
);
|
|
32892
33387
|
const header = columns.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
32893
|
-
console.log(
|
|
32894
|
-
console.log(
|
|
33388
|
+
console.log(chalk228.dim(header));
|
|
33389
|
+
console.log(chalk228.dim("-".repeat(header.length)));
|
|
32895
33390
|
for (const row of rows) {
|
|
32896
33391
|
const line = columns.map((c, i) => formatCell(row[c]).padEnd(widths[i])).join(" ");
|
|
32897
33392
|
console.log(line);
|
|
32898
33393
|
}
|
|
32899
|
-
console.log(
|
|
33394
|
+
console.log(chalk228.dim(`
|
|
32900
33395
|
${rows.length} row${rows.length === 1 ? "" : "s"}`));
|
|
32901
33396
|
}
|
|
32902
33397
|
|
|
@@ -32956,7 +33451,7 @@ async function sqlColumns(table, connectionName) {
|
|
|
32956
33451
|
}
|
|
32957
33452
|
|
|
32958
33453
|
// src/commands/sql/sqlMutate.ts
|
|
32959
|
-
import
|
|
33454
|
+
import chalk229 from "chalk";
|
|
32960
33455
|
|
|
32961
33456
|
// src/commands/sql/isMutation.ts
|
|
32962
33457
|
var MUTATION_KEYWORDS = [
|
|
@@ -32990,7 +33485,7 @@ function isMutation(sql29) {
|
|
|
32990
33485
|
async function sqlMutate(query, connectionName) {
|
|
32991
33486
|
if (!isMutation(query)) {
|
|
32992
33487
|
console.error(
|
|
32993
|
-
|
|
33488
|
+
chalk229.red(
|
|
32994
33489
|
"assist sql mutate refuses non-mutating statements. Use `assist sql query` instead."
|
|
32995
33490
|
)
|
|
32996
33491
|
);
|
|
@@ -33000,18 +33495,18 @@ async function sqlMutate(query, connectionName) {
|
|
|
33000
33495
|
const pool = await sqlConnect(conn);
|
|
33001
33496
|
try {
|
|
33002
33497
|
const result = await pool.request().query(query);
|
|
33003
|
-
console.log(
|
|
33498
|
+
console.log(chalk229.dim(`${result.rowsAffected.join(", ")} row(s) affected`));
|
|
33004
33499
|
} finally {
|
|
33005
33500
|
await pool.close();
|
|
33006
33501
|
}
|
|
33007
33502
|
}
|
|
33008
33503
|
|
|
33009
33504
|
// src/commands/sql/sqlQuery.ts
|
|
33010
|
-
import
|
|
33505
|
+
import chalk230 from "chalk";
|
|
33011
33506
|
async function sqlQuery(query, connectionName) {
|
|
33012
33507
|
if (isMutation(query)) {
|
|
33013
33508
|
console.error(
|
|
33014
|
-
|
|
33509
|
+
chalk230.red(
|
|
33015
33510
|
"assist sql query refuses mutating statements. Use `assist sql mutate` instead."
|
|
33016
33511
|
)
|
|
33017
33512
|
);
|
|
@@ -33026,7 +33521,7 @@ async function sqlQuery(query, connectionName) {
|
|
|
33026
33521
|
printTable(rows);
|
|
33027
33522
|
} else {
|
|
33028
33523
|
console.log(
|
|
33029
|
-
|
|
33524
|
+
chalk230.dim(`${result.rowsAffected.join(", ")} row(s) affected`)
|
|
33030
33525
|
);
|
|
33031
33526
|
}
|
|
33032
33527
|
} finally {
|
|
@@ -33400,7 +33895,7 @@ function syncPi(claudeDir, options2) {
|
|
|
33400
33895
|
// src/commands/sync/syncSettings.ts
|
|
33401
33896
|
import * as fs48 from "fs";
|
|
33402
33897
|
import * as path84 from "path";
|
|
33403
|
-
import
|
|
33898
|
+
import chalk231 from "chalk";
|
|
33404
33899
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
33405
33900
|
const source = path84.join(claudeDir, "settings.json");
|
|
33406
33901
|
const target = path84.join(targetBase, "settings.json");
|
|
@@ -33419,7 +33914,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
33419
33914
|
if (mergedContent !== normalizedTarget) {
|
|
33420
33915
|
if (!options2?.yes) {
|
|
33421
33916
|
console.log(
|
|
33422
|
-
|
|
33917
|
+
chalk231.yellow(
|
|
33423
33918
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
33424
33919
|
)
|
|
33425
33920
|
);
|
|
@@ -33427,7 +33922,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
33427
33922
|
printDiff(targetContent, mergedContent);
|
|
33428
33923
|
printAutoConfirmHint();
|
|
33429
33924
|
const confirm = await promptConfirm(
|
|
33430
|
-
|
|
33925
|
+
chalk231.red("Overwrite existing settings.json?"),
|
|
33431
33926
|
false
|
|
33432
33927
|
);
|
|
33433
33928
|
if (!confirm) {
|
|
@@ -34296,7 +34791,7 @@ import { mkdirSync as mkdirSync31 } from "fs";
|
|
|
34296
34791
|
import { join as join93 } from "path";
|
|
34297
34792
|
|
|
34298
34793
|
// src/commands/voice/checkLockFile.ts
|
|
34299
|
-
import { execSync as
|
|
34794
|
+
import { execSync as execSync61 } from "child_process";
|
|
34300
34795
|
import { existsSync as existsSync78, mkdirSync as mkdirSync30, readFileSync as readFileSync58, writeFileSync as writeFileSync48 } from "fs";
|
|
34301
34796
|
import { join as join92 } from "path";
|
|
34302
34797
|
function isProcessAlive2(pid) {
|
|
@@ -34325,7 +34820,7 @@ function bootstrapVenv() {
|
|
|
34325
34820
|
if (existsSync78(getVenvPython())) return;
|
|
34326
34821
|
console.log("Setting up Python environment...");
|
|
34327
34822
|
const pythonDir = getPythonDir();
|
|
34328
|
-
|
|
34823
|
+
execSync61(
|
|
34329
34824
|
`uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
|
|
34330
34825
|
{
|
|
34331
34826
|
stdio: "inherit",
|
|
@@ -34946,11 +35441,11 @@ function runCommandToCompletion(command, args, env, cwd, quiet) {
|
|
|
34946
35441
|
}
|
|
34947
35442
|
|
|
34948
35443
|
// src/commands/run/runPreCommands.ts
|
|
34949
|
-
import { execSync as
|
|
35444
|
+
import { execSync as execSync62 } from "child_process";
|
|
34950
35445
|
function runPreCommands(pre, cwd) {
|
|
34951
35446
|
for (const cmd of pre) {
|
|
34952
35447
|
try {
|
|
34953
|
-
|
|
35448
|
+
execSync62(cmd, { stdio: "inherit", cwd });
|
|
34954
35449
|
} catch (error) {
|
|
34955
35450
|
const code = error && typeof error === "object" && "status" in error ? error.status : 1;
|
|
34956
35451
|
process.exit(code);
|
|
@@ -35169,7 +35664,7 @@ function registerWatch(program2) {
|
|
|
35169
35664
|
|
|
35170
35665
|
// src/commands/roam/auth.ts
|
|
35171
35666
|
import { randomBytes } from "crypto";
|
|
35172
|
-
import
|
|
35667
|
+
import chalk232 from "chalk";
|
|
35173
35668
|
|
|
35174
35669
|
// src/commands/roam/waitForCallback.ts
|
|
35175
35670
|
import { createServer as createServer3 } from "http";
|
|
@@ -35300,13 +35795,13 @@ async function auth() {
|
|
|
35300
35795
|
saveGlobalConfig(config);
|
|
35301
35796
|
const state = randomBytes(16).toString("hex");
|
|
35302
35797
|
console.log(
|
|
35303
|
-
|
|
35798
|
+
chalk232.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
35304
35799
|
);
|
|
35305
|
-
console.log(
|
|
35306
|
-
console.log(
|
|
35307
|
-
console.log(
|
|
35800
|
+
console.log(chalk232.white("http://localhost:14523/callback\n"));
|
|
35801
|
+
console.log(chalk232.blue("Opening browser for authorization..."));
|
|
35802
|
+
console.log(chalk232.dim("Waiting for authorization callback..."));
|
|
35308
35803
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
35309
|
-
console.log(
|
|
35804
|
+
console.log(chalk232.dim("Exchanging code for tokens..."));
|
|
35310
35805
|
const tokens = await exchangeToken({
|
|
35311
35806
|
code,
|
|
35312
35807
|
clientId,
|
|
@@ -35322,7 +35817,7 @@ async function auth() {
|
|
|
35322
35817
|
};
|
|
35323
35818
|
saveGlobalConfig(config);
|
|
35324
35819
|
console.log(
|
|
35325
|
-
|
|
35820
|
+
chalk232.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
35326
35821
|
);
|
|
35327
35822
|
}
|
|
35328
35823
|
|
|
@@ -35677,11 +36172,11 @@ function registerRun(program2) {
|
|
|
35677
36172
|
}
|
|
35678
36173
|
|
|
35679
36174
|
// src/commands/screenshot/index.ts
|
|
35680
|
-
import { execSync as
|
|
36175
|
+
import { execSync as execSync63 } from "child_process";
|
|
35681
36176
|
import { existsSync as existsSync85, mkdirSync as mkdirSync34, unlinkSync as unlinkSync22, writeFileSync as writeFileSync51 } from "fs";
|
|
35682
36177
|
import { tmpdir as tmpdir9 } from "os";
|
|
35683
36178
|
import { join as join100, resolve as resolve21 } from "path";
|
|
35684
|
-
import
|
|
36179
|
+
import chalk233 from "chalk";
|
|
35685
36180
|
|
|
35686
36181
|
// src/commands/screenshot/captureWindowPs1.ts
|
|
35687
36182
|
var captureWindowPs1 = `
|
|
@@ -35820,7 +36315,7 @@ function runPowerShellScript(processName, outputPath) {
|
|
|
35820
36315
|
const scriptPath = join100(tmpdir9(), `assist-screenshot-${Date.now()}.ps1`);
|
|
35821
36316
|
writeFileSync51(scriptPath, captureWindowPs1, "utf8");
|
|
35822
36317
|
try {
|
|
35823
|
-
|
|
36318
|
+
execSync63(
|
|
35824
36319
|
`powershell -NoProfile -ExecutionPolicy Bypass -File "${scriptPath}" -ProcessName "${processName}" -OutputPath "${outputPath}"`,
|
|
35825
36320
|
{ stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" }
|
|
35826
36321
|
);
|
|
@@ -35832,13 +36327,13 @@ function screenshot(processName) {
|
|
|
35832
36327
|
const config = loadConfig();
|
|
35833
36328
|
const outputDir = resolve21(config.screenshot.outputDir);
|
|
35834
36329
|
const outputPath = buildOutputPath(outputDir, processName);
|
|
35835
|
-
console.log(
|
|
36330
|
+
console.log(chalk233.gray(`Capturing window for process "${processName}" ...`));
|
|
35836
36331
|
try {
|
|
35837
36332
|
runPowerShellScript(processName, outputPath);
|
|
35838
|
-
console.log(
|
|
36333
|
+
console.log(chalk233.green(`Screenshot saved: ${outputPath}`));
|
|
35839
36334
|
} catch (error) {
|
|
35840
36335
|
const msg = error instanceof Error ? error.message : String(error);
|
|
35841
|
-
console.error(
|
|
36336
|
+
console.error(chalk233.red(`Failed to capture screenshot: ${msg}`));
|
|
35842
36337
|
process.exit(1);
|
|
35843
36338
|
}
|
|
35844
36339
|
}
|
|
@@ -41800,7 +42295,7 @@ async function renameSession(title) {
|
|
|
41800
42295
|
|
|
41801
42296
|
// src/commands/sessions/summarise/index.ts
|
|
41802
42297
|
import * as fs57 from "fs";
|
|
41803
|
-
import
|
|
42298
|
+
import chalk234 from "chalk";
|
|
41804
42299
|
|
|
41805
42300
|
// src/commands/sessions/summarise/shared.ts
|
|
41806
42301
|
import * as fs56 from "fs";
|
|
@@ -41859,22 +42354,22 @@ ${firstMessage}`);
|
|
|
41859
42354
|
async function summarise2(options2) {
|
|
41860
42355
|
const files = await discoverSessionFiles();
|
|
41861
42356
|
if (files.length === 0) {
|
|
41862
|
-
console.log(
|
|
42357
|
+
console.log(chalk234.yellow("No sessions found."));
|
|
41863
42358
|
return;
|
|
41864
42359
|
}
|
|
41865
42360
|
const toProcess = selectCandidates(files, options2);
|
|
41866
42361
|
if (toProcess.length === 0) {
|
|
41867
|
-
console.log(
|
|
42362
|
+
console.log(chalk234.green("All sessions already summarised."));
|
|
41868
42363
|
return;
|
|
41869
42364
|
}
|
|
41870
42365
|
console.log(
|
|
41871
|
-
|
|
42366
|
+
chalk234.cyan(
|
|
41872
42367
|
`Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
|
|
41873
42368
|
)
|
|
41874
42369
|
);
|
|
41875
42370
|
const { succeeded, failed: failed2 } = processSessions(toProcess);
|
|
41876
42371
|
console.log(
|
|
41877
|
-
|
|
42372
|
+
chalk234.green(`Done: ${succeeded} summarised`) + (failed2 > 0 ? chalk234.yellow(`, ${failed2} skipped`) : "")
|
|
41878
42373
|
);
|
|
41879
42374
|
}
|
|
41880
42375
|
function selectCandidates(files, options2) {
|
|
@@ -41894,16 +42389,16 @@ function processSessions(files) {
|
|
|
41894
42389
|
let failed2 = 0;
|
|
41895
42390
|
for (let i = 0; i < files.length; i++) {
|
|
41896
42391
|
const file = files[i];
|
|
41897
|
-
process.stdout.write(
|
|
42392
|
+
process.stdout.write(chalk234.dim(` [${i + 1}/${files.length}] `));
|
|
41898
42393
|
const summary = summariseSession(file);
|
|
41899
42394
|
if (summary) {
|
|
41900
42395
|
writeSummary(file, summary);
|
|
41901
42396
|
succeeded++;
|
|
41902
|
-
process.stdout.write(`${
|
|
42397
|
+
process.stdout.write(`${chalk234.green("\u2713")} ${summary}
|
|
41903
42398
|
`);
|
|
41904
42399
|
} else {
|
|
41905
42400
|
failed2++;
|
|
41906
|
-
process.stdout.write(` ${
|
|
42401
|
+
process.stdout.write(` ${chalk234.yellow("skip")}
|
|
41907
42402
|
`);
|
|
41908
42403
|
}
|
|
41909
42404
|
}
|
|
@@ -41930,7 +42425,7 @@ function registerSessions(program2) {
|
|
|
41930
42425
|
}
|
|
41931
42426
|
|
|
41932
42427
|
// src/commands/statusLine.ts
|
|
41933
|
-
import
|
|
42428
|
+
import chalk236 from "chalk";
|
|
41934
42429
|
|
|
41935
42430
|
// src/shared/contextLevel.ts
|
|
41936
42431
|
function contextLevel(pct) {
|
|
@@ -41940,7 +42435,7 @@ function contextLevel(pct) {
|
|
|
41940
42435
|
}
|
|
41941
42436
|
|
|
41942
42437
|
// src/commands/buildLimitsSegment.ts
|
|
41943
|
-
import
|
|
42438
|
+
import chalk235 from "chalk";
|
|
41944
42439
|
|
|
41945
42440
|
// src/shared/rateLimitLevel.ts
|
|
41946
42441
|
var FIVE_HOUR_SECONDS = 5 * 3600;
|
|
@@ -41978,9 +42473,9 @@ function rateLimitLevel(pct, resetsAt, windowSeconds, now) {
|
|
|
41978
42473
|
|
|
41979
42474
|
// src/commands/buildLimitsSegment.ts
|
|
41980
42475
|
var LEVEL_COLOR = {
|
|
41981
|
-
ok:
|
|
41982
|
-
warn:
|
|
41983
|
-
over:
|
|
42476
|
+
ok: chalk235.green,
|
|
42477
|
+
warn: chalk235.yellow,
|
|
42478
|
+
over: chalk235.red
|
|
41984
42479
|
};
|
|
41985
42480
|
function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel, now) {
|
|
41986
42481
|
const level = rateLimitLevel(pct, resetsAt, windowSeconds, now);
|
|
@@ -42076,7 +42571,7 @@ async function relayUsage(claudeSessionId, transcriptPath2, usedPct) {
|
|
|
42076
42571
|
}
|
|
42077
42572
|
|
|
42078
42573
|
// src/commands/statusLine.ts
|
|
42079
|
-
|
|
42574
|
+
chalk236.level = 3;
|
|
42080
42575
|
function formatNumber(num) {
|
|
42081
42576
|
return num.toLocaleString("en-US");
|
|
42082
42577
|
}
|
|
@@ -42084,9 +42579,9 @@ function colorizePercent(pct) {
|
|
|
42084
42579
|
const label2 = `${Math.round(pct)}%`;
|
|
42085
42580
|
switch (contextLevel(pct)) {
|
|
42086
42581
|
case "red":
|
|
42087
|
-
return
|
|
42582
|
+
return chalk236.red(label2);
|
|
42088
42583
|
case "yellow":
|
|
42089
|
-
return
|
|
42584
|
+
return chalk236.yellow(label2);
|
|
42090
42585
|
default:
|
|
42091
42586
|
return label2;
|
|
42092
42587
|
}
|
|
@@ -42099,7 +42594,7 @@ async function statusLine() {
|
|
|
42099
42594
|
const usedPct = data.context_window.used_percentage ?? 0;
|
|
42100
42595
|
const dir = data.workspace?.current_dir ?? data.cwd;
|
|
42101
42596
|
const branch2 = dir ? readGitBranch(toGitCwd(dir)) : null;
|
|
42102
|
-
const branchSegment = branch2 ? `\u{1F33F}\uFE0F ${
|
|
42597
|
+
const branchSegment = branch2 ? `\u{1F33F}\uFE0F ${chalk236.cyan(branch2)} | ` : "";
|
|
42103
42598
|
console.log(
|
|
42104
42599
|
`${branchSegment}${model} | Tokens - ${formatNumber(totalIn)} \u2191 : ${formatNumber(totalOut)} \u2193 | Context - ${colorizePercent(usedPct)}${buildLimitsSegment(data.rate_limits)}`
|
|
42105
42600
|
);
|
|
@@ -42112,7 +42607,7 @@ async function statusLine() {
|
|
|
42112
42607
|
}
|
|
42113
42608
|
|
|
42114
42609
|
// src/commands/update.ts
|
|
42115
|
-
import { execSync as
|
|
42610
|
+
import { execSync as execSync64 } from "child_process";
|
|
42116
42611
|
import * as path90 from "path";
|
|
42117
42612
|
|
|
42118
42613
|
// src/commands/restartDaemonAfterUpdate.ts
|
|
@@ -42136,7 +42631,7 @@ function isGlobalNpmInstall(dir) {
|
|
|
42136
42631
|
if (resolved.split(path90.sep).includes("node_modules")) {
|
|
42137
42632
|
return true;
|
|
42138
42633
|
}
|
|
42139
|
-
const globalPrefix =
|
|
42634
|
+
const globalPrefix = execSync64("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
42140
42635
|
return resolved.toLowerCase().startsWith(path90.resolve(globalPrefix).toLowerCase());
|
|
42141
42636
|
} catch {
|
|
42142
42637
|
return false;
|
|
@@ -42147,18 +42642,18 @@ async function update2() {
|
|
|
42147
42642
|
console.log(`Assist is installed at: ${installDir}`);
|
|
42148
42643
|
if (isGitRepo(installDir)) {
|
|
42149
42644
|
console.log("Detected git repo installation, pulling latest...");
|
|
42150
|
-
|
|
42645
|
+
execSync64("git pull", { cwd: installDir, stdio: "inherit" });
|
|
42151
42646
|
console.log("Installing dependencies...");
|
|
42152
|
-
|
|
42647
|
+
execSync64("npm i", { cwd: installDir, stdio: "inherit" });
|
|
42153
42648
|
console.log("Building...");
|
|
42154
|
-
|
|
42649
|
+
execSync64("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
42155
42650
|
console.log("Syncing commands...");
|
|
42156
|
-
|
|
42651
|
+
execSync64("assist sync", { stdio: "inherit" });
|
|
42157
42652
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
42158
42653
|
console.log("Detected global npm installation, updating...");
|
|
42159
|
-
|
|
42654
|
+
execSync64("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
42160
42655
|
console.log("Syncing commands...");
|
|
42161
|
-
|
|
42656
|
+
execSync64("assist sync", { stdio: "inherit" });
|
|
42162
42657
|
} else {
|
|
42163
42658
|
console.error(
|
|
42164
42659
|
"Could not determine installation method. Expected a git repo or global npm install."
|
|
@@ -42169,10 +42664,10 @@ async function update2() {
|
|
|
42169
42664
|
}
|
|
42170
42665
|
|
|
42171
42666
|
// src/reportCliError.ts
|
|
42172
|
-
import
|
|
42667
|
+
import chalk237 from "chalk";
|
|
42173
42668
|
function reportCliError(error) {
|
|
42174
42669
|
if (error instanceof InvalidItemIdError || error instanceof AmbiguousRepoConfigError || error instanceof UnknownRepoConfigError || error instanceof MissingRunCwdError || error instanceof MiroExtractError) {
|
|
42175
|
-
console.error(
|
|
42670
|
+
console.error(chalk237.red(error.message));
|
|
42176
42671
|
} else {
|
|
42177
42672
|
console.error(error);
|
|
42178
42673
|
}
|