@staff0rd/assist 0.660.1 → 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 +387 -387
- package/dist/index.js +806 -310
- 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
|
};
|
|
@@ -23278,7 +23298,7 @@ function registerCreateIssue(issueCommand) {
|
|
|
23278
23298
|
[]
|
|
23279
23299
|
).addHelpText(
|
|
23280
23300
|
"after",
|
|
23281
|
-
"\nThere is no What/Why/How template: an issue reports a problem, and the target repo's own issue template is unknowable from here. Write the body as the repo's maintainers would expect.\nIn an assist web session the title and body are previewed for approve/reject first (with inline comments); nothing is created until it is approved.\nThe reviewer may also drop or paste screenshots or video into the pane; on approval these are appended to the issue body under a ## Screenshots section automatically, and on rejection they
|
|
23301
|
+
"\nThere is no What/Why/How template: an issue reports a problem, and the target repo's own issue template is unknowable from here. Write the body as the repo's maintainers would expect.\nIn an assist web session the title and body are previewed for approve/reject first (with inline comments); nothing is created until it is approved.\nThe reviewer may also drop or paste screenshots or video into the pane; on approval these are appended to the issue body under a ## Screenshots section automatically, and on rejection they stay attached and reappear in the preview you re-propose \u2014 so never author that section yourself.\n--type, --parent, --project, --status and --label are all resolved before the preview, so an unknown name, an unreadable parent, a missing project scope or --status without --project creates nothing.\nA bare --parent number is read against --repo, or the current repo; a parent in another repository is allowed.\nThe project scope is needed for --project: gh auth refresh -h github.com -s project"
|
|
23282
23302
|
).action(createIssue);
|
|
23283
23303
|
}
|
|
23284
23304
|
|
|
@@ -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
|
}
|
|
@@ -27734,8 +27964,9 @@ as numbered quoted-span + note pairs on stderr. Address every comment (and the
|
|
|
27734
27964
|
reason), then run the command again to re-preview the revised PR. Repeat until it
|
|
27735
27965
|
is approved. The reviewer may also drop or paste screenshots or video into the
|
|
27736
27966
|
pane; on approval these are appended to the PR body under a ## Screenshots section
|
|
27737
|
-
automatically
|
|
27738
|
-
yourself. Just compose the sections
|
|
27967
|
+
automatically; on rejection they stay attached and reappear in the preview you
|
|
27968
|
+
re-propose, so you never author that section yourself. Just compose the sections
|
|
27969
|
+
and run the command.`;
|
|
27739
27970
|
function raiseHelpText(promptJira, promptGithub, draft) {
|
|
27740
27971
|
const config = loadConfig().prs;
|
|
27741
27972
|
const jira = promptJira ?? config?.promptJira ?? false;
|
|
@@ -27781,6 +28012,13 @@ function registerPrsRaise(prsCommand) {
|
|
|
27781
28012
|
configHelp(raiseCommand, prsRaiseConfigHelp);
|
|
27782
28013
|
}
|
|
27783
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
|
+
|
|
27784
28022
|
// src/commands/readTime/readTime.ts
|
|
27785
28023
|
import { readFileSync as readFileSync46 } from "fs";
|
|
27786
28024
|
|
|
@@ -27870,7 +28108,7 @@ function resolveReadTimeTarget(target) {
|
|
|
27870
28108
|
}
|
|
27871
28109
|
|
|
27872
28110
|
// src/commands/prs/fetchPrBody.ts
|
|
27873
|
-
import { execSync as
|
|
28111
|
+
import { execSync as execSync53 } from "child_process";
|
|
27874
28112
|
function exitGhNotInstalled() {
|
|
27875
28113
|
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
27876
28114
|
console.error("Install it from https://cli.github.com/");
|
|
@@ -27891,7 +28129,7 @@ function currentRepo() {
|
|
|
27891
28129
|
function fetchPrBody(number, repo) {
|
|
27892
28130
|
const { org, repo: name } = repo ?? currentRepo();
|
|
27893
28131
|
try {
|
|
27894
|
-
const raw =
|
|
28132
|
+
const raw = execSync53(`gh pr view ${number} --json body -R ${org}/${name}`, {
|
|
27895
28133
|
encoding: "utf8",
|
|
27896
28134
|
stdio: ["pipe", "pipe", "pipe"]
|
|
27897
28135
|
});
|
|
@@ -27970,6 +28208,7 @@ function registerReadTime(parent) {
|
|
|
27970
28208
|
function registerPrs(program2) {
|
|
27971
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);
|
|
27972
28210
|
registerPrsRaise(prsCommand);
|
|
28211
|
+
registerPrsStatus(prsCommand);
|
|
27973
28212
|
registerPrsEdit(prsCommand);
|
|
27974
28213
|
registerPrsComments(prsCommand);
|
|
27975
28214
|
registerReadTime(prsCommand);
|
|
@@ -27977,10 +28216,10 @@ function registerPrs(program2) {
|
|
|
27977
28216
|
}
|
|
27978
28217
|
|
|
27979
28218
|
// src/commands/ravendb/ravendbAuth.ts
|
|
27980
|
-
import
|
|
28219
|
+
import chalk193 from "chalk";
|
|
27981
28220
|
|
|
27982
28221
|
// src/shared/createConnectionAuth.ts
|
|
27983
|
-
import
|
|
28222
|
+
import chalk188 from "chalk";
|
|
27984
28223
|
function listConnections(connections, format2) {
|
|
27985
28224
|
if (connections.length === 0) {
|
|
27986
28225
|
console.log("No connections configured.");
|
|
@@ -27993,7 +28232,7 @@ function listConnections(connections, format2) {
|
|
|
27993
28232
|
function removeConnection(connections, name, save) {
|
|
27994
28233
|
const filtered = connections.filter((c) => c.name !== name);
|
|
27995
28234
|
if (filtered.length === connections.length) {
|
|
27996
|
-
console.error(
|
|
28235
|
+
console.error(chalk188.red(`Connection "${name}" not found.`));
|
|
27997
28236
|
process.exit(1);
|
|
27998
28237
|
}
|
|
27999
28238
|
save(filtered);
|
|
@@ -28039,17 +28278,17 @@ function saveConnections(connections) {
|
|
|
28039
28278
|
}
|
|
28040
28279
|
|
|
28041
28280
|
// src/commands/ravendb/promptConnection.ts
|
|
28042
|
-
import
|
|
28281
|
+
import chalk191 from "chalk";
|
|
28043
28282
|
|
|
28044
28283
|
// src/commands/ravendb/selectOpSecret.ts
|
|
28045
|
-
import
|
|
28284
|
+
import chalk190 from "chalk";
|
|
28046
28285
|
import Enquirer2 from "enquirer";
|
|
28047
28286
|
|
|
28048
28287
|
// src/commands/ravendb/searchItems.ts
|
|
28049
|
-
import { execSync as
|
|
28050
|
-
import
|
|
28288
|
+
import { execSync as execSync54 } from "child_process";
|
|
28289
|
+
import chalk189 from "chalk";
|
|
28051
28290
|
function opExec(args) {
|
|
28052
|
-
return
|
|
28291
|
+
return execSync54(`op ${args}`, {
|
|
28053
28292
|
encoding: "utf8",
|
|
28054
28293
|
stdio: ["pipe", "pipe", "pipe"]
|
|
28055
28294
|
}).trim();
|
|
@@ -28060,7 +28299,7 @@ function searchItems(search2) {
|
|
|
28060
28299
|
items2 = JSON.parse(opExec("item list --format=json"));
|
|
28061
28300
|
} catch {
|
|
28062
28301
|
console.error(
|
|
28063
|
-
|
|
28302
|
+
chalk189.red(
|
|
28064
28303
|
"Failed to search 1Password. Ensure the CLI is installed and you are signed in."
|
|
28065
28304
|
)
|
|
28066
28305
|
);
|
|
@@ -28074,7 +28313,7 @@ function getItemFields(itemId2) {
|
|
|
28074
28313
|
const item = JSON.parse(opExec(`item get "${itemId2}" --format=json`));
|
|
28075
28314
|
return item.fields.filter((f) => f.reference && f.label);
|
|
28076
28315
|
} catch {
|
|
28077
|
-
console.error(
|
|
28316
|
+
console.error(chalk189.red("Failed to get item details from 1Password."));
|
|
28078
28317
|
process.exit(1);
|
|
28079
28318
|
}
|
|
28080
28319
|
}
|
|
@@ -28093,7 +28332,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
28093
28332
|
}).run();
|
|
28094
28333
|
const items2 = searchItems(search2);
|
|
28095
28334
|
if (items2.length === 0) {
|
|
28096
|
-
console.error(
|
|
28335
|
+
console.error(chalk190.red(`No items found matching "${search2}".`));
|
|
28097
28336
|
process.exit(1);
|
|
28098
28337
|
}
|
|
28099
28338
|
const itemId2 = await selectOne(
|
|
@@ -28102,7 +28341,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
28102
28341
|
);
|
|
28103
28342
|
const fields = getItemFields(itemId2);
|
|
28104
28343
|
if (fields.length === 0) {
|
|
28105
|
-
console.error(
|
|
28344
|
+
console.error(chalk190.red("No fields with references found on this item."));
|
|
28106
28345
|
process.exit(1);
|
|
28107
28346
|
}
|
|
28108
28347
|
const ref = await selectOne(
|
|
@@ -28116,7 +28355,7 @@ async function selectOpSecret(searchTerm) {
|
|
|
28116
28355
|
async function promptConnection(existingNames) {
|
|
28117
28356
|
const name = await promptInput("name", "Connection name:");
|
|
28118
28357
|
if (existingNames.includes(name)) {
|
|
28119
|
-
console.error(
|
|
28358
|
+
console.error(chalk191.red(`Connection "${name}" already exists.`));
|
|
28120
28359
|
process.exit(1);
|
|
28121
28360
|
}
|
|
28122
28361
|
const url = await promptInput(
|
|
@@ -28125,22 +28364,22 @@ async function promptConnection(existingNames) {
|
|
|
28125
28364
|
);
|
|
28126
28365
|
const database = await promptInput("database", "Database name:");
|
|
28127
28366
|
if (!name || !url || !database) {
|
|
28128
|
-
console.error(
|
|
28367
|
+
console.error(chalk191.red("All fields are required."));
|
|
28129
28368
|
process.exit(1);
|
|
28130
28369
|
}
|
|
28131
28370
|
const apiKeyRef = await selectOpSecret();
|
|
28132
|
-
console.log(
|
|
28371
|
+
console.log(chalk191.dim(`Using: ${apiKeyRef}`));
|
|
28133
28372
|
return { name, url, database, apiKeyRef };
|
|
28134
28373
|
}
|
|
28135
28374
|
|
|
28136
28375
|
// src/commands/ravendb/ravendbSetConnection.ts
|
|
28137
|
-
import
|
|
28376
|
+
import chalk192 from "chalk";
|
|
28138
28377
|
function ravendbSetConnection(name) {
|
|
28139
28378
|
const raw = loadGlobalConfigRaw();
|
|
28140
28379
|
const ravendb = raw.ravendb ?? {};
|
|
28141
28380
|
const connections = ravendb.connections ?? [];
|
|
28142
28381
|
if (!connections.some((c) => c.name === name)) {
|
|
28143
|
-
console.error(
|
|
28382
|
+
console.error(chalk192.red(`Connection "${name}" not found.`));
|
|
28144
28383
|
console.error(
|
|
28145
28384
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
28146
28385
|
);
|
|
@@ -28156,16 +28395,16 @@ function ravendbSetConnection(name) {
|
|
|
28156
28395
|
var ravendbAuth = createConnectionAuth({
|
|
28157
28396
|
load: loadConnections,
|
|
28158
28397
|
save: saveConnections,
|
|
28159
|
-
format: (c) => `${
|
|
28398
|
+
format: (c) => `${chalk193.bold(c.name)} ${c.url} db=${c.database} key=${c.apiKeyRef}`,
|
|
28160
28399
|
promptNew: promptConnection,
|
|
28161
28400
|
onFirst: (c) => ravendbSetConnection(c.name)
|
|
28162
28401
|
});
|
|
28163
28402
|
|
|
28164
28403
|
// src/commands/ravendb/ravendbCollections.ts
|
|
28165
|
-
import
|
|
28404
|
+
import chalk197 from "chalk";
|
|
28166
28405
|
|
|
28167
28406
|
// src/commands/ravendb/ravenFetch.ts
|
|
28168
|
-
import
|
|
28407
|
+
import chalk195 from "chalk";
|
|
28169
28408
|
|
|
28170
28409
|
// src/commands/ravendb/getAccessToken.ts
|
|
28171
28410
|
var OAUTH_URL = "https://amazon-useast-1-oauth.ravenhq.com/ApiKeys/OAuth/AccessToken";
|
|
@@ -28201,21 +28440,21 @@ ${errorText}`
|
|
|
28201
28440
|
}
|
|
28202
28441
|
|
|
28203
28442
|
// src/commands/ravendb/resolveOpSecret.ts
|
|
28204
|
-
import { execSync as
|
|
28205
|
-
import
|
|
28443
|
+
import { execSync as execSync55 } from "child_process";
|
|
28444
|
+
import chalk194 from "chalk";
|
|
28206
28445
|
function resolveOpSecret(reference) {
|
|
28207
28446
|
if (!reference.startsWith("op://")) {
|
|
28208
|
-
console.error(
|
|
28447
|
+
console.error(chalk194.red(`Invalid secret reference: must start with op://`));
|
|
28209
28448
|
process.exit(1);
|
|
28210
28449
|
}
|
|
28211
28450
|
try {
|
|
28212
|
-
return
|
|
28451
|
+
return execSync55(`op read "${reference}"`, {
|
|
28213
28452
|
encoding: "utf8",
|
|
28214
28453
|
stdio: ["pipe", "pipe", "pipe"]
|
|
28215
28454
|
}).trim();
|
|
28216
28455
|
} catch {
|
|
28217
28456
|
console.error(
|
|
28218
|
-
|
|
28457
|
+
chalk194.red(
|
|
28219
28458
|
"Failed to resolve secret reference. Ensure 1Password CLI is installed and you are signed in."
|
|
28220
28459
|
)
|
|
28221
28460
|
);
|
|
@@ -28242,7 +28481,7 @@ async function ravenFetch(connection, path91) {
|
|
|
28242
28481
|
if (!response.ok) {
|
|
28243
28482
|
const body = await response.text();
|
|
28244
28483
|
console.error(
|
|
28245
|
-
|
|
28484
|
+
chalk195.red(`RavenDB error: ${response.status} ${response.statusText}`)
|
|
28246
28485
|
);
|
|
28247
28486
|
console.error(body.substring(0, 500));
|
|
28248
28487
|
process.exit(1);
|
|
@@ -28251,7 +28490,7 @@ async function ravenFetch(connection, path91) {
|
|
|
28251
28490
|
}
|
|
28252
28491
|
|
|
28253
28492
|
// src/commands/ravendb/resolveConnection.ts
|
|
28254
|
-
import
|
|
28493
|
+
import chalk196 from "chalk";
|
|
28255
28494
|
function loadRavendb() {
|
|
28256
28495
|
const raw = loadGlobalConfigRaw();
|
|
28257
28496
|
const ravendb = raw.ravendb;
|
|
@@ -28265,7 +28504,7 @@ function resolveConnection(name) {
|
|
|
28265
28504
|
const connectionName = name ?? defaultConnection;
|
|
28266
28505
|
if (!connectionName) {
|
|
28267
28506
|
console.error(
|
|
28268
|
-
|
|
28507
|
+
chalk196.red(
|
|
28269
28508
|
"No connection specified and no default set. Use assist ravendb set-connection <name> or pass a connection name."
|
|
28270
28509
|
)
|
|
28271
28510
|
);
|
|
@@ -28273,7 +28512,7 @@ function resolveConnection(name) {
|
|
|
28273
28512
|
}
|
|
28274
28513
|
const connection = connections.find((c) => c.name === connectionName);
|
|
28275
28514
|
if (!connection) {
|
|
28276
|
-
console.error(
|
|
28515
|
+
console.error(chalk196.red(`Connection "${connectionName}" not found.`));
|
|
28277
28516
|
console.error(
|
|
28278
28517
|
`Available: ${connections.map((c) => c.name).join(", ") || "(none)"}`
|
|
28279
28518
|
);
|
|
@@ -28304,15 +28543,15 @@ async function ravendbCollections(connectionName) {
|
|
|
28304
28543
|
return;
|
|
28305
28544
|
}
|
|
28306
28545
|
for (const c of collections) {
|
|
28307
|
-
console.log(`${
|
|
28546
|
+
console.log(`${chalk197.bold(c.Name)} ${c.CountOfDocuments} docs`);
|
|
28308
28547
|
}
|
|
28309
28548
|
}
|
|
28310
28549
|
|
|
28311
28550
|
// src/commands/ravendb/ravendbQuery.ts
|
|
28312
|
-
import
|
|
28551
|
+
import chalk199 from "chalk";
|
|
28313
28552
|
|
|
28314
28553
|
// src/commands/ravendb/fetchAllPages.ts
|
|
28315
|
-
import
|
|
28554
|
+
import chalk198 from "chalk";
|
|
28316
28555
|
|
|
28317
28556
|
// src/commands/ravendb/buildQueryPath.ts
|
|
28318
28557
|
function buildQueryPath(opts) {
|
|
@@ -28350,7 +28589,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
28350
28589
|
allResults.push(...results);
|
|
28351
28590
|
start3 += results.length;
|
|
28352
28591
|
process.stderr.write(
|
|
28353
|
-
`\r${
|
|
28592
|
+
`\r${chalk198.dim(`Fetched ${allResults.length}/${totalResults}`)}`
|
|
28354
28593
|
);
|
|
28355
28594
|
if (start3 >= totalResults) break;
|
|
28356
28595
|
if (opts.limit !== void 0 && allResults.length >= opts.limit) break;
|
|
@@ -28365,7 +28604,7 @@ async function fetchAllPages(connection, opts) {
|
|
|
28365
28604
|
async function ravendbQuery(connectionName, collection, options2) {
|
|
28366
28605
|
const resolved = resolveArgs(connectionName, collection);
|
|
28367
28606
|
if (!resolved.collection && !options2.query) {
|
|
28368
|
-
console.error(
|
|
28607
|
+
console.error(chalk199.red("Provide a collection name or --query filter."));
|
|
28369
28608
|
process.exit(1);
|
|
28370
28609
|
}
|
|
28371
28610
|
const { collection: col } = resolved;
|
|
@@ -28404,7 +28643,7 @@ import { spawn as spawn6 } from "child_process";
|
|
|
28404
28643
|
import * as path46 from "path";
|
|
28405
28644
|
|
|
28406
28645
|
// src/commands/refactor/logViolations.ts
|
|
28407
|
-
import
|
|
28646
|
+
import chalk200 from "chalk";
|
|
28408
28647
|
var DEFAULT_MAX_LINES2 = 100;
|
|
28409
28648
|
function logViolations(violations, maxLines = DEFAULT_MAX_LINES2) {
|
|
28410
28649
|
if (violations.length === 0) {
|
|
@@ -28413,43 +28652,43 @@ function logViolations(violations, maxLines = DEFAULT_MAX_LINES2) {
|
|
|
28413
28652
|
}
|
|
28414
28653
|
return;
|
|
28415
28654
|
}
|
|
28416
|
-
console.error(
|
|
28655
|
+
console.error(chalk200.red(`
|
|
28417
28656
|
Refactor check failed:
|
|
28418
28657
|
`));
|
|
28419
|
-
console.error(
|
|
28658
|
+
console.error(chalk200.red(` The following files exceed ${maxLines} lines:
|
|
28420
28659
|
`));
|
|
28421
28660
|
for (const violation of violations) {
|
|
28422
|
-
console.error(
|
|
28661
|
+
console.error(chalk200.red(` ${violation.file} (${violation.lines} lines)`));
|
|
28423
28662
|
}
|
|
28424
28663
|
console.error(
|
|
28425
|
-
|
|
28664
|
+
chalk200.yellow(
|
|
28426
28665
|
`
|
|
28427
28666
|
Each file needs to be sensibly refactored, or if there is no sensible
|
|
28428
28667
|
way to refactor it, ignore it with:
|
|
28429
28668
|
`
|
|
28430
28669
|
)
|
|
28431
28670
|
);
|
|
28432
|
-
console.error(
|
|
28671
|
+
console.error(chalk200.gray(` assist refactor ignore <file>
|
|
28433
28672
|
`));
|
|
28434
28673
|
if (process.env.CLAUDECODE) {
|
|
28435
|
-
console.error(
|
|
28674
|
+
console.error(chalk200.cyan(`
|
|
28436
28675
|
## Extracting Code to New Files
|
|
28437
28676
|
`));
|
|
28438
28677
|
console.error(
|
|
28439
|
-
|
|
28678
|
+
chalk200.cyan(
|
|
28440
28679
|
` When extracting logic from one file to another, consider where the extracted code belongs:
|
|
28441
28680
|
`
|
|
28442
28681
|
)
|
|
28443
28682
|
);
|
|
28444
28683
|
console.error(
|
|
28445
|
-
|
|
28684
|
+
chalk200.cyan(
|
|
28446
28685
|
` 1. Keep related logic together: If the extracted code is tightly coupled to the
|
|
28447
28686
|
original file's domain, create a new folder containing both the original and extracted files.
|
|
28448
28687
|
`
|
|
28449
28688
|
)
|
|
28450
28689
|
);
|
|
28451
28690
|
console.error(
|
|
28452
|
-
|
|
28691
|
+
chalk200.cyan(
|
|
28453
28692
|
` 2. Share common utilities: If the extracted code can be reused across multiple
|
|
28454
28693
|
domains, move it to a common/shared folder.
|
|
28455
28694
|
`
|
|
@@ -28459,7 +28698,7 @@ Refactor check failed:
|
|
|
28459
28698
|
}
|
|
28460
28699
|
|
|
28461
28700
|
// src/commands/refactor/check/getViolations/index.ts
|
|
28462
|
-
import { execSync as
|
|
28701
|
+
import { execSync as execSync56 } from "child_process";
|
|
28463
28702
|
import fs31 from "fs";
|
|
28464
28703
|
import { minimatch as minimatch6 } from "minimatch";
|
|
28465
28704
|
|
|
@@ -28509,7 +28748,7 @@ function getGitFiles(options2) {
|
|
|
28509
28748
|
}
|
|
28510
28749
|
const files = /* @__PURE__ */ new Set();
|
|
28511
28750
|
if (options2.staged || options2.modified) {
|
|
28512
|
-
const staged =
|
|
28751
|
+
const staged = execSync56("git diff --cached --name-only", {
|
|
28513
28752
|
encoding: "utf8"
|
|
28514
28753
|
});
|
|
28515
28754
|
for (const file of staged.trim().split("\n").filter(Boolean)) {
|
|
@@ -28517,7 +28756,7 @@ function getGitFiles(options2) {
|
|
|
28517
28756
|
}
|
|
28518
28757
|
}
|
|
28519
28758
|
if (options2.unstaged || options2.modified) {
|
|
28520
|
-
const unstaged =
|
|
28759
|
+
const unstaged = execSync56("git diff --name-only", { encoding: "utf8" });
|
|
28521
28760
|
for (const file of unstaged.trim().split("\n").filter(Boolean)) {
|
|
28522
28761
|
files.add(file);
|
|
28523
28762
|
}
|
|
@@ -28605,7 +28844,7 @@ async function check(pattern2, options2) {
|
|
|
28605
28844
|
|
|
28606
28845
|
// src/commands/refactor/extract/index.ts
|
|
28607
28846
|
import path54 from "path";
|
|
28608
|
-
import
|
|
28847
|
+
import chalk203 from "chalk";
|
|
28609
28848
|
|
|
28610
28849
|
// src/commands/refactor/extract/applyExtraction.ts
|
|
28611
28850
|
import { SyntaxKind as SyntaxKind4 } from "ts-morph";
|
|
@@ -29204,23 +29443,23 @@ function buildPlan2(functionName, sourceFile, sourcePath, destPath, project) {
|
|
|
29204
29443
|
|
|
29205
29444
|
// src/commands/refactor/extract/displayPlan.ts
|
|
29206
29445
|
import path50 from "path";
|
|
29207
|
-
import
|
|
29446
|
+
import chalk201 from "chalk";
|
|
29208
29447
|
function section(title) {
|
|
29209
29448
|
return `
|
|
29210
|
-
${
|
|
29449
|
+
${chalk201.cyan(title)}`;
|
|
29211
29450
|
}
|
|
29212
29451
|
function displayImporters(plan2, cwd) {
|
|
29213
29452
|
if (plan2.importersToUpdate.length === 0) return;
|
|
29214
29453
|
console.log(section("Update importers:"));
|
|
29215
29454
|
for (const imp of plan2.importersToUpdate) {
|
|
29216
29455
|
const rel = path50.relative(cwd, imp.file.getFilePath());
|
|
29217
|
-
console.log(` ${
|
|
29456
|
+
console.log(` ${chalk201.dim(rel)}: \u2192 import from "${imp.relPath}"`);
|
|
29218
29457
|
}
|
|
29219
29458
|
}
|
|
29220
29459
|
function displayPlan(functionName, relDest, plan2, cwd) {
|
|
29221
|
-
console.log(
|
|
29460
|
+
console.log(chalk201.bold(`Extract: ${functionName} \u2192 ${relDest}
|
|
29222
29461
|
`));
|
|
29223
|
-
console.log(` ${
|
|
29462
|
+
console.log(` ${chalk201.cyan("Functions to move:")}`);
|
|
29224
29463
|
for (const name of plan2.extractedNames) {
|
|
29225
29464
|
console.log(` ${name}`);
|
|
29226
29465
|
}
|
|
@@ -29254,7 +29493,7 @@ function displayPlan(functionName, relDest, plan2, cwd) {
|
|
|
29254
29493
|
|
|
29255
29494
|
// src/commands/refactor/extract/loadProjectFile.ts
|
|
29256
29495
|
import path53 from "path";
|
|
29257
|
-
import
|
|
29496
|
+
import chalk202 from "chalk";
|
|
29258
29497
|
import { Project as Project4 } from "ts-morph";
|
|
29259
29498
|
|
|
29260
29499
|
// src/commands/refactor/extract/findTsConfig.ts
|
|
@@ -29346,7 +29585,7 @@ function loadProjectFile(file) {
|
|
|
29346
29585
|
});
|
|
29347
29586
|
const sourceFile = project.getSourceFile(sourcePath);
|
|
29348
29587
|
if (!sourceFile) {
|
|
29349
|
-
console.log(
|
|
29588
|
+
console.log(chalk202.red(`File not found in project: ${file}`));
|
|
29350
29589
|
process.exit(1);
|
|
29351
29590
|
}
|
|
29352
29591
|
return { project, sourceFile };
|
|
@@ -29369,19 +29608,19 @@ async function extract(file, functionName, destination, options2 = {}) {
|
|
|
29369
29608
|
displayPlan(functionName, relDest, plan2, cwd);
|
|
29370
29609
|
if (options2.apply) {
|
|
29371
29610
|
await applyExtraction(functionName, sourceFile, destPath, plan2, project);
|
|
29372
|
-
console.log(
|
|
29611
|
+
console.log(chalk203.green("\nExtraction complete"));
|
|
29373
29612
|
} else {
|
|
29374
|
-
console.log(
|
|
29613
|
+
console.log(chalk203.dim("\nDry run. Use --apply to execute."));
|
|
29375
29614
|
}
|
|
29376
29615
|
}
|
|
29377
29616
|
|
|
29378
29617
|
// src/commands/refactor/ignore.ts
|
|
29379
29618
|
import fs34 from "fs";
|
|
29380
|
-
import
|
|
29619
|
+
import chalk204 from "chalk";
|
|
29381
29620
|
var REFACTOR_YML_PATH2 = "refactor.yml";
|
|
29382
29621
|
function ignore2(file) {
|
|
29383
29622
|
if (!fs34.existsSync(file)) {
|
|
29384
|
-
console.error(
|
|
29623
|
+
console.error(chalk204.red(`Error: File does not exist: ${file}`));
|
|
29385
29624
|
process.exit(1);
|
|
29386
29625
|
}
|
|
29387
29626
|
const content = fs34.readFileSync(file, "utf8");
|
|
@@ -29397,7 +29636,7 @@ function ignore2(file) {
|
|
|
29397
29636
|
fs34.writeFileSync(REFACTOR_YML_PATH2, entry);
|
|
29398
29637
|
}
|
|
29399
29638
|
console.log(
|
|
29400
|
-
|
|
29639
|
+
chalk204.green(
|
|
29401
29640
|
`Added ${file} to refactor ignore list (max ${maxLines} lines)`
|
|
29402
29641
|
)
|
|
29403
29642
|
);
|
|
@@ -29406,12 +29645,12 @@ function ignore2(file) {
|
|
|
29406
29645
|
// src/commands/refactor/rename/index.ts
|
|
29407
29646
|
import fs37 from "fs";
|
|
29408
29647
|
import path59 from "path";
|
|
29409
|
-
import
|
|
29648
|
+
import chalk207 from "chalk";
|
|
29410
29649
|
|
|
29411
29650
|
// src/commands/refactor/rename/applyRename.ts
|
|
29412
29651
|
import fs36 from "fs";
|
|
29413
29652
|
import path56 from "path";
|
|
29414
|
-
import
|
|
29653
|
+
import chalk205 from "chalk";
|
|
29415
29654
|
|
|
29416
29655
|
// src/commands/refactor/restructure/computeRewrites/index.ts
|
|
29417
29656
|
import path55 from "path";
|
|
@@ -29516,13 +29755,13 @@ function applyRename(rewrites, sourcePath, destPath, cwd) {
|
|
|
29516
29755
|
const updatedContents = applyRewrites(rewrites);
|
|
29517
29756
|
for (const [file, content] of updatedContents) {
|
|
29518
29757
|
fs36.writeFileSync(file, content, "utf8");
|
|
29519
|
-
console.log(
|
|
29758
|
+
console.log(chalk205.cyan(` Updated imports in ${path56.relative(cwd, file)}`));
|
|
29520
29759
|
}
|
|
29521
29760
|
const destDir = path56.dirname(destPath);
|
|
29522
29761
|
if (!fs36.existsSync(destDir)) fs36.mkdirSync(destDir, { recursive: true });
|
|
29523
29762
|
fs36.renameSync(sourcePath, destPath);
|
|
29524
29763
|
console.log(
|
|
29525
|
-
|
|
29764
|
+
chalk205.white(
|
|
29526
29765
|
` Moved ${path56.relative(cwd, sourcePath)} \u2192 ${path56.relative(cwd, destPath)}`
|
|
29527
29766
|
)
|
|
29528
29767
|
);
|
|
@@ -29609,16 +29848,16 @@ function computeRenameRewrites(sourcePath, destPath) {
|
|
|
29609
29848
|
|
|
29610
29849
|
// src/commands/refactor/rename/printRenamePreview.ts
|
|
29611
29850
|
import path58 from "path";
|
|
29612
|
-
import
|
|
29851
|
+
import chalk206 from "chalk";
|
|
29613
29852
|
function printRenamePreview(rewrites, cwd) {
|
|
29614
29853
|
for (const rewrite of rewrites) {
|
|
29615
29854
|
console.log(
|
|
29616
|
-
|
|
29855
|
+
chalk206.dim(
|
|
29617
29856
|
` ${path58.relative(cwd, rewrite.file)}: ${rewrite.oldSpecifier} \u2192 ${rewrite.newSpecifier}`
|
|
29618
29857
|
)
|
|
29619
29858
|
);
|
|
29620
29859
|
}
|
|
29621
|
-
console.log(
|
|
29860
|
+
console.log(chalk206.dim("Dry run. Use --apply to execute."));
|
|
29622
29861
|
}
|
|
29623
29862
|
|
|
29624
29863
|
// src/commands/refactor/rename/index.ts
|
|
@@ -29629,20 +29868,20 @@ async function rename(source, destination, options2 = {}) {
|
|
|
29629
29868
|
const relSource = path59.relative(cwd, sourcePath);
|
|
29630
29869
|
const relDest = path59.relative(cwd, destPath);
|
|
29631
29870
|
if (!fs37.existsSync(sourcePath)) {
|
|
29632
|
-
console.log(
|
|
29871
|
+
console.log(chalk207.red(`File not found: ${source}`));
|
|
29633
29872
|
process.exit(1);
|
|
29634
29873
|
}
|
|
29635
29874
|
if (destPath !== sourcePath && fs37.existsSync(destPath)) {
|
|
29636
|
-
console.log(
|
|
29875
|
+
console.log(chalk207.red(`Destination already exists: ${destination}`));
|
|
29637
29876
|
process.exit(1);
|
|
29638
29877
|
}
|
|
29639
|
-
console.log(
|
|
29640
|
-
console.log(
|
|
29641
|
-
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..."));
|
|
29642
29881
|
const rewrites = computeRenameRewrites(sourcePath, destPath);
|
|
29643
29882
|
const affectedFiles = new Set(rewrites.map((r) => r.file)).size;
|
|
29644
29883
|
console.log(
|
|
29645
|
-
|
|
29884
|
+
chalk207.dim(
|
|
29646
29885
|
`${rewrites.length} import path(s) to update across ${affectedFiles} file(s)`
|
|
29647
29886
|
)
|
|
29648
29887
|
);
|
|
@@ -29651,11 +29890,11 @@ async function rename(source, destination, options2 = {}) {
|
|
|
29651
29890
|
return;
|
|
29652
29891
|
}
|
|
29653
29892
|
applyRename(rewrites, sourcePath, destPath, cwd);
|
|
29654
|
-
console.log(
|
|
29893
|
+
console.log(chalk207.green("Done"));
|
|
29655
29894
|
}
|
|
29656
29895
|
|
|
29657
29896
|
// src/commands/refactor/renameSymbol/index.ts
|
|
29658
|
-
import
|
|
29897
|
+
import chalk208 from "chalk";
|
|
29659
29898
|
|
|
29660
29899
|
// src/commands/refactor/renameSymbol/findSymbol.ts
|
|
29661
29900
|
import { SyntaxKind as SyntaxKind15 } from "ts-morph";
|
|
@@ -29701,33 +29940,33 @@ async function renameSymbol(file, oldName, newName, options2 = {}) {
|
|
|
29701
29940
|
const { project, sourceFile } = loadProjectFile(file);
|
|
29702
29941
|
const symbol = findSymbol(sourceFile, oldName);
|
|
29703
29942
|
if (!symbol) {
|
|
29704
|
-
console.log(
|
|
29943
|
+
console.log(chalk208.red(`Symbol "${oldName}" not found in ${file}`));
|
|
29705
29944
|
process.exit(1);
|
|
29706
29945
|
}
|
|
29707
29946
|
const grouped = groupReferences(symbol, cwd);
|
|
29708
29947
|
const totalRefs = [...grouped.values()].reduce((s, l) => s + l.length, 0);
|
|
29709
29948
|
console.log(
|
|
29710
|
-
|
|
29949
|
+
chalk208.bold(`Rename: ${oldName} \u2192 ${newName} (${totalRefs} references)
|
|
29711
29950
|
`)
|
|
29712
29951
|
);
|
|
29713
29952
|
for (const [refFile, lines2] of grouped) {
|
|
29714
29953
|
console.log(
|
|
29715
|
-
` ${
|
|
29954
|
+
` ${chalk208.dim(refFile)}: lines ${chalk208.cyan(lines2.join(", "))}`
|
|
29716
29955
|
);
|
|
29717
29956
|
}
|
|
29718
29957
|
if (options2.apply) {
|
|
29719
29958
|
symbol.rename(newName);
|
|
29720
29959
|
await project.save();
|
|
29721
|
-
console.log(
|
|
29960
|
+
console.log(chalk208.green(`
|
|
29722
29961
|
Renamed ${oldName} \u2192 ${newName}`));
|
|
29723
29962
|
} else {
|
|
29724
|
-
console.log(
|
|
29963
|
+
console.log(chalk208.dim("\nDry run. Use --apply to execute."));
|
|
29725
29964
|
}
|
|
29726
29965
|
}
|
|
29727
29966
|
|
|
29728
29967
|
// src/commands/refactor/restructure/index.ts
|
|
29729
29968
|
import path67 from "path";
|
|
29730
|
-
import
|
|
29969
|
+
import chalk211 from "chalk";
|
|
29731
29970
|
|
|
29732
29971
|
// src/commands/refactor/restructure/clusterDirectories.ts
|
|
29733
29972
|
import path61 from "path";
|
|
@@ -29806,50 +30045,50 @@ function clusterFiles(graph) {
|
|
|
29806
30045
|
|
|
29807
30046
|
// src/commands/refactor/restructure/displayPlan.ts
|
|
29808
30047
|
import path63 from "path";
|
|
29809
|
-
import
|
|
30048
|
+
import chalk209 from "chalk";
|
|
29810
30049
|
function relPath(filePath) {
|
|
29811
30050
|
return path63.relative(process.cwd(), filePath);
|
|
29812
30051
|
}
|
|
29813
30052
|
function displayMoves(plan2) {
|
|
29814
30053
|
if (plan2.moves.length === 0) return;
|
|
29815
|
-
console.log(
|
|
30054
|
+
console.log(chalk209.bold("\nFile moves:"));
|
|
29816
30055
|
for (const move2 of plan2.moves) {
|
|
29817
30056
|
console.log(
|
|
29818
|
-
` ${
|
|
30057
|
+
` ${chalk209.red(relPath(move2.from))} \u2192 ${chalk209.green(relPath(move2.to))}`
|
|
29819
30058
|
);
|
|
29820
|
-
console.log(
|
|
30059
|
+
console.log(chalk209.dim(` ${move2.reason}`));
|
|
29821
30060
|
}
|
|
29822
30061
|
}
|
|
29823
30062
|
function displayRewrites(rewrites) {
|
|
29824
30063
|
if (rewrites.length === 0) return;
|
|
29825
30064
|
const affectedFiles = new Set(rewrites.map((r) => r.file));
|
|
29826
|
-
console.log(
|
|
30065
|
+
console.log(chalk209.bold(`
|
|
29827
30066
|
Import rewrites (${affectedFiles.size} files):`));
|
|
29828
30067
|
for (const file of affectedFiles) {
|
|
29829
|
-
console.log(` ${
|
|
30068
|
+
console.log(` ${chalk209.cyan(relPath(file))}:`);
|
|
29830
30069
|
for (const { oldSpecifier, newSpecifier } of rewrites.filter(
|
|
29831
30070
|
(r) => r.file === file
|
|
29832
30071
|
)) {
|
|
29833
30072
|
console.log(
|
|
29834
|
-
` ${
|
|
30073
|
+
` ${chalk209.red(`"${oldSpecifier}"`)} \u2192 ${chalk209.green(`"${newSpecifier}"`)}`
|
|
29835
30074
|
);
|
|
29836
30075
|
}
|
|
29837
30076
|
}
|
|
29838
30077
|
}
|
|
29839
30078
|
function displayPlan2(plan2) {
|
|
29840
30079
|
if (plan2.warnings.length > 0) {
|
|
29841
|
-
console.log(
|
|
29842
|
-
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}`));
|
|
29843
30082
|
}
|
|
29844
30083
|
if (plan2.newDirectories.length > 0) {
|
|
29845
|
-
console.log(
|
|
30084
|
+
console.log(chalk209.bold("\nNew directories:"));
|
|
29846
30085
|
for (const dir of plan2.newDirectories)
|
|
29847
|
-
console.log(
|
|
30086
|
+
console.log(chalk209.green(` ${dir}/`));
|
|
29848
30087
|
}
|
|
29849
30088
|
displayMoves(plan2);
|
|
29850
30089
|
displayRewrites(plan2.rewrites);
|
|
29851
30090
|
console.log(
|
|
29852
|
-
|
|
30091
|
+
chalk209.dim(
|
|
29853
30092
|
`
|
|
29854
30093
|
Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports rewritten`
|
|
29855
30094
|
)
|
|
@@ -29859,18 +30098,18 @@ Summary: ${plan2.moves.length} file(s) moved, ${plan2.rewrites.length} imports r
|
|
|
29859
30098
|
// src/commands/refactor/restructure/executePlan.ts
|
|
29860
30099
|
import fs38 from "fs";
|
|
29861
30100
|
import path64 from "path";
|
|
29862
|
-
import
|
|
30101
|
+
import chalk210 from "chalk";
|
|
29863
30102
|
function executePlan(plan2) {
|
|
29864
30103
|
const updatedContents = applyRewrites(plan2.rewrites);
|
|
29865
30104
|
for (const [file, content] of updatedContents) {
|
|
29866
30105
|
fs38.writeFileSync(file, content, "utf8");
|
|
29867
30106
|
console.log(
|
|
29868
|
-
|
|
30107
|
+
chalk210.cyan(` Rewrote imports in ${path64.relative(process.cwd(), file)}`)
|
|
29869
30108
|
);
|
|
29870
30109
|
}
|
|
29871
30110
|
for (const dir of plan2.newDirectories) {
|
|
29872
30111
|
fs38.mkdirSync(dir, { recursive: true });
|
|
29873
|
-
console.log(
|
|
30112
|
+
console.log(chalk210.green(` Created ${path64.relative(process.cwd(), dir)}/`));
|
|
29874
30113
|
}
|
|
29875
30114
|
for (const move2 of plan2.moves) {
|
|
29876
30115
|
const targetDir = path64.dirname(move2.to);
|
|
@@ -29879,7 +30118,7 @@ function executePlan(plan2) {
|
|
|
29879
30118
|
}
|
|
29880
30119
|
fs38.renameSync(move2.from, move2.to);
|
|
29881
30120
|
console.log(
|
|
29882
|
-
|
|
30121
|
+
chalk210.white(
|
|
29883
30122
|
` Moved ${path64.relative(process.cwd(), move2.from)} \u2192 ${path64.relative(process.cwd(), move2.to)}`
|
|
29884
30123
|
)
|
|
29885
30124
|
);
|
|
@@ -29894,7 +30133,7 @@ function removeEmptyDirectories(dirs) {
|
|
|
29894
30133
|
if (entries.length === 0) {
|
|
29895
30134
|
fs38.rmdirSync(dir);
|
|
29896
30135
|
console.log(
|
|
29897
|
-
|
|
30136
|
+
chalk210.dim(
|
|
29898
30137
|
` Removed empty directory ${path64.relative(process.cwd(), dir)}`
|
|
29899
30138
|
)
|
|
29900
30139
|
);
|
|
@@ -30027,22 +30266,22 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
30027
30266
|
const targetPattern = pattern2 ?? "src";
|
|
30028
30267
|
const files = findSourceFiles2(targetPattern);
|
|
30029
30268
|
if (files.length === 0) {
|
|
30030
|
-
console.log(
|
|
30269
|
+
console.log(chalk211.yellow("No files found matching pattern"));
|
|
30031
30270
|
return;
|
|
30032
30271
|
}
|
|
30033
30272
|
const tsConfigPath = findTsConfig(path67.resolve(files[0]));
|
|
30034
30273
|
const plan2 = buildPlan3(files, tsConfigPath);
|
|
30035
30274
|
if (plan2.moves.length === 0) {
|
|
30036
|
-
console.log(
|
|
30275
|
+
console.log(chalk211.green("No restructuring needed"));
|
|
30037
30276
|
return;
|
|
30038
30277
|
}
|
|
30039
30278
|
displayPlan2(plan2);
|
|
30040
30279
|
if (options2.apply) {
|
|
30041
|
-
console.log(
|
|
30280
|
+
console.log(chalk211.bold("\nApplying changes..."));
|
|
30042
30281
|
executePlan(plan2);
|
|
30043
|
-
console.log(
|
|
30282
|
+
console.log(chalk211.green("\nRestructuring complete"));
|
|
30044
30283
|
} else {
|
|
30045
|
-
console.log(
|
|
30284
|
+
console.log(chalk211.dim("\nDry run. Use --apply to execute."));
|
|
30046
30285
|
}
|
|
30047
30286
|
}
|
|
30048
30287
|
|
|
@@ -30095,6 +30334,293 @@ async function checkoutOnlySession(number) {
|
|
|
30095
30334
|
await done2;
|
|
30096
30335
|
}
|
|
30097
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
|
+
|
|
30098
30624
|
// src/commands/review/annotateDiffWithLineNumbers.ts
|
|
30099
30625
|
var FILE_HEADER2 = /^\+\+\+ (?:b\/)?(.+)$/;
|
|
30100
30626
|
var HUNK_HEADER2 = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/;
|
|
@@ -30228,9 +30754,9 @@ function buildReviewPaths(repoRoot2, key) {
|
|
|
30228
30754
|
}
|
|
30229
30755
|
|
|
30230
30756
|
// src/commands/review/fetchExistingComments.ts
|
|
30231
|
-
import { execSync as
|
|
30757
|
+
import { execSync as execSync58 } from "child_process";
|
|
30232
30758
|
function fetchRawComments(org, repo, prNumber) {
|
|
30233
|
-
const out =
|
|
30759
|
+
const out = execSync58(
|
|
30234
30760
|
`gh api --paginate repos/${org}/${repo}/pulls/${prNumber}/comments`,
|
|
30235
30761
|
{ encoding: "utf8", maxBuffer: 64 * 1024 * 1024 }
|
|
30236
30762
|
);
|
|
@@ -30261,14 +30787,14 @@ function fetchExistingComments() {
|
|
|
30261
30787
|
}
|
|
30262
30788
|
|
|
30263
30789
|
// src/commands/review/gatherContext.ts
|
|
30264
|
-
import { execSync as
|
|
30790
|
+
import { execSync as execSync60 } from "child_process";
|
|
30265
30791
|
|
|
30266
30792
|
// src/commands/review/fetchPrDiff.ts
|
|
30267
|
-
import { execSync as
|
|
30793
|
+
import { execSync as execSync59 } from "child_process";
|
|
30268
30794
|
function fetchPrDiff(prNumber, baseSha, headSha) {
|
|
30269
30795
|
const { org, repo } = getRepoInfo();
|
|
30270
30796
|
try {
|
|
30271
|
-
return
|
|
30797
|
+
return execSync59(`gh pr diff ${prNumber} -R ${org}/${repo}`, {
|
|
30272
30798
|
encoding: "utf8",
|
|
30273
30799
|
maxBuffer: 256 * 1024 * 1024,
|
|
30274
30800
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -30283,68 +30809,22 @@ function isDiffTooLarge(error) {
|
|
|
30283
30809
|
}
|
|
30284
30810
|
function fetchDiffViaGit(baseSha, headSha) {
|
|
30285
30811
|
try {
|
|
30286
|
-
|
|
30812
|
+
execSync59(`git fetch origin ${baseSha} ${headSha}`, { stdio: "ignore" });
|
|
30287
30813
|
} catch {
|
|
30288
30814
|
}
|
|
30289
|
-
return
|
|
30815
|
+
return execSync59(`git diff ${baseSha}...${headSha}`, {
|
|
30290
30816
|
encoding: "utf8",
|
|
30291
30817
|
maxBuffer: 256 * 1024 * 1024
|
|
30292
30818
|
});
|
|
30293
30819
|
}
|
|
30294
30820
|
|
|
30295
|
-
// src/commands/review/fetchPrDiffInfo.ts
|
|
30296
|
-
import { execSync as execSync58 } from "child_process";
|
|
30297
|
-
function getCurrentBranch3() {
|
|
30298
|
-
return execSync58("git rev-parse --abbrev-ref HEAD", {
|
|
30299
|
-
encoding: "utf8"
|
|
30300
|
-
}).trim();
|
|
30301
|
-
}
|
|
30302
|
-
function fetchPrDiffInfo() {
|
|
30303
|
-
const { org, repo } = getRepoInfo();
|
|
30304
|
-
const branch2 = getCurrentBranch3();
|
|
30305
|
-
const fields = "number,baseRefName,baseRefOid,headRefName,headRefOid";
|
|
30306
|
-
const raw = execSync58(
|
|
30307
|
-
`gh pr list --state open --head ${branch2} --json ${fields} -R ${org}/${repo}`,
|
|
30308
|
-
{
|
|
30309
|
-
encoding: "utf8",
|
|
30310
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
30311
|
-
}
|
|
30312
|
-
);
|
|
30313
|
-
const parsed = JSON.parse(raw);
|
|
30314
|
-
const pr = parsed[0];
|
|
30315
|
-
if (!pr) {
|
|
30316
|
-
console.error(
|
|
30317
|
-
`Error: No open pull request found for branch \`${branch2}\`. Open a PR for this branch before running \`assist review\`.`
|
|
30318
|
-
);
|
|
30319
|
-
process.exit(1);
|
|
30320
|
-
}
|
|
30321
|
-
return {
|
|
30322
|
-
prNumber: pr.number,
|
|
30323
|
-
baseRef: pr.baseRefName,
|
|
30324
|
-
baseSha: pr.baseRefOid,
|
|
30325
|
-
headRef: pr.headRefName,
|
|
30326
|
-
headSha: pr.headRefOid
|
|
30327
|
-
};
|
|
30328
|
-
}
|
|
30329
|
-
function fetchPrChangedFiles(prNumber) {
|
|
30330
|
-
const { org, repo } = getRepoInfo();
|
|
30331
|
-
const out = execSync58(
|
|
30332
|
-
`gh api repos/${org}/${repo}/pulls/${prNumber}/files --paginate --jq ".[].filename"`,
|
|
30333
|
-
{
|
|
30334
|
-
encoding: "utf8",
|
|
30335
|
-
maxBuffer: 64 * 1024 * 1024
|
|
30336
|
-
}
|
|
30337
|
-
);
|
|
30338
|
-
return out.trim().split("\n").filter(Boolean);
|
|
30339
|
-
}
|
|
30340
|
-
|
|
30341
30821
|
// src/commands/review/gatherContext.ts
|
|
30342
30822
|
function gatherContext() {
|
|
30343
|
-
const branch2 =
|
|
30823
|
+
const branch2 = execSync60("git rev-parse --abbrev-ref HEAD", {
|
|
30344
30824
|
encoding: "utf8"
|
|
30345
30825
|
}).trim();
|
|
30346
|
-
const sha =
|
|
30347
|
-
const shortSha =
|
|
30826
|
+
const sha = execSync60("git rev-parse HEAD", { encoding: "utf8" }).trim();
|
|
30827
|
+
const shortSha = execSync60("git rev-parse --short=7 HEAD", {
|
|
30348
30828
|
encoding: "utf8"
|
|
30349
30829
|
}).trim();
|
|
30350
30830
|
const prInfo = fetchPrDiffInfo();
|
|
@@ -30703,18 +31183,18 @@ function partitionFindingsByDiff(findings, index3) {
|
|
|
30703
31183
|
}
|
|
30704
31184
|
|
|
30705
31185
|
// src/commands/review/warnOutOfDiff.ts
|
|
30706
|
-
import
|
|
31186
|
+
import chalk214 from "chalk";
|
|
30707
31187
|
function warnOutOfDiff(outOfDiff) {
|
|
30708
31188
|
if (outOfDiff.length === 0) return;
|
|
30709
31189
|
console.warn(
|
|
30710
|
-
|
|
31190
|
+
chalk214.yellow(
|
|
30711
31191
|
`Moved ${outOfDiff.length} finding(s) whose lines fall outside the PR diff into the review body (GitHub cannot anchor a comment on these):`
|
|
30712
31192
|
)
|
|
30713
31193
|
);
|
|
30714
31194
|
for (const finding of outOfDiff) {
|
|
30715
31195
|
const range = finding.startLine !== void 0 ? `${finding.startLine}-${finding.line}` : `${finding.line}`;
|
|
30716
31196
|
console.warn(
|
|
30717
|
-
` ${
|
|
31197
|
+
` ${chalk214.yellow("\xB7")} ${finding.title} ${chalk214.dim(
|
|
30718
31198
|
`(${finding.file}:${range})`
|
|
30719
31199
|
)}`
|
|
30720
31200
|
);
|
|
@@ -30738,18 +31218,18 @@ function selectInDiffFindings(lineBound, prDiff) {
|
|
|
30738
31218
|
}
|
|
30739
31219
|
|
|
30740
31220
|
// src/commands/review/warnUnlocated.ts
|
|
30741
|
-
import
|
|
31221
|
+
import chalk215 from "chalk";
|
|
30742
31222
|
function warnUnlocated(unlocated) {
|
|
30743
31223
|
if (unlocated.length === 0) return;
|
|
30744
31224
|
console.warn(
|
|
30745
|
-
|
|
31225
|
+
chalk215.yellow(
|
|
30746
31226
|
`Moved ${unlocated.length} finding(s) without a parseable file:line into the review body:`
|
|
30747
31227
|
)
|
|
30748
31228
|
);
|
|
30749
31229
|
for (const finding of unlocated) {
|
|
30750
|
-
const where = finding.location ||
|
|
31230
|
+
const where = finding.location || chalk215.dim("missing");
|
|
30751
31231
|
console.warn(
|
|
30752
|
-
` ${
|
|
31232
|
+
` ${chalk215.yellow("\xB7")} ${finding.title} ${chalk215.dim(`(${where})`)}`
|
|
30753
31233
|
);
|
|
30754
31234
|
}
|
|
30755
31235
|
}
|
|
@@ -31339,9 +31819,9 @@ import { writeFileSync as writeFileSync39 } from "fs";
|
|
|
31339
31819
|
// src/commands/review/finaliseReviewerSpinner.ts
|
|
31340
31820
|
var SUMMARY_MAX_LEN = 80;
|
|
31341
31821
|
function summariseStderr(stderr) {
|
|
31342
|
-
const
|
|
31343
|
-
if (!
|
|
31344
|
-
const trimmed =
|
|
31822
|
+
const firstLine3 = stderr.split(/\r?\n/).find((l) => l.trim().length > 0);
|
|
31823
|
+
if (!firstLine3) return "";
|
|
31824
|
+
const trimmed = firstLine3.trim();
|
|
31345
31825
|
return trimmed.length > SUMMARY_MAX_LEN ? `${trimmed.slice(0, SUMMARY_MAX_LEN - 1)}\u2026` : trimmed;
|
|
31346
31826
|
}
|
|
31347
31827
|
function finaliseReviewerSpinner(spinner, outcome) {
|
|
@@ -32049,14 +32529,8 @@ async function reviewPr(repoRoot2, options2) {
|
|
|
32049
32529
|
console.log(`Done. Review folder: ${paths.reviewDir}`);
|
|
32050
32530
|
}
|
|
32051
32531
|
|
|
32052
|
-
// src/commands/review/
|
|
32053
|
-
function
|
|
32054
|
-
const repoRoot2 = findRepoRoot(process.cwd());
|
|
32055
|
-
if (repoRoot2) return repoRoot2;
|
|
32056
|
-
console.error("Error: not inside a git repository.");
|
|
32057
|
-
process.exit(1);
|
|
32058
|
-
}
|
|
32059
|
-
function validateOptions(options2) {
|
|
32532
|
+
// src/commands/review/validateReviewOptions.ts
|
|
32533
|
+
function validateReviewOptions(options2) {
|
|
32060
32534
|
if (options2.apply && options2.refine) {
|
|
32061
32535
|
console.error("Error: --apply cannot be combined with --refine.");
|
|
32062
32536
|
process.exit(1);
|
|
@@ -32068,6 +32542,7 @@ function validateOptions(options2) {
|
|
|
32068
32542
|
process.exit(1);
|
|
32069
32543
|
}
|
|
32070
32544
|
validateCheckoutOnly(options2);
|
|
32545
|
+
validateHighLevel(options2);
|
|
32071
32546
|
}
|
|
32072
32547
|
function validateCheckoutOnly(options2) {
|
|
32073
32548
|
if (!options2.checkoutOnly) return;
|
|
@@ -32082,13 +32557,31 @@ function validateCheckoutOnly(options2) {
|
|
|
32082
32557
|
process.exit(1);
|
|
32083
32558
|
}
|
|
32084
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
|
+
}
|
|
32085
32577
|
async function review(options2 = {}) {
|
|
32086
|
-
|
|
32578
|
+
validateReviewOptions(options2);
|
|
32087
32579
|
startReviewLog();
|
|
32088
32580
|
const invokedIn = resolveRepoRoot();
|
|
32089
32581
|
if (options2.checkoutOnly && options2.number)
|
|
32090
32582
|
return checkoutOnlySession(options2.number);
|
|
32091
32583
|
emitActivity({ kind: "command", name: "review" });
|
|
32584
|
+
if (options2.highLevel) return runHighLevelReview(options2.number);
|
|
32092
32585
|
if (!options2.number) return reviewPr(invokedIn, options2);
|
|
32093
32586
|
await checkoutPr(options2.number);
|
|
32094
32587
|
return reviewPr(resolveRepoRoot(), options2);
|
|
@@ -32122,6 +32615,9 @@ function registerReview(program2) {
|
|
|
32122
32615
|
).option(
|
|
32123
32616
|
"--checkout-only",
|
|
32124
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"
|
|
32125
32621
|
).option(
|
|
32126
32622
|
"--address-comments",
|
|
32127
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"
|
|
@@ -32144,7 +32640,7 @@ function registerReview(program2) {
|
|
|
32144
32640
|
// src/commands/rules/addRule.ts
|
|
32145
32641
|
import { existsSync as existsSync68, readFileSync as readFileSync52, writeFileSync as writeFileSync41 } from "fs";
|
|
32146
32642
|
import path72 from "path";
|
|
32147
|
-
import
|
|
32643
|
+
import chalk216 from "chalk";
|
|
32148
32644
|
|
|
32149
32645
|
// src/commands/rules/insertRuleBullet.ts
|
|
32150
32646
|
function ruleBullet({ code, title, text: text18 }) {
|
|
@@ -32280,7 +32776,7 @@ function read2(file) {
|
|
|
32280
32776
|
function addRule(text18, options2) {
|
|
32281
32777
|
const rule = text18.trim();
|
|
32282
32778
|
if (rule === "") {
|
|
32283
|
-
console.error(
|
|
32779
|
+
console.error(chalk216.red("Rule text is required"));
|
|
32284
32780
|
process.exitCode = 1;
|
|
32285
32781
|
return;
|
|
32286
32782
|
}
|
|
@@ -32298,13 +32794,13 @@ function addRule(text18, options2) {
|
|
|
32298
32794
|
);
|
|
32299
32795
|
updateScopedRulesIndex(root);
|
|
32300
32796
|
console.log(
|
|
32301
|
-
`Added ${
|
|
32797
|
+
`Added ${chalk216.cyan(code)} to ${path72.relative(process.cwd(), target) || target}`
|
|
32302
32798
|
);
|
|
32303
32799
|
}
|
|
32304
32800
|
|
|
32305
32801
|
// src/commands/rules/indexRules.ts
|
|
32306
32802
|
import path73 from "path";
|
|
32307
|
-
import
|
|
32803
|
+
import chalk217 from "chalk";
|
|
32308
32804
|
function indexRules() {
|
|
32309
32805
|
const startDir = scopeDirectory(process.cwd());
|
|
32310
32806
|
const root = findRepoRoot(startDir) ?? startDir;
|
|
@@ -32312,24 +32808,24 @@ function indexRules() {
|
|
|
32312
32808
|
const rootFile = path73.relative(process.cwd(), path73.join(root, "CLAUDE.md"));
|
|
32313
32809
|
if (directories.length === 0) {
|
|
32314
32810
|
console.log(
|
|
32315
|
-
|
|
32811
|
+
chalk217.gray(`No directories carry their own \`## Rules\` under ${root}`)
|
|
32316
32812
|
);
|
|
32317
32813
|
return;
|
|
32318
32814
|
}
|
|
32319
32815
|
console.log(`Recorded in ${rootFile || "CLAUDE.md"}`);
|
|
32320
32816
|
for (const directory of directories)
|
|
32321
|
-
console.log(` ${
|
|
32817
|
+
console.log(` ${chalk217.cyan(directory)}`);
|
|
32322
32818
|
}
|
|
32323
32819
|
|
|
32324
32820
|
// src/commands/rules/listRules.ts
|
|
32325
32821
|
import path74 from "path";
|
|
32326
|
-
import
|
|
32822
|
+
import chalk218 from "chalk";
|
|
32327
32823
|
function listRules(target, options2 = {}) {
|
|
32328
32824
|
const resolved = path74.resolve(target ?? process.cwd());
|
|
32329
32825
|
const rules3 = readScopedRules(resolved);
|
|
32330
32826
|
if (rules3.length === 0) {
|
|
32331
32827
|
const label2 = path74.relative(process.cwd(), resolved) || ".";
|
|
32332
|
-
console.log(
|
|
32828
|
+
console.log(chalk218.gray(`No rules in scope for ${label2}`));
|
|
32333
32829
|
return;
|
|
32334
32830
|
}
|
|
32335
32831
|
const base = findRepoRoot(scopeDirectory(resolved));
|
|
@@ -32339,14 +32835,14 @@ function listRules(target, options2 = {}) {
|
|
|
32339
32835
|
if (rule.source !== shown) {
|
|
32340
32836
|
shown = rule.source;
|
|
32341
32837
|
console.log(
|
|
32342
|
-
|
|
32838
|
+
chalk218.dim(base ? path74.relative(base, rule.source) : rule.source)
|
|
32343
32839
|
);
|
|
32344
32840
|
}
|
|
32345
32841
|
console.log(
|
|
32346
|
-
` ${
|
|
32842
|
+
` ${chalk218.cyan(rule.code.padEnd(width))} ${rule.title ?? rule.text}`
|
|
32347
32843
|
);
|
|
32348
32844
|
if (options2.full && rule.title)
|
|
32349
|
-
console.log(` ${" ".repeat(width)} ${
|
|
32845
|
+
console.log(` ${" ".repeat(width)} ${chalk218.dim(rule.text)}`);
|
|
32350
32846
|
}
|
|
32351
32847
|
}
|
|
32352
32848
|
|
|
@@ -32375,7 +32871,7 @@ function registerRules(program2) {
|
|
|
32375
32871
|
}
|
|
32376
32872
|
|
|
32377
32873
|
// src/commands/seq/seqAuth.ts
|
|
32378
|
-
import
|
|
32874
|
+
import chalk220 from "chalk";
|
|
32379
32875
|
|
|
32380
32876
|
// src/commands/seq/loadConnections.ts
|
|
32381
32877
|
function loadConnections2() {
|
|
@@ -32404,10 +32900,10 @@ function setDefaultConnection(name) {
|
|
|
32404
32900
|
}
|
|
32405
32901
|
|
|
32406
32902
|
// src/shared/assertUniqueName.ts
|
|
32407
|
-
import
|
|
32903
|
+
import chalk219 from "chalk";
|
|
32408
32904
|
function assertUniqueName(existingNames, name) {
|
|
32409
32905
|
if (existingNames.includes(name)) {
|
|
32410
|
-
console.error(
|
|
32906
|
+
console.error(chalk219.red(`Connection "${name}" already exists.`));
|
|
32411
32907
|
process.exit(1);
|
|
32412
32908
|
}
|
|
32413
32909
|
}
|
|
@@ -32425,16 +32921,16 @@ async function promptConnection2(existingNames) {
|
|
|
32425
32921
|
var seqAuth = createConnectionAuth({
|
|
32426
32922
|
load: loadConnections2,
|
|
32427
32923
|
save: saveConnections2,
|
|
32428
|
-
format: (c) => `${
|
|
32924
|
+
format: (c) => `${chalk220.bold(c.name)} ${c.url}`,
|
|
32429
32925
|
promptNew: promptConnection2,
|
|
32430
32926
|
onFirst: (c) => setDefaultConnection(c.name)
|
|
32431
32927
|
});
|
|
32432
32928
|
|
|
32433
32929
|
// src/commands/seq/seqQuery.ts
|
|
32434
|
-
import
|
|
32930
|
+
import chalk224 from "chalk";
|
|
32435
32931
|
|
|
32436
32932
|
// src/commands/seq/fetchSeq.ts
|
|
32437
|
-
import
|
|
32933
|
+
import chalk221 from "chalk";
|
|
32438
32934
|
async function fetchSeq(conn, path91, params) {
|
|
32439
32935
|
const url = `${conn.url}${path91}?${params}`;
|
|
32440
32936
|
const response = await fetch(url, {
|
|
@@ -32445,7 +32941,7 @@ async function fetchSeq(conn, path91, params) {
|
|
|
32445
32941
|
});
|
|
32446
32942
|
if (!response.ok) {
|
|
32447
32943
|
const body = await response.text();
|
|
32448
|
-
console.error(
|
|
32944
|
+
console.error(chalk221.red(`Seq returned ${response.status}: ${body}`));
|
|
32449
32945
|
process.exit(1);
|
|
32450
32946
|
}
|
|
32451
32947
|
return response;
|
|
@@ -32504,23 +33000,23 @@ async function fetchSeqEvents(conn, params) {
|
|
|
32504
33000
|
}
|
|
32505
33001
|
|
|
32506
33002
|
// src/commands/seq/formatEvent.ts
|
|
32507
|
-
import
|
|
33003
|
+
import chalk222 from "chalk";
|
|
32508
33004
|
function levelColor(level) {
|
|
32509
33005
|
switch (level) {
|
|
32510
33006
|
case "Fatal":
|
|
32511
|
-
return
|
|
33007
|
+
return chalk222.bgRed.white;
|
|
32512
33008
|
case "Error":
|
|
32513
|
-
return
|
|
33009
|
+
return chalk222.red;
|
|
32514
33010
|
case "Warning":
|
|
32515
|
-
return
|
|
33011
|
+
return chalk222.yellow;
|
|
32516
33012
|
case "Information":
|
|
32517
|
-
return
|
|
33013
|
+
return chalk222.cyan;
|
|
32518
33014
|
case "Debug":
|
|
32519
|
-
return
|
|
33015
|
+
return chalk222.gray;
|
|
32520
33016
|
case "Verbose":
|
|
32521
|
-
return
|
|
33017
|
+
return chalk222.dim;
|
|
32522
33018
|
default:
|
|
32523
|
-
return
|
|
33019
|
+
return chalk222.white;
|
|
32524
33020
|
}
|
|
32525
33021
|
}
|
|
32526
33022
|
function levelAbbrev(level) {
|
|
@@ -32561,12 +33057,12 @@ function formatTimestamp(iso) {
|
|
|
32561
33057
|
function formatEvent(event) {
|
|
32562
33058
|
const color = levelColor(event.Level);
|
|
32563
33059
|
const abbrev = levelAbbrev(event.Level);
|
|
32564
|
-
const ts8 =
|
|
33060
|
+
const ts8 = chalk222.dim(formatTimestamp(event.Timestamp));
|
|
32565
33061
|
const msg = renderMessage(event);
|
|
32566
33062
|
const lines2 = [`${ts8} ${color(`[${abbrev}]`)} ${msg}`];
|
|
32567
33063
|
if (event.Exception) {
|
|
32568
33064
|
for (const line of event.Exception.split("\n")) {
|
|
32569
|
-
lines2.push(
|
|
33065
|
+
lines2.push(chalk222.red(` ${line}`));
|
|
32570
33066
|
}
|
|
32571
33067
|
}
|
|
32572
33068
|
return lines2.join("\n");
|
|
@@ -32599,11 +33095,11 @@ function rejectTimestampFilter(filter) {
|
|
|
32599
33095
|
}
|
|
32600
33096
|
|
|
32601
33097
|
// src/shared/resolveNamedConnection.ts
|
|
32602
|
-
import
|
|
33098
|
+
import chalk223 from "chalk";
|
|
32603
33099
|
function resolveNamedConnection(connections, requested, defaultName, kind, authCommand) {
|
|
32604
33100
|
if (connections.length === 0) {
|
|
32605
33101
|
console.error(
|
|
32606
|
-
|
|
33102
|
+
chalk223.red(
|
|
32607
33103
|
`No ${kind} connections configured. Run '${authCommand}' first.`
|
|
32608
33104
|
)
|
|
32609
33105
|
);
|
|
@@ -32612,7 +33108,7 @@ function resolveNamedConnection(connections, requested, defaultName, kind, authC
|
|
|
32612
33108
|
const target = requested ?? defaultName ?? connections[0].name;
|
|
32613
33109
|
const connection = connections.find((c) => c.name === target);
|
|
32614
33110
|
if (!connection) {
|
|
32615
|
-
console.error(
|
|
33111
|
+
console.error(chalk223.red(`${kind} connection "${target}" not found.`));
|
|
32616
33112
|
process.exit(1);
|
|
32617
33113
|
}
|
|
32618
33114
|
return connection;
|
|
@@ -32641,7 +33137,7 @@ async function seqQuery(filter, options2) {
|
|
|
32641
33137
|
new URLSearchParams({ filter, count: String(count8) })
|
|
32642
33138
|
);
|
|
32643
33139
|
if (events.length === 0) {
|
|
32644
|
-
console.log(
|
|
33140
|
+
console.log(chalk224.yellow("No events found."));
|
|
32645
33141
|
return;
|
|
32646
33142
|
}
|
|
32647
33143
|
if (options2.json) {
|
|
@@ -32652,11 +33148,11 @@ async function seqQuery(filter, options2) {
|
|
|
32652
33148
|
for (const event of chronological) {
|
|
32653
33149
|
console.log(formatEvent(event));
|
|
32654
33150
|
}
|
|
32655
|
-
console.log(
|
|
33151
|
+
console.log(chalk224.dim(`
|
|
32656
33152
|
${events.length} events`));
|
|
32657
33153
|
if (events.length >= count8) {
|
|
32658
33154
|
console.log(
|
|
32659
|
-
|
|
33155
|
+
chalk224.yellow(
|
|
32660
33156
|
`Results limited to ${count8}. Use --count to retrieve more.`
|
|
32661
33157
|
)
|
|
32662
33158
|
);
|
|
@@ -32664,10 +33160,10 @@ ${events.length} events`));
|
|
|
32664
33160
|
}
|
|
32665
33161
|
|
|
32666
33162
|
// src/shared/setNamedDefaultConnection.ts
|
|
32667
|
-
import
|
|
33163
|
+
import chalk225 from "chalk";
|
|
32668
33164
|
function setNamedDefaultConnection(connections, name, setDefault, kind) {
|
|
32669
33165
|
if (!connections.find((c) => c.name === name)) {
|
|
32670
|
-
console.error(
|
|
33166
|
+
console.error(chalk225.red(`Connection "${name}" not found.`));
|
|
32671
33167
|
process.exit(1);
|
|
32672
33168
|
}
|
|
32673
33169
|
setDefault(name);
|
|
@@ -32816,7 +33312,7 @@ function registerSlack(program2) {
|
|
|
32816
33312
|
}
|
|
32817
33313
|
|
|
32818
33314
|
// src/commands/sql/sqlAuth.ts
|
|
32819
|
-
import
|
|
33315
|
+
import chalk227 from "chalk";
|
|
32820
33316
|
|
|
32821
33317
|
// src/commands/sql/loadConnections.ts
|
|
32822
33318
|
function loadConnections3() {
|
|
@@ -32845,7 +33341,7 @@ function setDefaultConnection2(name) {
|
|
|
32845
33341
|
}
|
|
32846
33342
|
|
|
32847
33343
|
// src/commands/sql/promptConnection.ts
|
|
32848
|
-
import
|
|
33344
|
+
import chalk226 from "chalk";
|
|
32849
33345
|
async function promptConnection3(existingNames) {
|
|
32850
33346
|
const name = await promptInput("name", "Connection name:", "default");
|
|
32851
33347
|
assertUniqueName(existingNames, name);
|
|
@@ -32853,7 +33349,7 @@ async function promptConnection3(existingNames) {
|
|
|
32853
33349
|
const portStr = await promptInput("port", "Port:", "1433");
|
|
32854
33350
|
const port = Number.parseInt(portStr, 10);
|
|
32855
33351
|
if (!Number.isFinite(port)) {
|
|
32856
|
-
console.error(
|
|
33352
|
+
console.error(chalk226.red(`Invalid port "${portStr}".`));
|
|
32857
33353
|
process.exit(1);
|
|
32858
33354
|
}
|
|
32859
33355
|
const user = await promptInput("user", "User:");
|
|
@@ -32866,13 +33362,13 @@ async function promptConnection3(existingNames) {
|
|
|
32866
33362
|
var sqlAuth = createConnectionAuth({
|
|
32867
33363
|
load: loadConnections3,
|
|
32868
33364
|
save: saveConnections3,
|
|
32869
|
-
format: (c) => `${
|
|
33365
|
+
format: (c) => `${chalk227.bold(c.name)} ${c.server}:${c.port}/${c.database} (${c.user})`,
|
|
32870
33366
|
promptNew: promptConnection3,
|
|
32871
33367
|
onFirst: (c) => setDefaultConnection2(c.name)
|
|
32872
33368
|
});
|
|
32873
33369
|
|
|
32874
33370
|
// src/commands/sql/printTable.ts
|
|
32875
|
-
import
|
|
33371
|
+
import chalk228 from "chalk";
|
|
32876
33372
|
function formatCell(value) {
|
|
32877
33373
|
if (value === null || value === void 0) return "";
|
|
32878
33374
|
if (value instanceof Date) return value.toISOString();
|
|
@@ -32881,7 +33377,7 @@ function formatCell(value) {
|
|
|
32881
33377
|
}
|
|
32882
33378
|
function printTable(rows) {
|
|
32883
33379
|
if (rows.length === 0) {
|
|
32884
|
-
console.log(
|
|
33380
|
+
console.log(chalk228.yellow("(no rows)"));
|
|
32885
33381
|
return;
|
|
32886
33382
|
}
|
|
32887
33383
|
const columns = Object.keys(rows[0]);
|
|
@@ -32889,13 +33385,13 @@ function printTable(rows) {
|
|
|
32889
33385
|
(col) => Math.max(col.length, ...rows.map((r) => formatCell(r[col]).length))
|
|
32890
33386
|
);
|
|
32891
33387
|
const header = columns.map((c, i) => c.padEnd(widths[i])).join(" ");
|
|
32892
|
-
console.log(
|
|
32893
|
-
console.log(
|
|
33388
|
+
console.log(chalk228.dim(header));
|
|
33389
|
+
console.log(chalk228.dim("-".repeat(header.length)));
|
|
32894
33390
|
for (const row of rows) {
|
|
32895
33391
|
const line = columns.map((c, i) => formatCell(row[c]).padEnd(widths[i])).join(" ");
|
|
32896
33392
|
console.log(line);
|
|
32897
33393
|
}
|
|
32898
|
-
console.log(
|
|
33394
|
+
console.log(chalk228.dim(`
|
|
32899
33395
|
${rows.length} row${rows.length === 1 ? "" : "s"}`));
|
|
32900
33396
|
}
|
|
32901
33397
|
|
|
@@ -32955,7 +33451,7 @@ async function sqlColumns(table, connectionName) {
|
|
|
32955
33451
|
}
|
|
32956
33452
|
|
|
32957
33453
|
// src/commands/sql/sqlMutate.ts
|
|
32958
|
-
import
|
|
33454
|
+
import chalk229 from "chalk";
|
|
32959
33455
|
|
|
32960
33456
|
// src/commands/sql/isMutation.ts
|
|
32961
33457
|
var MUTATION_KEYWORDS = [
|
|
@@ -32989,7 +33485,7 @@ function isMutation(sql29) {
|
|
|
32989
33485
|
async function sqlMutate(query, connectionName) {
|
|
32990
33486
|
if (!isMutation(query)) {
|
|
32991
33487
|
console.error(
|
|
32992
|
-
|
|
33488
|
+
chalk229.red(
|
|
32993
33489
|
"assist sql mutate refuses non-mutating statements. Use `assist sql query` instead."
|
|
32994
33490
|
)
|
|
32995
33491
|
);
|
|
@@ -32999,18 +33495,18 @@ async function sqlMutate(query, connectionName) {
|
|
|
32999
33495
|
const pool = await sqlConnect(conn);
|
|
33000
33496
|
try {
|
|
33001
33497
|
const result = await pool.request().query(query);
|
|
33002
|
-
console.log(
|
|
33498
|
+
console.log(chalk229.dim(`${result.rowsAffected.join(", ")} row(s) affected`));
|
|
33003
33499
|
} finally {
|
|
33004
33500
|
await pool.close();
|
|
33005
33501
|
}
|
|
33006
33502
|
}
|
|
33007
33503
|
|
|
33008
33504
|
// src/commands/sql/sqlQuery.ts
|
|
33009
|
-
import
|
|
33505
|
+
import chalk230 from "chalk";
|
|
33010
33506
|
async function sqlQuery(query, connectionName) {
|
|
33011
33507
|
if (isMutation(query)) {
|
|
33012
33508
|
console.error(
|
|
33013
|
-
|
|
33509
|
+
chalk230.red(
|
|
33014
33510
|
"assist sql query refuses mutating statements. Use `assist sql mutate` instead."
|
|
33015
33511
|
)
|
|
33016
33512
|
);
|
|
@@ -33025,7 +33521,7 @@ async function sqlQuery(query, connectionName) {
|
|
|
33025
33521
|
printTable(rows);
|
|
33026
33522
|
} else {
|
|
33027
33523
|
console.log(
|
|
33028
|
-
|
|
33524
|
+
chalk230.dim(`${result.rowsAffected.join(", ")} row(s) affected`)
|
|
33029
33525
|
);
|
|
33030
33526
|
}
|
|
33031
33527
|
} finally {
|
|
@@ -33399,7 +33895,7 @@ function syncPi(claudeDir, options2) {
|
|
|
33399
33895
|
// src/commands/sync/syncSettings.ts
|
|
33400
33896
|
import * as fs48 from "fs";
|
|
33401
33897
|
import * as path84 from "path";
|
|
33402
|
-
import
|
|
33898
|
+
import chalk231 from "chalk";
|
|
33403
33899
|
async function syncSettings(claudeDir, targetBase, options2) {
|
|
33404
33900
|
const source = path84.join(claudeDir, "settings.json");
|
|
33405
33901
|
const target = path84.join(targetBase, "settings.json");
|
|
@@ -33418,7 +33914,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
33418
33914
|
if (mergedContent !== normalizedTarget) {
|
|
33419
33915
|
if (!options2?.yes) {
|
|
33420
33916
|
console.log(
|
|
33421
|
-
|
|
33917
|
+
chalk231.yellow(
|
|
33422
33918
|
"\n\u26A0\uFE0F Warning: settings.json differs from existing file"
|
|
33423
33919
|
)
|
|
33424
33920
|
);
|
|
@@ -33426,7 +33922,7 @@ async function syncSettings(claudeDir, targetBase, options2) {
|
|
|
33426
33922
|
printDiff(targetContent, mergedContent);
|
|
33427
33923
|
printAutoConfirmHint();
|
|
33428
33924
|
const confirm = await promptConfirm(
|
|
33429
|
-
|
|
33925
|
+
chalk231.red("Overwrite existing settings.json?"),
|
|
33430
33926
|
false
|
|
33431
33927
|
);
|
|
33432
33928
|
if (!confirm) {
|
|
@@ -34295,7 +34791,7 @@ import { mkdirSync as mkdirSync31 } from "fs";
|
|
|
34295
34791
|
import { join as join93 } from "path";
|
|
34296
34792
|
|
|
34297
34793
|
// src/commands/voice/checkLockFile.ts
|
|
34298
|
-
import { execSync as
|
|
34794
|
+
import { execSync as execSync61 } from "child_process";
|
|
34299
34795
|
import { existsSync as existsSync78, mkdirSync as mkdirSync30, readFileSync as readFileSync58, writeFileSync as writeFileSync48 } from "fs";
|
|
34300
34796
|
import { join as join92 } from "path";
|
|
34301
34797
|
function isProcessAlive2(pid) {
|
|
@@ -34324,7 +34820,7 @@ function bootstrapVenv() {
|
|
|
34324
34820
|
if (existsSync78(getVenvPython())) return;
|
|
34325
34821
|
console.log("Setting up Python environment...");
|
|
34326
34822
|
const pythonDir = getPythonDir();
|
|
34327
|
-
|
|
34823
|
+
execSync61(
|
|
34328
34824
|
`uv sync --project "${pythonDir}" --extra runtime --no-install-project`,
|
|
34329
34825
|
{
|
|
34330
34826
|
stdio: "inherit",
|
|
@@ -34945,11 +35441,11 @@ function runCommandToCompletion(command, args, env, cwd, quiet) {
|
|
|
34945
35441
|
}
|
|
34946
35442
|
|
|
34947
35443
|
// src/commands/run/runPreCommands.ts
|
|
34948
|
-
import { execSync as
|
|
35444
|
+
import { execSync as execSync62 } from "child_process";
|
|
34949
35445
|
function runPreCommands(pre, cwd) {
|
|
34950
35446
|
for (const cmd of pre) {
|
|
34951
35447
|
try {
|
|
34952
|
-
|
|
35448
|
+
execSync62(cmd, { stdio: "inherit", cwd });
|
|
34953
35449
|
} catch (error) {
|
|
34954
35450
|
const code = error && typeof error === "object" && "status" in error ? error.status : 1;
|
|
34955
35451
|
process.exit(code);
|
|
@@ -35168,7 +35664,7 @@ function registerWatch(program2) {
|
|
|
35168
35664
|
|
|
35169
35665
|
// src/commands/roam/auth.ts
|
|
35170
35666
|
import { randomBytes } from "crypto";
|
|
35171
|
-
import
|
|
35667
|
+
import chalk232 from "chalk";
|
|
35172
35668
|
|
|
35173
35669
|
// src/commands/roam/waitForCallback.ts
|
|
35174
35670
|
import { createServer as createServer3 } from "http";
|
|
@@ -35299,13 +35795,13 @@ async function auth() {
|
|
|
35299
35795
|
saveGlobalConfig(config);
|
|
35300
35796
|
const state = randomBytes(16).toString("hex");
|
|
35301
35797
|
console.log(
|
|
35302
|
-
|
|
35798
|
+
chalk232.yellow("\nEnsure this Redirect URI is set in your Roam OAuth app:")
|
|
35303
35799
|
);
|
|
35304
|
-
console.log(
|
|
35305
|
-
console.log(
|
|
35306
|
-
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..."));
|
|
35307
35803
|
const { code, redirectUri } = await authorizeInBrowser(clientId, state);
|
|
35308
|
-
console.log(
|
|
35804
|
+
console.log(chalk232.dim("Exchanging code for tokens..."));
|
|
35309
35805
|
const tokens = await exchangeToken({
|
|
35310
35806
|
code,
|
|
35311
35807
|
clientId,
|
|
@@ -35321,7 +35817,7 @@ async function auth() {
|
|
|
35321
35817
|
};
|
|
35322
35818
|
saveGlobalConfig(config);
|
|
35323
35819
|
console.log(
|
|
35324
|
-
|
|
35820
|
+
chalk232.green("Roam credentials and tokens saved to ~/.assist.yml")
|
|
35325
35821
|
);
|
|
35326
35822
|
}
|
|
35327
35823
|
|
|
@@ -35676,11 +36172,11 @@ function registerRun(program2) {
|
|
|
35676
36172
|
}
|
|
35677
36173
|
|
|
35678
36174
|
// src/commands/screenshot/index.ts
|
|
35679
|
-
import { execSync as
|
|
36175
|
+
import { execSync as execSync63 } from "child_process";
|
|
35680
36176
|
import { existsSync as existsSync85, mkdirSync as mkdirSync34, unlinkSync as unlinkSync22, writeFileSync as writeFileSync51 } from "fs";
|
|
35681
36177
|
import { tmpdir as tmpdir9 } from "os";
|
|
35682
36178
|
import { join as join100, resolve as resolve21 } from "path";
|
|
35683
|
-
import
|
|
36179
|
+
import chalk233 from "chalk";
|
|
35684
36180
|
|
|
35685
36181
|
// src/commands/screenshot/captureWindowPs1.ts
|
|
35686
36182
|
var captureWindowPs1 = `
|
|
@@ -35819,7 +36315,7 @@ function runPowerShellScript(processName, outputPath) {
|
|
|
35819
36315
|
const scriptPath = join100(tmpdir9(), `assist-screenshot-${Date.now()}.ps1`);
|
|
35820
36316
|
writeFileSync51(scriptPath, captureWindowPs1, "utf8");
|
|
35821
36317
|
try {
|
|
35822
|
-
|
|
36318
|
+
execSync63(
|
|
35823
36319
|
`powershell -NoProfile -ExecutionPolicy Bypass -File "${scriptPath}" -ProcessName "${processName}" -OutputPath "${outputPath}"`,
|
|
35824
36320
|
{ stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" }
|
|
35825
36321
|
);
|
|
@@ -35831,13 +36327,13 @@ function screenshot(processName) {
|
|
|
35831
36327
|
const config = loadConfig();
|
|
35832
36328
|
const outputDir = resolve21(config.screenshot.outputDir);
|
|
35833
36329
|
const outputPath = buildOutputPath(outputDir, processName);
|
|
35834
|
-
console.log(
|
|
36330
|
+
console.log(chalk233.gray(`Capturing window for process "${processName}" ...`));
|
|
35835
36331
|
try {
|
|
35836
36332
|
runPowerShellScript(processName, outputPath);
|
|
35837
|
-
console.log(
|
|
36333
|
+
console.log(chalk233.green(`Screenshot saved: ${outputPath}`));
|
|
35838
36334
|
} catch (error) {
|
|
35839
36335
|
const msg = error instanceof Error ? error.message : String(error);
|
|
35840
|
-
console.error(
|
|
36336
|
+
console.error(chalk233.red(`Failed to capture screenshot: ${msg}`));
|
|
35841
36337
|
process.exit(1);
|
|
35842
36338
|
}
|
|
35843
36339
|
}
|
|
@@ -41799,7 +42295,7 @@ async function renameSession(title) {
|
|
|
41799
42295
|
|
|
41800
42296
|
// src/commands/sessions/summarise/index.ts
|
|
41801
42297
|
import * as fs57 from "fs";
|
|
41802
|
-
import
|
|
42298
|
+
import chalk234 from "chalk";
|
|
41803
42299
|
|
|
41804
42300
|
// src/commands/sessions/summarise/shared.ts
|
|
41805
42301
|
import * as fs56 from "fs";
|
|
@@ -41858,22 +42354,22 @@ ${firstMessage}`);
|
|
|
41858
42354
|
async function summarise2(options2) {
|
|
41859
42355
|
const files = await discoverSessionFiles();
|
|
41860
42356
|
if (files.length === 0) {
|
|
41861
|
-
console.log(
|
|
42357
|
+
console.log(chalk234.yellow("No sessions found."));
|
|
41862
42358
|
return;
|
|
41863
42359
|
}
|
|
41864
42360
|
const toProcess = selectCandidates(files, options2);
|
|
41865
42361
|
if (toProcess.length === 0) {
|
|
41866
|
-
console.log(
|
|
42362
|
+
console.log(chalk234.green("All sessions already summarised."));
|
|
41867
42363
|
return;
|
|
41868
42364
|
}
|
|
41869
42365
|
console.log(
|
|
41870
|
-
|
|
42366
|
+
chalk234.cyan(
|
|
41871
42367
|
`Summarising ${toProcess.length} session(s) (${files.length} total)\u2026`
|
|
41872
42368
|
)
|
|
41873
42369
|
);
|
|
41874
42370
|
const { succeeded, failed: failed2 } = processSessions(toProcess);
|
|
41875
42371
|
console.log(
|
|
41876
|
-
|
|
42372
|
+
chalk234.green(`Done: ${succeeded} summarised`) + (failed2 > 0 ? chalk234.yellow(`, ${failed2} skipped`) : "")
|
|
41877
42373
|
);
|
|
41878
42374
|
}
|
|
41879
42375
|
function selectCandidates(files, options2) {
|
|
@@ -41893,16 +42389,16 @@ function processSessions(files) {
|
|
|
41893
42389
|
let failed2 = 0;
|
|
41894
42390
|
for (let i = 0; i < files.length; i++) {
|
|
41895
42391
|
const file = files[i];
|
|
41896
|
-
process.stdout.write(
|
|
42392
|
+
process.stdout.write(chalk234.dim(` [${i + 1}/${files.length}] `));
|
|
41897
42393
|
const summary = summariseSession(file);
|
|
41898
42394
|
if (summary) {
|
|
41899
42395
|
writeSummary(file, summary);
|
|
41900
42396
|
succeeded++;
|
|
41901
|
-
process.stdout.write(`${
|
|
42397
|
+
process.stdout.write(`${chalk234.green("\u2713")} ${summary}
|
|
41902
42398
|
`);
|
|
41903
42399
|
} else {
|
|
41904
42400
|
failed2++;
|
|
41905
|
-
process.stdout.write(` ${
|
|
42401
|
+
process.stdout.write(` ${chalk234.yellow("skip")}
|
|
41906
42402
|
`);
|
|
41907
42403
|
}
|
|
41908
42404
|
}
|
|
@@ -41929,7 +42425,7 @@ function registerSessions(program2) {
|
|
|
41929
42425
|
}
|
|
41930
42426
|
|
|
41931
42427
|
// src/commands/statusLine.ts
|
|
41932
|
-
import
|
|
42428
|
+
import chalk236 from "chalk";
|
|
41933
42429
|
|
|
41934
42430
|
// src/shared/contextLevel.ts
|
|
41935
42431
|
function contextLevel(pct) {
|
|
@@ -41939,7 +42435,7 @@ function contextLevel(pct) {
|
|
|
41939
42435
|
}
|
|
41940
42436
|
|
|
41941
42437
|
// src/commands/buildLimitsSegment.ts
|
|
41942
|
-
import
|
|
42438
|
+
import chalk235 from "chalk";
|
|
41943
42439
|
|
|
41944
42440
|
// src/shared/rateLimitLevel.ts
|
|
41945
42441
|
var FIVE_HOUR_SECONDS = 5 * 3600;
|
|
@@ -41977,9 +42473,9 @@ function rateLimitLevel(pct, resetsAt, windowSeconds, now) {
|
|
|
41977
42473
|
|
|
41978
42474
|
// src/commands/buildLimitsSegment.ts
|
|
41979
42475
|
var LEVEL_COLOR = {
|
|
41980
|
-
ok:
|
|
41981
|
-
warn:
|
|
41982
|
-
over:
|
|
42476
|
+
ok: chalk235.green,
|
|
42477
|
+
warn: chalk235.yellow,
|
|
42478
|
+
over: chalk235.red
|
|
41983
42479
|
};
|
|
41984
42480
|
function formatLimit(pct, resetsAt, windowSeconds, fallbackLabel, now) {
|
|
41985
42481
|
const level = rateLimitLevel(pct, resetsAt, windowSeconds, now);
|
|
@@ -42075,7 +42571,7 @@ async function relayUsage(claudeSessionId, transcriptPath2, usedPct) {
|
|
|
42075
42571
|
}
|
|
42076
42572
|
|
|
42077
42573
|
// src/commands/statusLine.ts
|
|
42078
|
-
|
|
42574
|
+
chalk236.level = 3;
|
|
42079
42575
|
function formatNumber(num) {
|
|
42080
42576
|
return num.toLocaleString("en-US");
|
|
42081
42577
|
}
|
|
@@ -42083,9 +42579,9 @@ function colorizePercent(pct) {
|
|
|
42083
42579
|
const label2 = `${Math.round(pct)}%`;
|
|
42084
42580
|
switch (contextLevel(pct)) {
|
|
42085
42581
|
case "red":
|
|
42086
|
-
return
|
|
42582
|
+
return chalk236.red(label2);
|
|
42087
42583
|
case "yellow":
|
|
42088
|
-
return
|
|
42584
|
+
return chalk236.yellow(label2);
|
|
42089
42585
|
default:
|
|
42090
42586
|
return label2;
|
|
42091
42587
|
}
|
|
@@ -42098,7 +42594,7 @@ async function statusLine() {
|
|
|
42098
42594
|
const usedPct = data.context_window.used_percentage ?? 0;
|
|
42099
42595
|
const dir = data.workspace?.current_dir ?? data.cwd;
|
|
42100
42596
|
const branch2 = dir ? readGitBranch(toGitCwd(dir)) : null;
|
|
42101
|
-
const branchSegment = branch2 ? `\u{1F33F}\uFE0F ${
|
|
42597
|
+
const branchSegment = branch2 ? `\u{1F33F}\uFE0F ${chalk236.cyan(branch2)} | ` : "";
|
|
42102
42598
|
console.log(
|
|
42103
42599
|
`${branchSegment}${model} | Tokens - ${formatNumber(totalIn)} \u2191 : ${formatNumber(totalOut)} \u2193 | Context - ${colorizePercent(usedPct)}${buildLimitsSegment(data.rate_limits)}`
|
|
42104
42600
|
);
|
|
@@ -42111,7 +42607,7 @@ async function statusLine() {
|
|
|
42111
42607
|
}
|
|
42112
42608
|
|
|
42113
42609
|
// src/commands/update.ts
|
|
42114
|
-
import { execSync as
|
|
42610
|
+
import { execSync as execSync64 } from "child_process";
|
|
42115
42611
|
import * as path90 from "path";
|
|
42116
42612
|
|
|
42117
42613
|
// src/commands/restartDaemonAfterUpdate.ts
|
|
@@ -42135,7 +42631,7 @@ function isGlobalNpmInstall(dir) {
|
|
|
42135
42631
|
if (resolved.split(path90.sep).includes("node_modules")) {
|
|
42136
42632
|
return true;
|
|
42137
42633
|
}
|
|
42138
|
-
const globalPrefix =
|
|
42634
|
+
const globalPrefix = execSync64("npm prefix -g", { stdio: "pipe" }).toString().trim();
|
|
42139
42635
|
return resolved.toLowerCase().startsWith(path90.resolve(globalPrefix).toLowerCase());
|
|
42140
42636
|
} catch {
|
|
42141
42637
|
return false;
|
|
@@ -42146,18 +42642,18 @@ async function update2() {
|
|
|
42146
42642
|
console.log(`Assist is installed at: ${installDir}`);
|
|
42147
42643
|
if (isGitRepo(installDir)) {
|
|
42148
42644
|
console.log("Detected git repo installation, pulling latest...");
|
|
42149
|
-
|
|
42645
|
+
execSync64("git pull", { cwd: installDir, stdio: "inherit" });
|
|
42150
42646
|
console.log("Installing dependencies...");
|
|
42151
|
-
|
|
42647
|
+
execSync64("npm i", { cwd: installDir, stdio: "inherit" });
|
|
42152
42648
|
console.log("Building...");
|
|
42153
|
-
|
|
42649
|
+
execSync64("npm run build", { cwd: installDir, stdio: "inherit" });
|
|
42154
42650
|
console.log("Syncing commands...");
|
|
42155
|
-
|
|
42651
|
+
execSync64("assist sync", { stdio: "inherit" });
|
|
42156
42652
|
} else if (isGlobalNpmInstall(installDir)) {
|
|
42157
42653
|
console.log("Detected global npm installation, updating...");
|
|
42158
|
-
|
|
42654
|
+
execSync64("npm i -g @staff0rd/assist@latest", { stdio: "inherit" });
|
|
42159
42655
|
console.log("Syncing commands...");
|
|
42160
|
-
|
|
42656
|
+
execSync64("assist sync", { stdio: "inherit" });
|
|
42161
42657
|
} else {
|
|
42162
42658
|
console.error(
|
|
42163
42659
|
"Could not determine installation method. Expected a git repo or global npm install."
|
|
@@ -42168,10 +42664,10 @@ async function update2() {
|
|
|
42168
42664
|
}
|
|
42169
42665
|
|
|
42170
42666
|
// src/reportCliError.ts
|
|
42171
|
-
import
|
|
42667
|
+
import chalk237 from "chalk";
|
|
42172
42668
|
function reportCliError(error) {
|
|
42173
42669
|
if (error instanceof InvalidItemIdError || error instanceof AmbiguousRepoConfigError || error instanceof UnknownRepoConfigError || error instanceof MissingRunCwdError || error instanceof MiroExtractError) {
|
|
42174
|
-
console.error(
|
|
42670
|
+
console.error(chalk237.red(error.message));
|
|
42175
42671
|
} else {
|
|
42176
42672
|
console.error(error);
|
|
42177
42673
|
}
|